RecyclerView 的核心知识
说道 RecyclerView 就不得不想起他的 前辈listView,RecyclerView 的 出现解决了ListView的一些问题。下面就说一下二者在缓存上面的区别。
ListView 的缓存
它分为两层缓存
1 Active View (在屏幕内部,可视部分的缓存)
2 Scrap View (滑出屏幕部分,不可见部分的缓存)
RecycleBean 会先去 1 里面找,没有的话就会去2里面找,还没有的话,就会在getView的 ContentView中返回null,此时就需要开发者自己去创建 View了 并且去findviewbyid()了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-78CHkKXq-1599029966398)(https://i.imgur.com/0M17an0.png)]
下图更清楚的说明了 两种的缓存作用的位置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KceEmjaO-1599029966400)(https://i.imgur.com/UawH6F1.png)]
RecyclerView 的缓存
图中已经做了标注解释
ps:这里的 Scrap 相当于 ListView 的可视部分缓存
在 1,2中 都是通过position 去找缓存了,都是干净的数据可以拿来直接用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pWdlatSt-1599029966402)(https://i.imgur.com/27bYwGC.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pNdEyBa5-1599029966403)(https://i.imgur.com/px6BWtw.png)]
列表中 item / 广告的 impression 统计
-
ListView 可以通过 getView 准确的知道 广告浏览次数。(因为ListView的item 从不可见到可见,肯定是调用getView)
-
RecyclerView 通过 onBindViewHolder()统计会有问题!(自身的缓存机制,会不走 onBindViewHolder()方法)。
-
要通过 onViewAttachedToWindow()方法统计 (每次进入屏幕都会调用这个方法)。
你可能不知道的 RecyclerView 性能优化策略
1 设置点击事件优化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7iRYTYkM-1599029966404)(https://i.imgur.com/OEYrJ8Z.png)]
我们通常会将 item中 view的点击事件设置在 onBindViewHodler()方法中,带式这样做会导致我么每次刷新的时候都会 new View.onClicklestener,浪费了性能。所以写在onCreateViewHolder()中比较好
2 LinearLayoutManager.setlnitialPrefetchltemCount() “横向滑动时候的预加载个数” 详情看下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r901AacQ-1599029966404)(https://i.imgur.com/7okr48y.png)]
3 RecyclerView.setHasFixedSize();
设置为true的时,在内容发生改变时,会走layoutChildren()局部更新,requestLayout() 是全部更新—能省点是点,
4 RecycledViewPool 共用。
上面提到 RecycledViewPool 会根据viewType缓存,那么如果我的几个RV 同时可能都用到一些相同的 viewType布局 ,那么就可以共用一个RecyclerViewPool,下面是代码例子(非实际情况)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gkcfEPe1-1599029966404)(https://i.imgur.com/bzslJkH.png)]
具体场景一个TabLayout 有多个标题 每个标题对应一个 Rv
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mUsqRPEU-1599029966404)(https://i.imgur.com/xmvnNpH.png)]
DiffUtil (稍难) 计算一个集合更新到另一个集合的最小变化。
使用它的原因
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKoc8QLO-1599029966405)(https://i.imgur.com/roMYsJu.png)]
先看一下 DiffUtil.Callback抽象类,他有5个方法
ps:它是给系统用的Callback,我需要在这5个方法中做计算,然后系统会根据这5个方法做 “动态规划”(算法的一种)。
- getOldListSize () 旧集合
- getNewListSize () 新集合
- areItemsTheSame() 判断是不是一个对象(并不是对比内存地址,而是对比关键参数,比如 User的 身份证号。。。)
- areContentSTheSame() 在上一个方法返回 true时 去对比User内部的成员 是否一致。
- getChangePayload 在上一个方法返回false是,说明有变化,那么就把变化的部分存起来。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XKZCitAK-1599029966406)(https://i.imgur.com/iPmpXLJ.png)]
具体代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pLZg5UPn-1599029966406)(https://i.imgur.com/asGwTlc.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ih258qge-1599029966406)(https://i.imgur.com/XpL8gDY.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dM1UJxwp-1599029966407)(https://i.imgur.com/fUidKM3.png)]
图中的this,传adapter的引用就好了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLKEcRrz-1599029966407)(https://i.imgur.com/8juJ60U.png)]
当我们使用diff的时候就要重载 onBindViewHodler 三个参数的方法 用到里面的payloads参数
这样就完成了增量更新
用三种方法讲diff的计算去异步进行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ER92BaVN-1599029966408)(https://i.imgur.com/ICWiAV0.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7RbBERPg-1599029966408)(https://i.imgur.com/z1cRPmY.png)]
ItemDecoration 做分隔线太麻烦?
- 1 想要将 Divider 画在item外边,需要offset 做偏移,就调用onDraw() 方法;
- 2 想要将 Divider 画在item里边 ,就调用 onDrawOver()方法;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GO1RtZq-1599029966408)(https://i.imgur.com/W53LHAp.png)]
ItemDecoration 还能干什么?
- 1 分隔线
- 2 高亮
- 3 分组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7O3MvD8R-1599029966409)(https://i.imgur.com/8QIIWhH.png)]
高亮
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FDF2Td14-1599029966409)(https://i.imgur.com/cPvLS66.png)]
ItemDecoration 是可以做效果叠加的 扩展性做的很好 所以可以做各种各样的效果 装饰!~
//因为这里是 add 而不是 set
DividerItemDecoration decoration = new DividerItemDecoration(getApplicationContext(), VERTICAL);
recyclerView.addItemDecoration(decoration);
###Recycler的更多知识请关注
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vzlWADOB-1599029966410)(https://i.imgur.com/eziAxRC.png)]