真正的嵌套表格RecyclerView,最大性能优化,内有源码
该篇文章只讲嵌套RecyclerView,有关其余知识可以去搜索其他RecyclerView相关文章。
为什么单独拿RecyclerView嵌套表格来说,是因为现在网上的有关文章要么并没讲的透彻,要么大部分完全误导(功能实现了,性能相当差),至于国外文章就更少了,因为嵌套表格一直是一种避免的设计。
那么说到这是一种避免的设计,但是像朋友圈,QQ空间,新浪微博,他们是怎么实现的呢?
方案1: 采用RecyclerView共享View pool。
方案2: 封装一个九宫格的Layout,可以根据图片数量layout相应样式。 注意内部的View需要自定义View pool。(目前微信采用此方案)
方案3: 自定义ImageView,在绘制的时候按照九宫格绘制图片,添加九宫格各个区域的事件相应以及绘制相应按下效果。(微博采用此方案)。
RecyclerView是随着14年10月份5.0发布出来的,按照国内的5.0普及大概是16年,也就是说16年前大部分Android用的是ListView,而ListView是没有RecyclerView的View pool的功能,所以国内各大公司不同的解决方案是可以理解的。
那么现在该篇划重点内容:
RecycledViewPool
关于RecycledViewPool,官方文档是这样说的:
Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views. This can be useful if you have multiple RecyclerViews with adapters that use the same view types, for example if you have several data sets with the same kinds of item views displayed by a ViewPager. RecyclerView automatically creates a pool for itself if you don’t provide one.
意思就是RecyclerView可以设置一个ViewHolder的对象池,这个池称为RecycledViewPool,这个对象池可以节省你创建ViewHolder的开销,更能避免GC。即便你不给它设置,它也会自己创建一个。同时,支持RecylerView间共用一个RecycledViewPool
。
缓存策略
在进一步解说RecycledViewPool前,先说说Recyclerview的缓存策略。
Recyclerview有4级缓存
缓存类 | 是否需要回调createView | 是否需要回调bindView | 生命周期 | 备注 |
---|---|---|---|---|
mChangedScrap和mAttachedScrap | 否 | 否 | onLayout函数周期内 | 用于屏幕内ItemView快速重用。mChangedScrap 表示数据已经改变的viewHolder列表。mAttachedScrap表示未与RecyclerView分离的ViewHolder列表 |
mCachedViews | 否 | 否 | 与mAdapter一致,当mAdapter被更换时,mCachedViews即被缓存至mRecycledPool
|
默认缓存2个,即缓存屏幕外2个itemView |
mViewCacheExtension | 这是开发者可以控制的ViewHolder缓存的帮助类,默认不实现 | |||
mRecyclerPool |
否 | 是 | 与自身生命周期一致,不再被引用时即被释放 | 默认上限为5个,代码可以设置所有都公用同一个RecycledViewPool
|
接下来我从网上找来2个图片讲解这个4级缓存机制


使用RecycledViewPool
仔细看了上文流程,会发现我们只需要让互相嵌套的RecyclerView的item都进入同一个共享池即可
外层的RecyclerView初始化时,加入RecycledViewPool
/**
* 初始化view
*/
private void initView() {
// 创建 ViewHolder的缓存共享池
RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool();
RecyclerView recyclerView = findViewById(R.id.rlParent);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// 需要注意:要使用RecycledViewPool的话,如果使用的LayoutManager是LinearLayoutManager或其子类(如GridLayoutManager),需要手动开启这个特性
layoutManager.setRecycleChildrenOnDetach(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setRecycledViewPool(recycledViewPool);
// 传递RecycledViewPool共享池进父适配器,让父适配器里面的子适配器也共用同一个共享池
ParentInfoAdapter adapter = new ParentInfoAdapter(this, dataInfoList, recycledViewPool);
recyclerView.setAdapter(adapter);
}
外层的RecyclerView初始化内层的RecyclerView的时候,也加入RecycledViewPool
/**
* ViewHolder
*/
static class ViewHolder extends RecyclerView.ViewHolder {
TextView mTitle;//标题
RecyclerView mRecyclerView; //子RecyclerView
ViewHolder(View itemView, RecyclerView.RecycledViewPool recycledViewPool) {
super(itemView);
mTitle = itemView.findViewById(R.id.tvParentTitle);
mRecyclerView = itemView.findViewById(R.id.rlChild);
LinearLayoutManager manager = new LinearLayoutManager(itemView.getContext());
// 需要注意:要使用RecycledViewPool的话,如果使用的LayoutManager是LinearLayoutManager或其子类(如GridLayoutManager),需要手动开启这个特性
manager.setRecycleChildrenOnDetach(true);
// 嵌套的子RecyclerView,需要将LinearLayoutManager设置setAutoMeasureEnabled(true)成自适应高度
manager.setAutoMeasureEnabled(true);
// 子RecyclerView没必要滚动本身
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setLayoutManager(manager);
// 子RecyclerView现在和父RecyclerView在同一个共享池了
mRecyclerView.setRecycledViewPool(recycledViewPool);
}
}
// 其实RecycledViewPool的内部维护了一个Map,里面以不同的viewType为Key存储了各自对应的ViewHolder集合,所以这边设置了常量防止父适配器和子适配器的ViewType冲突
public static final int PARENT_VIEW_TYPE = 0;
public static final int CHILD_VIEW_TYPE = 1;
核心代码在上面了,具体效果请下载我写的一个demo,有非常详细的注释
https://github.com/zhongjhATC/NestingRecyclerView