zoukankan      html  css  js  c++  java
  • RecyclerView和ListView的区别

    RecyclerView和ListView的区别

    ListView

    Android ListView工作原理完全解析,带你从源码的角度彻底理解

    郭霖ListView异步加载图片闪动的问题

    ListView缓存机制

    RecycleBin机制,RecycleBin定义在AbsListView当中。其中使用View[] mActiveViews存储View,是屏幕当中正在使用的View,mActiveViews中存储的View只能被获取一次;ArrayList[] mScrapViews和ArrayList mCurrentScrap则是用于存储移动出屏幕后被废弃的View。当getView方法在适配器中被调用的时候,其中传入的convertView如果不为空,就是从废弃的View当中获得的。

    ListView 异步加载图片出现乱序的原因

    ListView 在借助RecycleBin机制的帮助下,实现了一个生产者消费者的模式,不管有多少条数据需要显示,ListView中的子View其实来来回回就那么几个,移出屏幕的子View 很快会被移入屏幕的数据重新利用起来, 移出屏幕的view会进入到RecycleBin当中,而新进入屏幕的元素则会从RecycleBin中获取view ,这就是 RecycleBin 机制。我们来说说产生乱序的原因,每当有新元素进入界面的时候就会回调 getView 方法,如果我们是异步加载图片的话, 就会在 getView 方法中开启异步操作从网络上面获取图片,但是异步操作可能是比较耗时的,当我们快速滑动图片的时候可能会出现这样一种情况,某一位置上的元素进入屏幕之后开始从网络上请求图片,但是还没等图片加载到控件上的时候,它就已经被移出了屏幕,又根据 ListView 的工作机制,被移出屏幕的控件会很快被进入屏幕的元素重新利用起来,正是因为这个原因导致了图片乱序,因为如果这时候前面发起的网络请求正好得到回应的话, 首先会将刚才请求的图片显示到控件上面,虽然他们的位置不同,但是共用同一个显示图片的控件,这时候新移进来的元素如果正好也发起一个网络请求获取图片的话,在图片下载结束就会将图片显示到当前控件上面,因此就出现了先显示一张图片后又显示一张图片的情况了。

    解决这个问题的方法有:在 getView 方法中对每个控件调用 setTag 方法,设置其标志,在需要用到控件的地方使用 findViewWithTag 获得控件,这种方式解决图片乱序的原理是:因为 ListView 中的控件都是可以重用的,当移出屏幕的控件重新被利用的时候会回调 getView 方法,在 getView 方法中会调用 setTag 方法为当前控件设置标志,那么这个标志将会覆盖掉原先设置在该控件上面的标志,这样每次使用 findViewWithTag 获取对应于旧的 Tag 的控件的时候就只能得到 null 了,而只有非 null 的时候我们才会显示图片到控件上面,这样就保证了不会显示旧的图片在控件上面的效果了。

    **ListView 优化方案 **

    关于 ListView 的优化总结几点

    • 重用 ConvertView,ConvertView会把之前加载好的布局进行缓存,可以重用,就减少不必要 view 的创建,因为 inflate 操作也是把 xml 实例化为相应的 View 实例,这个过程是属于 IO 操作,相对来说比较耗时。

    • 减少 findViewById 的次数,因为即使使用了ConvertView以后不会重复加载布局了,但是每次getView方法回调的时候还是通过findViewById 重复创建控件的实例,那么就通过把 xml 文件中的涉及到的控件封装成内部类ViewHolder的方式, 通过 View 的 setTag 和 getTag 方法将 当前布局View 和对应的 ViewHolder 对象绑定在一起,减少不必要 的 findViewById 次数。

    • 如果涉及到加载图片之类比较耗时的操作,可以在用户快速滑动屏幕的时候禁止图片的加载,等到滑动停止的时候再去加载图片。

    • ListView 中 Item 的布局层次越简单越好,主要为了避免布局太深带来的重绘太复杂问题,还可以减少半透明元素的绘制,因为半透明更加的耗时。

    • 尽量保证 Adapter 适配器的 hasStables 方法返回 true,这样在调用 notifyDataSetChanged()方法的时候,如果 Item 内容没有发生变化的话,ListView 将不再重绘,以此达到优化。

    • 直接使用 RecycleView 代替 ListView,每个 Item 自己发生变动,ListView 都会去调用 notifyDataSetChanged 方法更新所有的 Item,未免有点太浪费性能了,而 RecycleView 可以实现每个 Item 的局部刷新,不再需要刷新所有的 Item,同时引入了增加和删除的动画 效果,在性能和定制上有很大改善。

    • 尽量开启硬件加速功能。

    • 当数据量比较大的时候,可以使用分段加载或者分页加载。

      分段加载。有些情况下需要加载网络中的数据,显示到ListView,而且往往此时都是数据量比较多的一种情况,如果数据有1000条,没有优化过的ListView都是会一次性把数据全部加载出来的,很显然需要一段时间才能加载出来,我们不可能让用户面对着空白的屏幕等好几分钟,那么这时我们可以使用分段加载,比如先设置每次加载数据10条,当用户滑动ListView到底部的时候,我们再加载20条数据出来,然后使用Adapter刷新ListView,这样用户只需要等待10条数据的加载时间,这样也可以缓解一次性加载大量数据而导致OOM崩溃的情况。

      分页加载。上面分段加载也不能完全解决OOM崩溃的情况,因为虽然我们在分段中一次只增加10条数据到List集合中,然后再刷新到ListView中去,假如有10万条数据,如果我们顺利读到最后这个List集合中还是会累积海量条数的数据,还是可能会造成OOM崩溃的情况,这时候我们就需要用到分页,比如说我们将这10万条数据分为1000页,每一页100条数据,每一页加载时都覆盖掉上一页中List集合中的内容,然后每一页内再使用分批加载,这样用户的体验就会相对好一些。

    ListView三级缓存

    • 内存缓存
    • 磁盘缓存
    • 网络拉取

    RecyclerView

    RecyclerView 源码解析

    把 RecyclerView 撸成马蜂窝

    RecyclerView缓存机制(咋复用?)

    RecyclerView缓存机制

    Scrap和Cache分别是通过position去找ViewHolder可以直接复用;

    在RecyclerView当中也并不是每次都重新创建ViewHolder对象,不是每次都重新绑定ViewHolder数据,而是通过Recycler来获得下一个ViewHolder。

    RecyclerView使用Recycler管理缓存ViewHolder,对于不同状态的ViewHolder存储在了不同的集合中,RecyclerView有四级缓存,分别是ArrayList mAttachedScrap、ArrayList mCachedViews、ViewCacheExtension mViewCacheExtension 和 RecycledViewPool mRecyclerPool,缓存的对象是ViewHolder

    而从RecycledViewPool 中复用的ViewHolder需要重新绑定数据,并且复用的ViewHolder只能复用于ViewType相同的表项,因为RecycledViewPool 对ViewHolder是按照ViewType分类存储的,RecycledViewPool通过type来获取ViewHolder,获取的ViewHolder是个全新,需要重新绑定数据;ViewCacheExtension自定义缓存,目前来说应用场景比较少却需慎用;从mCachedViews中复用ViewHolder只能复用于指定位置的item;而从mAttachedScrap复用的ViewHolder不需要重新创建也不需要重新绑定数据。

    如果四个层级的缓存都没有命中,才会重新创建ViewHolder对象并且绑定。


    ListView和RecyclerView的对比

    基础使用

    ListView的适配器最终是继承BaseAdapter类重写方法,自定义ViewHolderConvertView一起完成复用优化工作。RecyclerView 同样也是需要继承重写 RecyclerView.Adapter 和 强制使用RecyclerView.ViewHolder ,以及使用到了LayoutManager。

    所以这样看来,在RecyclerView当中与ListView不同之处在于:

    • 对ViewHolder的编写进行了规范化。另外,RecyclerView对 Item的复用 ,关于使用到ViewHolder对控件实例进行缓存的工作已经封装好,不需要像 ListView 那样调用 setTag 。
    • 但是 RecyclerView 多出了 LayoutManager 的设置工作 。

    布局效果

    • ListView 做到了数据和视图的分离,布局排列是自身去管理。
    • 而RecycleView 将视图和布局进一步分离, 因而出现了 LayoutManager,RecycleView 只负责管理视图的重复利用,然后将布局的管理全权交给了 LayoutManager, 不像 ListView 那样被限制在垂直滚动布局样式 。LayoutManager中制定了一套可扩展的布局排列接口,子类只要按照接口规范来实现,就能定制出各种不同排列方式的布局了。 LayoutManager 是一个抽象类,系统已经为我们提供了三个相关的实现类 LinearLayoutManager(线性布局效果)GridLayoutManager(网格布局效果)StaggeredGridLayoutManager(瀑布流布局效果)。 RecyclerView 默认就能支持 线性布局网格布局瀑布流布局 三种 ,通过配置和切换 LayoutManager 就可以获得不同的布局效果。所以如果想自定义出更多布局效果,可以继承重写自己的LayoutManager,通过LayoutManager还可以设置滚动方向、获取Item的位置等等。

    缓存机制

    • ListView的RecycleBin机制
    • RecyclerView的多级别缓存

    事件监听

    • ListView中提供了setOnItemClickListener、 setOnItemLongClickListener、setOnItemSelectedListener 等几个用于设置监听器,但是这些只能注册的是子项的点击事件,具体到子项里面的某个控件的点击事件的处理会比较麻烦,而且添加了 HeaderView 和 FooterView ,ListView 会把 HeaderView 和 FooterView 算入 position 内,代码有可能会报数组越界的错误 。
    • 但是RecyclerView ,它并没有像 ListView 提供太多关于 Item 的某种事件监听,需要我们自己通过ViewHolder去给子项具体的View去注册点击事件,或者就是通过 addOnItemTouchListener 和GestureDetector手势判断来实现

    空数据的处理

    • ListView 提供了 setEmptyView 这个 API 来让我们处理 Adapter 中数据为空的情况。
    • 但是 RecyclerView 并没有提供这类 API ,所以需要自己处理。

    HeaderView 和 FooterView

    • 在 ListView 的设计中,addFooterView 、 addFooterView 可以添加HeaderView 和 FooterView 两种类型的视图 。
    • RecyclerView 并没有提供这类 API。

    局部刷新

    • 更新了 ListView 的数据源后需要通过 Adapter 的 notifyDataSetChanged 方法来通知视图更新变化,调用简单,但它会重绘每个 Item,实际上却并不是每个 Item 都需要重绘。

    • 而RecyclerView.Adapter 提供了 notifyItemChanged 用于更新单个 Item View 的刷新,我们可以省去自己写局部更新的工作。

    动画效果

    RecyclerView 支持子项目层次的动画效果,是通过 ItemAnimation 接口(确定一下)实现的, 可以在 Adapter 中的数据发生变化的时候,通过调用 Adapter 的相关方法激活动画的产生,就是RecyclerView 在做局部刷新等等的时候有一个渐变的动画效果 。

    嵌套滚动机制

    掘金

  • 相关阅读:
    SVN的import和export的使用
    windows下CreateDirectory创建路径失败的解决办法
    windows下查看rabbitmq服务是否启动
    tcp和udp的socket形式
    sockaddr_in 转成string
    Halcon系列(1) 菜鸟入门
    tesseract系列(3) -- tesseract训练
    tesseract系列(2) -- tesseract的使用
    springboot之redis
    hadoop格式化
  • 原文地址:https://www.cnblogs.com/chen-ying/p/12386712.html
Copyright © 2011-2022 走看看