位置: 编程技术 - 正文

RecycleBin原理解析,带你领会ListView的View重用机制(recyclebinh)

编辑:rootadmin

推荐整理分享RecycleBin原理解析,带你领会ListView的View重用机制(recyclebinh),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:recycled bin,recycled bin,recycle bin,recyclibinhw,recycle/bin,recyclebinghw,recyclebinh,recyclebinwh,内容如对您有帮助,希望把文章链接给更多的朋友!

ListView无疑是Android开发中使用最多的组件之一了,可以肯定是%以上的应用中都是用了ListView,不过ListView也不是万能的,很多时候你会觉得ListView提供给我们的功能并不够,我们需要扩展ListView,或者重新自定义一个支持滚动,view重用特性的组件。如果我们能够了解ListView的内部实现原理,相信对于更好地利用ListView以及对其进行扩展都是不错的。

转载请注明出处: view的重用

ListView最重要的特性就是对view进行了重用,我们只要理解了view的重用原理,就对listview有了很大程度的了解。我们来看看它是如何做的呢?在将这个之前,让我们简要看看ListView的继承结构,ListView继承自AbsListView,而AbsListView继承自AdapterView,AdapterView继承自ViewGroup,因此我们从AdapterView说起就比较明了了。

AdapterView是一个抽象类,里面定义很多接口方法,有待子类去实现,不过也实现了一部分的方法。如下图所示

其中有一部分是继承自ViewGroup的,比如AddView系列以及removeView系列,Accessibility系列。这里我们就不多讲了,相信大家也很熟悉,除此之外就是AdapterView自己的函数,AdapterView顾名思义肯定是根据一个Adapter来生成它内部的view。说到这,我们又不得不引出Adapter这个接口,注意这是个接口,它只是定义了一些接口函数。

这些函数规定了适配器内部每个item的类型,id,每个item对应的view,以及总公的item数量。此外有一个函数需要注意的是hasStableIds(), 这个函数返回一个bool值,如果为true,代表同一个id总是对应同一个item。这个函数在稍后我们将view重用时会用到。AdapterView接口规定了使用Adapter来生成view的函数。

2. RecycheBin

关于view重用的代码大部分都在AbsListView里面,那是因为不光是ListView, 还有GridView也继承了AbsListView,他们都是使用了相同的view重用的原理。我们直接看AbsListView,这个类比较复杂,不过不要担心,我们就从view重用这一点慢慢展开,我们可以看到它有一个内部类叫RecycleBin,正是这个类实现了view的重用,看名字也可以看出。它的成员比较简单,就这些:

mActiveViews代表了在每一次layout开始的时候,位于屏幕上的view, mFirstActivePosition指定了第一个active的view的位置。对于mActiveViews通常的操作为,在每次layout开始,AbsListView会将位于屏幕上的view全部填充到RecycleBin的mActiveViews中去。layout过程中,将下一轮即将显示在屏幕上得view从RecycleBin中取出来,最后如果mActiveViews中还有元素,就在layout结束时将它们统统转移到mScrapView中去。这个流程可以从ListView中的layoutChildren看出,每次layout时,onlayout最终会调用这个函数

从layout的过程就可以看出来,scrapview实际上是一个存放备用view的回收池,每次layout完,有多余的view会存储到池子里,以后可能会用到。那这是layout时候做的事情,如果是scroll的情况呢,情况其实类似。我们来看看scroll的流程图

RecycleBin原理解析,带你领会ListView的View重用机制(recyclebinh)

scroll事件从onTouchEvent函数发起,大家肯定知道的,中间经过一些判断,最终带着deltaY和incrementalDetalY到达trackMotionScroll函数,我们的分析从这个函数开始.

从我的注释,大家可以看到在滚动过程中,RecycleBin所起的作用,当然函数在走到fillGap前,只是完成了一部分滚出view的回收,接下来,是利用这些view进行重用还是生成新的view就要看fillGap函数所做的操作了。

fillGap的实现在ListView中,它只是做了个分派操作,内部分向上滚动和向下滚动分别调用fillUp和fillDown,我们分析其中一个就好

fillDown的两个参数分别为即将开始填充的view的item 位置 和 top坐标位置。

makeAndAddView所做的操作就是获得一个view并将其添加到viewHierarchy上。

makeAndAddView在数据没有改变的情况下,会首先尝试从mActiveViews中去获取。不过需要注意的是,在scroll操作中,我们一开始并没有把屏幕上的view填充到mActiveViews中,因此scroll逻辑走到这里的时候,从mActiveViews中是拿不到view的,为什么还有这一段呢?那是因为这段代码在layout时也会调用的,从layoutChildren函数的重新填充子view那一步中,会调用一系列以fill开头的函数,最终这些函数都会走到这里。现在我们将注意力集中到obtainView上去。

3. Transient State

说到这里读者肯定回问什么是TransientState的View,大家可以看到View类中其实有两个方法,一个叫做hasTransientState,一个叫setHasTransientState,如果一个view被设置了具有transient state,那么系统会尽量保持view的状态属性,不让其它被其他数据模型绑定,比如这个view正在执行动画操作,或者这个view正在跟踪用户的选择。比如我们正在对某个view执行动画操作时,我们可以设定setHasTransientView(true),动画结束后,再设定setHasTransientView(false),注意这两个必须成对出现. 将view设成transient state的其实是对view的一个保护,不让其被其填充新的数据。

RecycleBin对这种状态的view做了单独的处理,其内部有两个SparseArray,用来存储已经滚出屏幕但是设置了transient state状态的view。

两者的区别在于,前者可以通过item的位置找到view,后者通过item的id,找到view。 在前面的分析中,我们可以看到,每次scroll开始时,都会对滚出的屏幕的view调用addScrapView。其实在addScrap过程中,会优先考虑是否添加到这两个容器里面。

在scrapActiveViews函数中也有类似的操作。如果每个view都被设成了transient state,那么scrapView中将不会收到任何view,以至于每次都要重新生成新的view,也就是adapter的getView函数传来的convertView为null,这是因为所有滚出屏幕的view都被添加到transientViews中去了。大家可以试试在adapter的getview函数中,view返回前将其设置为transientState的,那每次我们都需要用inflater去inflate,或者new出新的view。

值得注意的是,只有在view被放入mCurrentScrap或mScrapViews中时,才会去调用onMoveToScrapHeap通知回收监听器,当前view已经被回收,是时候释放一些view所持有的资源了,比如释放图片。

4. SkippedScrap

最后,RecycleBin中还有一个不太重要的mSkippedScrap,什么时候添加view到其中呢?简单搜一下,只有一处,就是在addScrapView函数中,当当前view有transient state,但是却不满足stableId或者 mAdapter数据没有发生变化这两个条件, 这个view就会被添加到skippedScrap中,因为这个view,不能被回收,却又找不到对应的数据item。RecycleBin还有一个函数removeSkippedScrap

在trackMotionScroll,和layoutChildren中会去调用这段代码,很好理解,因为只有在滚动时重新layout时,才会view可能被加入skippedScrap中去。

好了,到了这里RecycleBin大部分的原理都讲得差不多了,其实看透了就很简单。大家在写类似view回收池时可以参考RecycleBin的写法哦。谢谢大家的阅读!

Go项目(二)、toolbar和Material Design风格的选择 一、问题的出现:使用google文档的写法,使用的materialdesign风格只能在androidL上面显示,sdk版本低于的模拟器出现崩溃,所以为了能够在低版本的手机

Android学习 各大网络请求库的比较 转载自:

android Intent的常用参数解说 1Intent.ACTION_MAINString:android.intent.action.MAIN标识Activity为一个程序的开始。比较常用。Input:nothingOutput:nothingactivityandroid:name=.Mainandroid:label=@string/app_nameintent-fi

标签: recyclebinh

本文链接地址:https://www.jiuchutong.com/biancheng/379705.html 转载请保留说明!

上一篇:android 内存使用总结(android内存使用情况)

下一篇:Go项目(二)、toolbar和Material Design风格的选择(gin项目)

  • 发票章是否需要到税务局备案
  • 预算会计退回预付差旅费如何记账?
  • 税务季度申报如何网上申报填写
  • 资产负债表怎么填
  • 销售人员购买的产品
  • 全额工资是到手工资吗
  • 同业清算交易渠道
  • 技术推广服务包含什么 奇瑞汽车
  • 没经营的个体户营业执照怎么注销
  • 内账固定资产已经费用化了怎么办
  • 税务1236600短信
  • 遗失的支票怎样避免经济损失
  • 利息收入可以作为业务招待费的基数吗
  • 会计核算的具体流程
  • 本月出库金额等于什么
  • 待摊费用不要了怎么做账?
  • 投资性房地产计量模式的转换
  • 公司取得增值税怎么算
  • 需不需要计提增值税发票
  • 免抵退说
  • 免税农产品的范围有哪些
  • 源泉扣缴税率是多少
  • 超市积分礼品
  • 六大减税措施内容
  • 关于有什么新的政策
  • 超市的商品进销表怎么做
  • 不交社保一告一个准罚多少钱
  • 担保扶持基金可以冲代偿吗
  • 商家收白条
  • PHP:oci_lob_is_equal()的用法_Oracle函数
  • 防水工程质量问题
  • w10怎么找蓝牙
  • 病毒dll文件
  • 金融企业贷款逾期怎么办
  • PHP:pg_escape_bytea()的用法_PostgreSQL函数
  • 公司的清洁费用是什么科目
  • 阿贡火山经常性爆发的原因
  • php中的include
  • 营改增后土增税收入
  • 代缴费社保
  • 虚开发票的管理办法是什么?
  • 银行贷款每个月都要还吗?
  • php下载远程文件到服务器
  • react.strictmode
  • 发票认证相符什么意思
  • 如何使用php给图片命名
  • 城建税减半征收会计分录
  • 减资的程序
  • 土地出让与土地划拨有什么区别
  • 开基本户一定要法人身份证吗
  • 生产油漆涂料的物质
  • 产品管理部门职责
  • 股权收购被收购方怎么做账
  • 电子产品对外加工
  • 预付账款转入其他应付款会计分录
  • 子公司注销母公司长投账务处理
  • 税种的分类方法有哪些
  • 应收账款核算内容
  • 什么是无形资产包括哪些
  • 所得税预缴多了可以不退税嘛
  • 配件销售人员应该具备哪些能力
  • 如何当好一个农民
  • 最新商业会计科目做账
  • vb.net invoke
  • MySQL存储过程和函数有什么区别?
  • 升级完鸿蒙系统5G咋没有了
  • 预览pdf文件
  • win10怎么安装网络驱动程序
  • xp系统1
  • win10玩游戏遇到问题需要重新启动
  • nodejs自启动
  • python time.now
  • linux命令行常用操作
  • android触屏事件的处理
  • Python装饰器入门学习教程(九步学习)
  • 莱鸟人集团
  • js正则regexp
  • 税务申报显示重名怎么办
  • 鄞江在哪里
  • 税控ic卡就是税控盘吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设