github地址:https://github.com/madethatold/eyepetizer
(含apk)
本项目仅供学习借鉴使用,请勿用做商业用途,项目若有侵权,请联系作者删除
一.效果图
二.项目中第三方依赖
- okhttp
OkHttp is an HTTP client that’s efficient by default - gson
谷歌提供的开源库-(将一段JSON格式的字符串自动映射成一个对象) - Tablayout控件
顶部导航栏 结合viewPager使用 - Glide
Glide is a fast and efficient image loading library for Android focused on smooth scrolling - litepal
allows developers to use SQLite database extremely easy
一款开源的Android数据库开发框架,采用了对象关系映射(ORM)的模式 - SmartRefreshLayout
下拉刷新框架 - Banner
第三方开源库–内部基于ViewPager2实现的轮播控件 - circleimageview
A fast circular ImageView perfect for profile images.
将imageview圆形化的开源库 - viewBinding
在大多数情况下,视图绑定会替代 findViewById(需要Android Studio版本3.6及以上;Gradle 插件版本3.6.0及以上)
三.初次仿写遇到的坑
1.gson解析数据空指针异常
实体类中变量名与json数据字段不符,无法通过解析将其实例化
避坑指南:
如果可以的话,将实体类中的变量名与json对应的字段对应上
或者选择添加注解@SerializedName("xxx")
2.OkHttpClient单例化
项目中将OkHttpClient单例化封装在了一个工具类中
OkHttpClients should be shared
OkHttp performs best when you create a single OkHttpClient instance and reuse it for all of your HTTP calls. This is because each client holds its own connection pool and thread pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request wastes resources on idle pools.
查阅博客得到创建OkHttpClient的三种方式:
new OkHttpClient()
默认创建方式new OkHttpClient.Builder()
允许自定义配置okHttpclient.newBuilder()
通过已经存在的client对象,创建特殊需要的client对象
项目中封装工具类仅实现了将client单例化
其实可以封装进一些数据的post
get
等基础方法
3.gson解析数据后空指针异常
项目中需要网络请求后得到List,传入对应的RecyclerView的Adapter
另开启了一个线程进行网络请求,在该线程中将List初始化,准备传入adapter;
但在运行过程中通过日志发现,List.size=0即List并没有被传入数据
避坑指南:
在结束数据解析,初始化List后,切换线程到主线程,此时List已经初始化结束,再传入adapter
线程切换方式很多:
runOnUiThread
Thread
+Handler
实现
4.Glide与CircleImageView不兼容的问题
解决方案:
Glide图片加载框架本事可以实现加载圆形图片
Glide.with(context)
.load(xxx)
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
.into(xxx);
5.实现瀑布流效果RecyclerView时item跳动问题
- 对于item跳动的问题:
manager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
binding.rvNominate2.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
manager.invalidateSpanAssignments();
}
});
上述代码优化了item滑动过程中跳动闪烁的问题
- 对于item重新滑动回来图片大小改变的问题
需要在onBindViewHolder
方法中预先得到图片的长与宽,将图片长宽预先设置好,防止复用时改变图片大小。(感谢后台同学准备了图片长宽)
By the way,后台传回来的图片长宽数据是以pixels为单位,但在加载图片设置长宽时,需要转换单位为dp
转换方法:
public int px2dip(Context context, float pxValue) {
float m = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / m + 0.5f);
}
6.实现Refreshlayout的onLoad()方法
实现思想:
数据从外部调用adapter的公共构造方法传进去,adapter对外提供add,refresh操作。那么,只需要维护adapter中的list是固定的(notifyDataSetChanged()
该方法要求adapter唯一不变),在外部需要加载更多时临时创建List初始化数据,然后adapter.add()
在本项目中,要注意的是,setAdapter()
方法一定要有,这时传入的数据可以为空,因为RecyclerView必须要有适配器,但要想有显示,需要add方法添加list
注意数据改变后notifyDataSetChanged()
和notifyItemInserted
7.对SmartRefreshlayout自定义header和footer
自定义一个类作为header
public class MyHeader extends LinearLayout implements RefreshHeader
重写里面的方法:重点是onStateChanged
onMoving
getSpinnerStyle()
getView()
和构造方法
在xml布局文件直接引用自定义的header## 8.视频播放页面使用栈顶复用启动模式
按照我的理解:栈顶复用即将启动过的activity push入栈,最近启动过的activity刚好于栈顶,当再次启动该activity时,不在启动新的activity,直接将栈顶的拿来用
当点击播放页面中相关视频时,不再开启新的activity,直接将目前的activity更换数据即可
9.设置控件透明度之后,导致其他控件透明度改变
getBackground().setAlpha
实现透明的改变后,在5.0以上系统时,就会导致其他共用一个资源的布局(例如:@color/white)透明度都跟着被改变。
在布局中多个控件同时使用一个资源的时候,这些控件会共用一个状态,例如ColorState,如果你改变了一个控件的状态,其他的控件都会接收到相同的通知。这时我们可以使用mutate()方法使该控件状态不定,这样不定状态的控件就不会共享自己的状态了
(感谢这篇博客的分享:https://blog.csdn.net/myatlantis/article/details/49336587)
10.Activity之间传递对象
Activity之间传递数据有3种常见方式:
- 简单的数据传递
putExtra()
- 通过bundle数据包传递
- 传递对象
传递对象前,首先要将对象对应的类序列化implements Serializable
,然后传递intent.putExtra("bean", bean);
注意序列化时,不仅该类要实现序列化接口,其内部类也都需要实现,否则报错 - 传递List
intent.putExtra("Beans", (Serializable) Beans);
11.使用WebView时避免内存泄漏和OOM
不在xml中定义 Webview ,而是在需要的时候在Activity中创建
并且Context使用 getApplicationContext()
六.项目所使用的Api
项目所使用的Api皆来源于网络,侵删
//首页-日报
public static final String DAILY="http://baobab.kaiyanapp.com/api/v5/index/tab/feed";
//首页-推荐
public static final String NOMINATE="http://baobab.kaiyanapp.com/api/v5/index/tab/allRec";
//首页-发现
public static final String DISCOVER="http://baobab.kaiyanapp.com/api/v7/index/tab/discovery";
//视频详情页-相关推荐
public static final String RELATED="http://baobab.kaiyanapp.com/api/v4/video/related?id=";
//视频详情页-评论
public static final String COMMENT="http://baobab.kaiyanapp.com/api/v2/replies/video?videoId=";
//社区页-推荐
public static final String NOMINATE2="http://baobab.kaiyanapp.com/api/v7/community/tab/rec";
//社区页-关注
public static final String FOLLOW="http://baobab.kaiyanapp.com/api/v6/community/tab/follow";
五.总结
项目中整体使用最多的是recyclerview,在MainFragment,CommunityFragment的界面,都是recyclerview的实现,流程大概是 :
频繁的自定义ViewHolder-> 写一个个不同的item的布局文件 ->绑定到对应的ViewHolder-> 完善adapter中的3个重载方法(其中包括判断List中的数据类型应当对应哪个layout和holder以及在onBindViewHolder
中绑定数据到控件) ->recyclerview添加该适配器。
目前项目基本完成的状态,有一些地方还没能完善或完成
例如对于某些手机屏幕适配不太好,导致画面很奇怪,textview被莫名其妙放大或缩小;在个人模块,还没有实现缓存,观看记录等功能;某些地方(按钮,文字,图标)点不了;点赞评论等功能缺失;在社区的推荐模块,查看图片,图片加载很慢…等等还有很多问题,项目还会一直修改。
构思如何巧妙地实现远比码字难。