Android---RecyclerView替代ListView

news2024/10/7 8:21:04

RecyclerView 简称RV,是作为 ListView 和 GridView 的加强版出现,目的是在有限的屏幕之上展示大量的内容。因此,RecyclerView 的复用机制的实现是它的一个核心部分。

RV 常规使用方式

// 1
RecyclerView.setLayoutManager();
// 2
RecyclerView.setAdapter();
// 3
RecyclerView.setItemAnimator();
// 4
RecyclerView.addItemDecoration();

\bullet setLayoutManager:必选项,设置 RV 的布局管理器,决定 RV 的显示风格。常用的有线性布局管理器(LinearLayoutManager)、网格布局管理器(GridLayoutmanager)、瀑布流布局管理器(StaggeredGridLayoutManager)。

\bullet setAdapter:必选项,设置 RV 的数据适配器,当数据发生改变时,以通知者的身份,通知 RV 数据改变进行列表刷新操作。

\bullet setItemDecoration:非必选项,设置 RV 中 Item 的装饰器,经常用来设置 Item 的分割线

\bullet setItemAnimatior:非必选项,设置 RV 中 Item 的动画。

下面我们看看 RV 是如何一步一步将每个 Item 显示到屏幕上。并且分析在显示和滑动过程中,是如何通过缓存复用来提升整体性能 ,将沿着 onMeasure -> onLayout -> onDraw 这 3 个方法的路线来深入研究。

绘制流程

RV 的 onMeasure() 方法

上述代码中,1处表示在 xml 布局文件中 RV 的宽高被设置为 match_parent 或者具体值。那么直接将 skipMeasure 设置为 true,并调用 mLayout.onMeasure() 方法来测量自身的宽高即可。图中 2处表示 xml 布局文件中 RV 的宽高设置为 wrap_content,则会执行 dispatchLayoutStep2(),其实就是测量 RV 的子 view 的大小,最终确定 RV 的实际宽高。

注意:dispatchLayoutStrp1() 与 RV 的动画有关。

RV 的 onLayout() 方法

仅调用了一层 dispatchLayout() 方法。此方法具体如下

如果在 onMeasure() 阶段没有执行 dispatchLayoutStep2() 去测量子 View,则会在 onLayout() 阶段重新执行。

dispatchLayoutStep2() 源码如下

可以看出,核心逻辑是调用了 onLayoutChildren() 方法。这个方法是 LayoutManager 中的一个空方法。主要作用是测量 RV 中子 View 的大小并确定它们所在的位置。

LinearLayoutManager、GridLayoutManager、以及 StaggeredLayoutManager 都分别复写了onLayoutChildren这个方法,并实现了不同方式的布局。

以 LinearLayoutManager 为例,其 onLayoutChildren() 实现如下

上图中1处,在 onLayoutChildren 方法中调用 fill() 方法完成子 View 的测量布局工作。图中2处,在 fill() 方法中通过 while 循环判断是否还有足够剩余空间来绘制一个完整的子 View。图中3处,LayoutChunk 方法中是子 View 测量布局的真的实现。每次执行完之后需要重新计算 remainingSpace。

LayoutChunk 是一个非常核心的方法。这个方法执行一次就填充一个 itemVIew 到 RV。部分代码如下

图中1处,从缓存中取出一个 itemView,然后调用 addView() 或者 addDisappearingView() 将子 itemView 添加到 RV 中。图中2处测量被添加到 RV 中的子 View 的宽高。图中3处根据所设置的 Decoration、margin 等所有选项确定子 View 的显示位置。

RV 的 onDraw() 方法

测量和布局都完成后,就剩下最后的绘制操作了。

如果有添加 ItemDecoration,则循环调用所有 decoration 的 onDraw 方法,将其显示。至于所有的子 ItemView 是通过 Android 渲染机制递归的调用子 ItemView 的 draw 方法显示到屏幕上。

小结

 RV 会将测量 onMeasure 和布局 onLayout 的工作委托给 LayoutManager 来执行。不同的 LayoutManager 会有不同风格的布局显示,这是一种策略模式。

缓存复用原理 Recycler

缓存复用是 RV 中另外一个重要的机制。这套机制主要实现了 ViewHolder 的缓存以及复用。核心代码如下

为 RV 中的一个内部类,主要用来缓存屏幕内 ViewHolder 以及部分屏幕外 ViewHolder。Recycler 的缓存机制是通过上述代码中的这些数据容器来实现的。实际上 Recycler 的缓存也是分级处理的,根据访问优先级从上到下可以分为4级,如下图所示

各级缓存功能

RV 中之所以将缓存分成这么多块,是为了在功能上进行一些区分,并分别对应不同的使用场景。

1. 第一级缓存 mAttachedScrap&mChangedScrap

这是两个名为 ScrapArrayList。这两者主要用来缓存屏幕内的 ViewHolder。

为什么屏幕内的 ViewHolder 需要缓存呢?

如下一种应用场景,下拉刷新。当刷新被触发时,在原有的 ViewHolder 基础上进行重新绑定新的数据 data,旧的 ViewHolder 被保存在 mAttachedScrap 和 mChangedScrap 中。实际上,当我调用 RV 的 notify 等等方法时,就会向这两个列表填充,将旧 ViewHolder 缓存起来。

2. 第二级缓存 mCachedViews

用来缓存移出屏幕外的 ViewHolder,默认情况下缓存个数为 2,缓存容量是不能改变的。如果 mCachedViews 的容量已满,则会根据先入先出的规则将旧的 ViewHolder 抛弃,然后添加新的 ViewHolder,如下图所示

通常 情况下,刚被移出屏幕的 ViewHolder 有可能接下来马上就会使用到。所以,RV 不会立即将其设置为无效 ViewHolder,而是会将它们保存到 mCachedViews 中。但又不能将所以移出到屏幕外的 ViewHolder 都视为有效 ViewHolder,所以它的默认容量只有2个。

3. 第三级缓存 ViewCacheExtension

这是 RV 预留给开发人员的一个抽象类,在这个类中只有一个抽象方法

开发人员可以通过继承 ViewCacheExtension 类,并复写抽象方法 getViewForPositionAndType 来实现自己的缓存机制。只是一般情况下我们不会自己实现也步建议自己添加缓存逻辑,因为这个类的使用门槛比较高,需要开发人员对 RV 的源码非常熟悉。

4. 第四级缓存 RecycledViewPool

RecycledViewPool 是用来缓存屏幕外的 ViewHolder。当 mCachedViews 中的个数已满(默认为2),则从 mCachedViews 中淘汰出来的 ViewHolder 会先缓存到 RecycledViewPool 中。ViewHolder 在被缓存到 RecycledViewPool 时,会将内部的数据清理。因此,从 RecycledViewPool 中取出来的 ViewHolder 需要重新调用 onBindViewHolder 绑定数据

RecycledViewPool 还有一个重要的功能。多个 RV 之间可以共享 RecycledViewPool,这对于多 tab 界面的优化效果会很显著。

注意:RecycledViewPool 是根据 type 来获取 ViewHolder,每个 type 默认最大缓存 5 个。因此,多个 RV 共享 RecycledViewPool 时,必须确保共享的 RecyclerView 使用的 Adapter 是同一个,或者 ViewType 是不会冲突的。

RV 是如何从缓存中获取 ViewHolder 的

在 onLayout() 阶段,在 layoutChunk() 方法中通过调用 layoutState.next() 方法,拿到某个子 itemView,然后添加到 RV 中。next() 详细代码如下

代码继续往下跟。可以看出,通过调用 tryGetViewHolderForPositionByDeadline 方法来查找相应位置上的 ViewHolder。

tryGetViewHolderForPositionByDeadline 的代码如下

在这个方法中,会从上面介绍的4级缓存中依次查找。如图中红框所示,如果在各级缓存中都没有找到相应的 ViewHolder,会在 Adapter 的 createViewHolder 方法中创建一个新的 ViewHolder。

何时将 ViewHolder 存入缓存

ViewHolder 存入各级缓存的场景。

1. 第一次 layout。

当调用 setLayoutManager 和 setAdapter 之后,RV 会经历第一次 layout,并被显示到屏幕上。此时,并不会有任何 ViewHolder 的缓存。所有的 ViewHolder 都是通过 createViewHolder 创建的。

2. 刷新列表

如果通过手势下拉刷新,获取到新的数据 data 之后。会调用 notify 方法通知 RV 数据发生改变。RV 会将屏幕内的所有 ViewHolder 保存在 scrap 中。如下所示

当缓存执行完之后,后续通过 RV 就可以从缓存中获取相应 position 的 ViewHolder,姑且称为旧 ViewHolder,然后将刷新后的数据设置到这些 viewHolder 之上,最后再将新的 ViewHoler 绘制到 RV 上。

总结

深入分析了Android RecyclerView源码中的2块核心实现:

● RecyclerView是如何经过测量、布局,最终绘制到屏幕上其中大部分工作是通过委托给LayoutManager来实现的。

●RecyclerView的缓存复用机制,主要是通过内部类Recycler来实现。


谷歌Android团队对RecyclerView做了很多优化,导致RecyclerView最终的代码极其庞大
理解RecyclerView的源码实现,有助于我们:

● 快速定位问题原因;

● 拓展RecyclerView功能;

● 提高分析 RecyclerView性能问题的能力。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1117385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaCV + FFmpeg 播放音视频

JavaCV FFmpeg 播放音视频 1、导入JavaCV库1.1 使用ffmpeg必要库1.2 简单FFmpeg命令 待续~~~~ FFmpeg documentation bytedeco/javacv - GitHub 1、导入JavaCV库 gradle下面这种会导入javacv-platform所有包,非常耗时:https://repo.maven.apache.org/…

西门子博途软件加密保护方法

一、程序块的专有技术保护 程序块的专有技术保护主要是对项目中的程序块(OB、FB、FC、DB)进行访问保护,如果没有专有技术保护密码则无法看到程序块中的具体内容,对于专有技术保护的 DB 块,如果没有密码只能读不能写。…

怎么理解函数式编程思维?

文章目录 (2023年9.29号,正月十五家乡的月亮) ​ 理解函数式编程要注重思维的转变。函数式编程聚焦于简洁的高阶函数,高阶函数注重封装底层运作原理来解决复杂的业务场景,比如 Scala、Groovy、Clojure 语言&#xff1a…

解决pip安装包后但是Pycharm检测不到

首先要知道python找包的原理:原理 之后把一下代码打印一下: import sys print(sys.executable)# /usr/bin/python2 print(sys.path)# [/usr/lib/python2.7, /usr/lib/python2.7/dist-packages, /usr/local/lib/python2.7/dist-packages] print(sys.prefi…

Leetcode—动态规划(背包问题)

1、背包基础问题:01背包 输入:背包最大重量为 4。物品重量数组weight[1,3,4],对应的价值数组value[15,20,30]。 五部曲: 1、确定dp数组以及下标的含义 对于背包问题,dp采用二维数组,即dp[i][j]表示从下标…

第78篇:巧妙方法抓取某商用红队扫描器的4000多个漏洞利用exp

Part1 前言 大家好,我是ABC_123,本期分享一个真实案例。大约在两年前,有机会接触到一台红队扫描器设备(也可以理解为渗透测试机器人),我抱着好奇的心态去那里做了一下测试,感觉还不错。里面大概…

jvm内存溢出溯源

1.先上神器 2.远程监控配置 JProfile是一款性能瓶颈分析工具,具体要干啥呢下面看 1:创建一个监控任务 2:选择tomcat版本 3:监控远程服务器 4:选择oracle 1.5.0 5:填写需要监控的服务器地址 6&#x…

后台交互-首页->与后台数据进行交互,wsx的使用

与后台数据进行交互wsx的使用 1.与后台数据进行交互 // index.js // 获取应用实例 const app getApp() const apirequire("../../config/app.js") const utilrequire("../../utils/util.js") Page({data: {imgSrcs:[{"img": "https://cd…

线性代数1:线性方程和系统

图片来自施泰德博物馆 Digital Collection (staedelmuseum.de) 一、前言 通过这些文章,我希望巩固我对这些基本概念的理解,同时如果可能的话,通过我希望成为一种基于直觉的数学学习方法为其他人提供额外的清晰度。如果有任何错误或机会需要我…

ToDoList全局事件总线学习笔记

全局事件总线 全局事件总线:任意组件间通信 安装全局事件总线 new Vue({……beforeCreate(){Vue.prototype.$busthis},…… })使用事件总线 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自…

2023年【氧化工艺】考试题库及氧化工艺考试总结

题库来源:安全生产模拟考试一点通公众号小程序 氧化工艺考试题库根据新氧化工艺考试大纲要求,安全生产模拟考试一点通将氧化工艺模拟考试试题进行汇编,组成一套氧化工艺全真模拟考试试题,学员可通过氧化工艺考试总结全真模拟&…

镜头边缘的解析力通常比中心差很多的原因是什么?

1、问题背景 之前有总结过一篇文章,“ 相机出图画面一半清晰,一半模糊的原因是什么?”里面有描述到关于镜头边缘的清晰度通常比中心要差的原因主要是光的折射导致的,有读者指出问题,折射率是和传输介质相关&#xff0…

并行计算技术与SIMD、SIMT

并行计算 指令并行 SIMD Inter MMX 64bitSSE SSE2 128bitAVX AVX2 256bitAVX-512 512bit ARM Neon 128bitSVE 128-2048bit RISC-V V指令扩展 SIMT CUDA openmp 线程并行MPI 进程并行 参考 SIMD - 百度百科 并发编程:SIMD 介绍

【考研数学】概率论与数理统计 —— 第六章 | 数理统计基本概念(2,三个重要的抽样分布)

文章目录 引言一、 χ 2 \chi^2 χ2 分布1.1 χ 2 \chi^2 χ2 分布定义1.2 性质 二、 t t t 分布2.1 定义2.2 性质 三、 F F F 分布3.1 定义3.2 性质 写在最后 引言 对数理统计的一些基本概念有了了解后,我们来学习三个重要的抽样分布。 一、 χ 2 \chi^2 χ2 分布…

mysql过期数据的清理方案(Java/springboot+mybatis)

比如说现在数据库表信息增加的很快&#xff0c;然后我们需要对每个表设置过期删除策略&#xff1b; 大概思路就是&#xff1a;定时任务调度&#xff0c;给每个表制定sql&#xff0c;然后执行删除数据的sql //删除一个月前的数据 delete FROM test_info WHERE create_time <…

C语言可执行程序到底怎样生成?

目录 程序的翻译环境 NO1.VS编译器工具 NO2.VS链接器工具 NO3.链接库是什么&#xff1f; 编译 预处理 编译 汇编 链接 程序的执行环境 C语言的程序到底是怎样生成的呢&#xff1f;又怎样去执行呢&#xff1f;我们来探索。本篇是讲解编译环境。 在ANSI C&#xff08…

如何避免osg绘制场景时因Z冲突导致绘制重影或不正常

目录 1. 问题的提出 2. Z冲突&#xff08;z-fighting&#xff09;简介 2.1. Z冲突&#xff08;z-fighting&#xff09;产生的原因 2.2. 如何消除Z冲突&#xff08;z-fighting&#xff09; 3. 代码实现 1. 问题的提出 今天绘制了一个棋盘格&#xff0c;鼠标在棋盘格上单击…

亲测防止google colab自动disconnect断连GPU

最近小虎在用colab跑diffusion的模型&#xff0c;但是运行的时候一直断连&#xff0c;就算充了会员也依然如此。 坏境 win11 chrome 解决方法 用Ctrl shift i打开console&#xff0c;输入 function ClickConnect() {console.log("Working");document.querySe…

倒计时 1 天|KCD 2023 杭州站

距离「KCD 2023 杭州站」开始只有 1 天啦 大家快点预约到现场哦&#xff5e; KCD 2023 活动介绍 HANGZHOU 关于 KCD Kubernetes Community Days&#xff08;KCD&#xff09;由云原生计算基金会&#xff08;CNCF&#xff09;发起&#xff0c;由全球各国当地的 CNCF 大使、CNCF 员…

STM32串口

前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 目前已经学习了GPIO的输入输出&#xff0c;但是没有完整的显示信息&#xff0c;最便宜的显示就是串口。 000 -111 AVR单片机 已经学会过了&#xff0c; 提示&#xff1a;以下是本篇文章正文内容&#x…