关于RecycleView,之前我写过一篇比较基础的文章,主要说的是缓存和优化等问题。但是有读者反映问题不够实际和深入。于是,我又去淘了一些关于RecycleView的面试真题,大家一起看看吧,这次的问题如果都弄懂了,下次面试再遇到RecycleView应该就没啥可担心的了。

  • 讲一下RecyclerView的缓存机制,滑动10个,再滑回去,会有几个执行onBindView。缓存的是什么?cachedView会执行onBindView吗?
  • RecyclerView预取机制
  • 如何实现RecyclerView的局部更新,用过payload吗,notifyItemChange方法中的参数?
  • RecyclerView嵌套RecyclerView滑动冲突,NestScrollView嵌套RecyclerView。

讲一下RecyclerView的缓存机制,滑动10个,再滑回去,会有几个执行onBindView。缓存的是什么?cachedView会执行onBindView吗?

RecyclerView预取机制

这两个问题都是关于缓存的,我就一起说了。

1)首先说下RecycleView的缓存结构:

Recycleview有四级缓存,分别是mAttachedScrap(屏幕内),mCacheViews(屏幕外),mViewCacheExtension(自定义缓存),mRecyclerPool(缓存池)

  • mAttachedScrap(屏幕内),用于屏幕内itemview快速重用,不需要重新createView和bindView
  • mCacheViews(屏幕外),保存最近移出屏幕的ViewHolder,包含数据和position信息,复用时必须是相同位置的ViewHolder才能复用,应用场景在那些需要来回滑动的列表中,当往回滑动时,能直接复用ViewHolder数据,不需要重新bindView。
  • mViewCacheExtension(自定义缓存),不直接使用,需要用户自定义实现,默认不实现。
  • mRecyclerPool(缓存池),当cacheView满了后或者adapter被更换,将cacheView中移出的ViewHolder放到Pool中,放之前会把ViewHolder数据清除掉,所以复用时需要重新bindView。

2)四级缓存按照顺序需要依次读取。所以完整缓存流程是:

  1. 保存缓存流程:
  • 插入或是删除itemView时,先把屏幕内的ViewHolder保存至AttachedScrap
  • 滑动屏幕的时候,先消失的itemview会保存到CacheView,CacheView大小默认是2,超过数量的话按照先入先出原则,移出头部的itemview保存到RecyclerPool缓存池(如果有自定义缓存就会保存到自定义缓存里),RecyclerPool缓存池会按照itemview的itemtype进行保存,每个itemType缓存个数为5个,超过就会被回收。
  1. 获取缓存流程:
  • AttachedScrap中获取,通过pos匹配holder——>获取失败,从CacheView中获取,也是通过pos获取holder缓存

    ——>获取失败,从自定义缓存中获取缓存——>获取失败,从mRecyclerPool中获取

    ——>获取失败,重新创建viewholder——createViewHolder并bindview。

3)了解了缓存结构和缓存流程,我们再来看看具体的问题

滑动10个,再滑回去,会有几个执行onBindView?

  • 由之前的缓存结构可知,需要重新执行onBindView的只有一种缓存区,就是缓存池mRecyclerPool

所以我们假设从加载RecyclView开始盘的话(页面假设可以容纳7条数据):

  • 首先,7条数据会依次调用onCreateViewHolderonBindViewHolder
  • 往下滑一条(position=7),那么会把position=0的数据放到mCacheViews中。此时mCacheViews缓存区数量为1,mRecyclerPool数量为0。然后新出现的position=7的数据通过postion在mCacheViews中找不到对应的ViewHolder,通过itemtype也在mRecyclerPool中找不到对应的数据,所以会调用onCreateViewHolderonBindViewHolder方法。
  • 再往下滑一条数据(position=8),如上。
  • 再往下滑一条数据(position=9),position=2的数据会放到mCacheViews中,但是由于mCacheViews缓存区默认容量为2,所以position=0的数据会被清空数据然后放到mRecyclerPool缓存池中。而新出现的position=9数据由于在mRecyclerPool中还是找不到相应type的ViewHolder,所以还是会走onCreateViewHolderonBindViewHolder方法。所以此时mCacheViews缓存区数量为2,mRecyclerPool数量为1。
  • 再往下滑一条数据(position=10),这时候由于可以在mRecyclerPool中找到相同viewtype的ViewHolder了。所以就直接复用了,并调用onBindViewHolder方法绑定数据。
  • 后面依次类推,刚消失的两条数据会被放到mCacheViews中,再出现的时候是不会调用onBindViewHolder方法,而复用的第三条数据是从mRecyclerPool中取得,就会调用onBindViewHolder方法了。

4)所以这个问题就得出结论了(假设mCacheViews容量为默认值2):

  • 如果一开始滑动的是新数据,那么滑动10个,就会走10个bindview方法。然后滑回去,会走10-2个bindview方法。一共18次调用。

  • 如果一开始滑动的是老数据,那么滑动10-2个,就会走8个bindview方法。然后滑回去,会走10-2个bindview方法。一共16次调用。

但是但是,实际情况又有点不一样。因为Recycleview在v25版本引入了一个新的机制,预取机制

预取机制,就是在滑动过程中,会把将要展示的一个元素提前缓存到mCachedViews中,所以滑动10个元素的时候,第11个元素也会被创建,也就多走了一次bindview方法。但是滑回去的时候不影响,因为就算提前取了一个缓存数据,只是把bindview方法提前了,并不影响总的绑定item数量。

所以滑动的是新数据的情况下就会多一次调用bindview方法。

5)总结,问题怎么答呢?

  • 四级缓存和流程说一下。
  • 滑动10个,再滑回去,bindview可以是19次调用,可以是16次调用。
  • 缓存的其实就是缓存item的view,在Recycleview中就是viewholder
  • cachedView就是mCacheViews缓存区中的view,是不需要重新绑定数据的。

如何实现RecyclerView的局部更新,用过payload吗,notifyItemChange方法中的参数?

关于RecycleView的数据更新,主要有以下几个方法:

  • notifyDataSetChanged(),刷新全部可见的item。

    *notifyItemChanged(int),刷新指定item。
  • notifyItemRangeChanged(int,int),从指定位置开始刷新指定个item。
  • notifyItemInserted(int)、notifyItemMoved(int)、notifyItemRemoved(int)。插入、移动一个并自动刷新。
  • notifyItemChanged(int, Object),局部刷新。

可以看到,关于view的局部刷新就是notifyItemChanged(int, Object)方法,下面具体说说:

notifyItemChange有两个构造方法:

  • notifyItemChanged(int position, @Nullable Object payload)
  • notifyItemChanged(int position)

其中payload参数可以认为是你要刷新的一个标示,比如我有时候只想刷新itemView中的textview,有时候只想刷新imageview?又或者我只想某一个view的文字颜色进行高亮设置?那么我就可以通过payload参数来标示这个特殊的需求了。

具体怎么做呢?比如我调用了notifyItemChanged(14,"changeColor"),那么在onBindViewHolder回调方法中做下判断即可:

    @Override
public void onBindViewHolder(ViewHolderholder, int position, List<Object> payloads) {
if (payloads.isEmpty()) {
// payloads为空,说明是更新整个ViewHolder
onBindViewHolder(holder, position);
} else {
// payloads不为空,这只更新需要更新的View即可。
String payload = payloads.get(0).toString();
if ("changeColor".equals(payload)) {
holder.textView.setTextColor("");
}
}
}

RecyclerView嵌套RecyclerView滑动冲突,NestScrollView嵌套RecyclerView。

1)RecyclerView嵌套RecyclerView的情况下,如果两者都要上下滑动,那么就会引起滑动冲突。默认情况下外层的RecycleView可滑,内层不可滑。

之前说过解决滑动冲突的办法有两种:内部拦截法和外部拦截法

这里我提供一种内部拦截法,还有一些其他的办法大家可以自己思考下。

   holder.recyclerView.setOnTouchListener { v, event ->
when(event.action){
//当按下操作的时候,就通知父view不要拦截,拿起操作就设置可以拦截,正常走父view的滑动。
MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE -> v.parent.requestDisallowInterceptTouchEvent(true)
MotionEvent.ACTION_UP -> v.parent.requestDisallowInterceptTouchEvent(false)
}
false}

2)关于ScrclerView的滑动冲突还是同样的解决办法,就是进行事件拦截。

还有一个办法就是用Nestedscrollview代替ScrollViewNestedscrollview是官方为了解决滑动冲突问题而设计的新的View。它的定义就是支持嵌套滑动的ScrollView。

所以直接替换成Nestedscrollview就能保证两者都能正常滑动了。但是要注意设置RecyclerView.setNestedScrollingEnabled(false)这个方法,用来取消RecyclerView本身的滑动效果。

这是因为RecyclerView默认是setNestedScrollingEnabled(true),这个方法的含义是支持嵌套滚动的。也就是说当它嵌套在NestedScrollView中时,默认会随着NestedScrollView滚动而滚动,放弃了自己的滚动。所以给我们的感觉就是滞留、卡顿。所以我们将它设置为false就解决了卡顿问题,让他正常的滑动,不受外部影响。

拜拜

今天聊了不少,关于RecycleView重要的知识点应该都涉及到了,其中bindview的问题下次有机会我会再详细的说一下,配合图片日志。

最后希望大家好好巩固知识,加油。

拜拜

有一起学习的小伙伴可以关注下️我的公众号——码上积木,每天剖析一个知识点,我们一起积累知识。

再也不用担心问RecycleView了——面试真题详解的更多相关文章

  1. 软考之信息安全工程师(包含2016-2018历年真题详解+官方指定教程+VIP视频教程)

    软考-中级信息安全工程师2016-2018历年考试真题以及详细答案,同时含有信息安全工程师官方指定清华版教程.信息安全工程师高清视频教程.持续更新后续年份的资料.请点赞!!请点赞!!!绝对全部货真价实 ...

  2. 妈妈再也不用担心别人问我是否真正用过redis了

    1. Memcache与Redis的区别 1.1. 存储方式不同 1.2. 数据支持类型 1.3. 使用底层模型不同 2. Redis支持的数据类型 3. Redis的回收策略 4. Redis小命令 ...

  3. python爬虫07 | 有了 BeautifulSoup ,妈妈再也不用担心我的正则表达式了

    我们上次做了 你的第一个爬虫,爬取当当网 Top 500 本五星好评书籍 有些朋友觉得 利用正则表达式去提取信息 太特么麻烦了 有没有什么别的方式 更方便过滤我们想要的内容啊 emmmm 你还别说 还 ...

  4. 使用BeautifulSoup高效解析网页,再也不用担心睡不着觉了

    BeautifulSoup是一个可以从 HTML 或 XML 文件中提取数据的 Python 库 那需要怎么使用呢? 首先我们要安装一下这个库 1.pip install beautifulsoup4 ...

  5. 锋利的js之妈妈再也不用担心我找错钱了

    用js实现收银功能. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <hea ...

  6. 【阿里云产品公测】离线归档OAS,再也不用担心备份空间了

    [阿里云产品公测]离线归档OAS,再也不用担心备份空间了 作者:阿里云用户莫须有3i 1 起步  1.1 初识OAS  啥是OAS,请看官方说明: 引用: 开放归档服务(Open Archive Se ...

  7. 有了 tldr,妈妈再也不用担心我记不住命令了

    引言 有一次我在培训时说「程序员要善于使用 Terminal 以提高开发效率」,一位程序员反驳道:「这是 21 世纪,我们为什么要用落后的命令行,而不是先进的 GUI?」 是的,在一些人眼里,这个黑黑 ...

  8. 妈妈再也不用担心我使用git了

    妈妈再也不用担心我使用git了 Dec 29, 2014 git git由于其灵活,速度快,离线工作等特点而倍受青睐,下面一步步来总结下git的基本命令和常用操作. 安装msysgit 下载地址:ms ...

  9. 利用CH341A编程器刷新BIOS,恢复BIOS,妈妈再也不用担心BIOS刷坏了

    前几天,修电脑主析就捣鼓刷BIOS,结果刷完黑屏开不了机,立刻意识到完了,BIOS刷错了.就从网上查资料,各种方法试了个遍,什么用处都没有.终于功夫不负有心人,找到了编码器,知道了怎么用.下面看看具体 ...

随机推荐

  1. Zotero导入Markdown here插件

    1. 下载Markdown Here源码包 网址:https://github.com/adam-p/markdown-here 2. 创建.xpi后缀文件 将文件夹 中的这几个文件放入同一个文件夹中 ...

  2. Google Cayley图数据库使用方法

    最近在用Golang做流程引擎,对于流程图的存储,我看到了Google的Cayley图数据库,感觉它可能会比较适合我的应用,于是便拿来用了用. 项目地址在这里:https://github.com/g ...

  3. Java8新特性探索之Lambda表达式

    为什么引入Lambda表达式? Lambda 表达式产生函数,而不是类. 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 lamb ...

  4. eclipse之SSH配置spring【二】

    第一篇中配置struts完成(http://www.cnblogs.com/dev2007/p/6475074.html),在此基础上,继续配置spring. web.xml中增加listener,依 ...

  5. PHP date_formate使用相关

    $date=date_create("2016-09-25"); echo date_format($date,"Y/m/d H:i:s");要使用date_f ...

  6. 【Luogu】P1436 棋盘分割 题解

    嗯,点开题目,哇!是一道闪亮亮的蓝题! 不要被吓到了,其实,这道题就是一个简单的DP啦! 我们设 \(f[x1][y1][x2][y2][c]\) 为以 \((x1,y1)\) 为左上角,以 \((x ...

  7. JS数组去重的9种方法(包括去重NaN和复杂数组类型)

    其实网上已经有很多js数组的去重方法,但是我看了很多篇并自己通过代码验证,发现都有一些缺陷,于是在研究多篇代码之后,自己总结了9种方法,如果有哪里不对请及时纠正我哈~ 转载请表明出处 测试代码 let ...

  8. 13 SOAP

    13 SOAP SOAP(原为Simple Object Access Protocol的首字母缩写,即简单对象访问协议)是交换数据的一种协议规范,使用在计算机网络Web服务(web service) ...

  9. .NET 5 和 C#9 /F#5 一起到来, 向实现 .NET 统一迈出了一大步

    经过一年多的开发,Microsoft 于北京时间 11 月 11 日(星期三)发布了其 .NET 5软件开发平台,强调平台的统一,并引入了 C# 9 和 F# 5 编程语言,新平台朝着桌面.Web.移 ...

  10. python实现年会抽奖程序

    用python来实现一个抽奖程序,供大家参考,具体内容如下 主要功能有 1.从一个csv文件中读入所有员工工号2.将这些工号初始到一个列表中3.用random模块下的choice函数来随机选择列表中的 ...