[译]Android 中 Kotlin 与 RecyclerView 高性能列表

2023-11-04

翻译说明:

原标题: Kotlin & RecyclerView for High Performance Lists in Android

原文地址: www.andreasjakl.com

原文作者: Andreas Jakl

RecyclerView 是在 Android 上显示滚动列表的最佳方法。它确保了高性能和平滑滚动,同时提供具有灵活布局的列表元素。结合 Kotlin 的现代语言功能,与传统的 Java 方法相比,RecyclerView 的代码开销大大降低。

示例项目:PartsList - 入门

在本文中,我们将介绍一个示例场景:维护应用程序的滚动列表,列出机器部件:“PartsList”。但是,此方案仅影响我们使用的字符串 - 您可以针对您需要的任何用例复制此方法。

要开始使用,请使用 Android Studio 3+ 创建一个新的 Android 应用。确保启用 Kotlin 支持,并为 MainActivity 选择 “Empty” 模板。或者,如果您不想手动编写以下步骤的代码,请从 GitHub 下载完成的源码

什么是 RecyclerView?

Android 中的屏幕列表由多个子视图组成。每个都有一个或多个视图的布局。例如,电子邮件应用会向您显示多封电子邮件; 这些项目中的每一项都包括主题,发件人姓名和一堆其他信息。

解析子视图的 XML 布局并将其扩展到类的实例中是一项消耗性能的操作。快速滑动会对手机性能造成巨大压力。目标是始终坚持每秒60帧。但是,这每帧只剩下不到17毫秒的计算时间。

RecyclerView 的主要技巧是重新使用列表中的子视图。一旦子视图 滚出可见区域,它就基本上被放入队列中。迟早,当新的子视图滚动时会再次用到它。然后,子视图的 UI 文本内容将被替换。下图显示了这个(简化的)RecyclerView 原理:

RecyclerView流程

不幸的是,这种有效的方法需要一些架构背景。刚开始看起来可能感到陌生。然而,一旦您拥有所有组件,就可以轻松的进行自定义。

使用RecyclerView需要配置/实现以下组件:

  • RecyclerView:管理一切。它主要由Android预先编写。您提供组件和配置。
  • Adapter:您将花费大部分时间编写此类。它连接到数据源。在RecyclerView的指示下,它会创建或更新列表中的各个项目。
  • ViewHolder:一个简单的类,用于分配/更新视图项中的数据。重新使用视图时,将覆盖先前的数据。
  • Data source:您喜欢的任何东西 - 从简单的数组到完整的数据源。您的适配器与之交互。
  • LayoutManager:负责将所有单独的视图项放在屏幕上,并确保它们获得所需的屏幕空间。

数据源和Kotlin数据类

我们的数据源是一个简单的列表/数组。该列表由自定义类的实例组成。您可以在应用程序中对这些进行硬编码,也可以动态创建它们 - 例如,基于来自在线源的 HTTP REST 请求。

Kotlin有一个很好的功能,可以简化数据类的编写。此方法适用于仅包含数据但不包含任何操作的所有类。我们的示例数据类称为 PartData,因为它存储有关维护应用程序的计算机部件的信息。

data class PartData ( val id: Long, val itemName: String)
复制代码

对于数据类,Kotlin自动生成所有实用程序函数:

  • 属性:您不再需要像普通 Java 那样编写 gettersetter
  • 附加功能:例如,equals()hasCode()toString()copy()

Antonio Leiva 写了一篇很棒的文章,在相同数据类的情况下将 Java 与 Kotlin 进行比较。您可以节省大约80%的代码并仍然得到相同的结果。

您像任何其他类一样实例化 Kotlin 数据类。请注意,Kotlin 不使用 new语句,因此我们只使用类名并将值提供给构造函数参数。添加该代码的nCreate() 您的 MainActivity。


var partList = ArrayList<PartData>()
partList.add(PartData(100411, "LED Green 568 nm, 5mm"))
partList.add(PartData(101119, "Aluminium Capacitor 4.7μF"))
partList.add(PartData(101624, "Potentiometer 500kΩ"))
// ...

复制代码

列表项目布局

RecyclerView 中的每个项目都需要自定义布局。您可以使用任意与 Activity 布局相同的方式创建这个文件:在 Android Studio 的 Android-style 项目视图中,打开 app > res > layout。右键单击"layout"文件夹名称,然后选择 New > Layout resource file。创建一个名为part_list_item.xml的布局。

对于示例用例,请使用以下属性:

  • Layout: LinearLayout (垂直)
  • Width: match_parent
  • Height: wrap_content
  • Padding: 16dp

接下来,将两个子项添加到布局中。我们将使用 TextView的。只要确保你给他们有用的ID,因为我们需要这些来分配Kotlin代码中的文本:

  • Id 1: @+id/tv_part_item_name (larger text size)
  • Id 2: @+id/tv_part_id (smaller text size)

Recycler View Dependencies

使用 Android 5.0(API级别21)将 RecyclerView 添加到 Android 系统中。许多应用程序仍然针对 Android 4.4+,因此您通常会通过 AppCompat / Support 库使用 RecyclerView 。将最新的支持库版本(取决于您的编译SDK)包含到应用程序构建build的依赖项中 .gradle 文件。

implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'
复制代码

在我们添加了这个定义之后,我们可以开始编写使用RecyclerView的源代码了。

ViewHolder:处理列表中的单个项目

首先,创建一个名为 PartAdapter 的类 。将其放在与 MainActivity 相同的目录中 。在这个类中,创建一个名为 PartViewHolder嵌套类

class PartViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bind(part: PartData) {
        itemView.tv_part_item_name.text = part.itemName
        itemView.tv_part_id.text = part.id.toString()
    }
}
复制代码

该 ViewHolder 介绍了项目视图。此外,它还在 RecyclerView 中存储其位置的元数据。

本质上,我们的 ViewHolder 实现的主要任务是将当前需要的数据绑定到先前膨胀的UI布局。每当滚动期间新项目可见时,此类确保项目的视图显示我们期望在列表中此位置的内容。

要更新 UI,我们创建自己的方法并将其命名为 bind()。请注意,这不是基类的重写方法,因此我们可以给它任何名称。作为方法参数,我们只需要在 UI 中显示数据。在 bind() 中,我们只是将提供的数据分配给视图项。

(从技术上讲,我们没有绑定它,我们只是分配数据。如果数据模型发生变化,它将不会自动更新此实现中的可见列表。)

Kotlin:主要构造函数

如果您是 Kotlin 的新手,您可能想知道:我们如何在 bind() 中访问 itemView?它从何而来?

魔术被称为主要构造函数。在 Kotlin 中,它可以成为类标题的一部分。前面的构造函数关键字是可选的。

class PartViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { ... }
复制代码

这有什么好处的? Kotlin 自动将所有参数作为属性提供。因此,我们没有看到属性定义和初始化。相反,我们只是从 bind() 函数中访问该属性 。

RecyclerView适配器:将它固定在一起

我们正在探索RecyclerView架构中最重要的组件:适配器。它有3个主要任务:

让我们看一下大图中的适配器以及这3个任务适合的位置:

  1. 为了在屏幕上绘制列表,RecyclerView 将询问适配器总共有多少项。我们的适配器在 getItemCount() 中返回此信息 。
  2. 每当 RecyclerView 决定它需要内存中的另一个视图实例时,它将调用 onCreateViewHolder()。在此方法中,适配器会膨胀并返回我们在上一步中创建的xml布局。
  3. 每次(重新)使用先前创建的 ViewHolder 时,RecyclerView 都会指示适配器更新其数据。我们通过重写 onBindViewHolder() 来自定义此过程 。

您不必编写代码来手动覆盖这三个函数。只需扩展 PartAdapter 类的定义 即可。让它继承 RecyclerView 的适配器。Android Studio 会自动提示您实施所有必需的成员:

然后,将参数 “partItemList” 添加到 PartAdapter 类中。使用主构造函数。这将允许 MainActivity 在实例化 PartAdapter 时提供数据模型。

和以前一样, partItemList 参数自动作为属性使用。在我们的例子中,属性类型是我们的数据类的简单列表(数组)。

class PartAdapter (val partItemList: List<PartData>, val clickListener: (PartData) -> Unit) :
        RecyclerView.Adapter<RecyclerView.ViewHolder>() {
复制代码

RecyclerView的适配器实现

Android Studio 已经创建了方法存根。我们只需编写几行实现。此代码根据我们的要求定制适配器,并完成我所描述的3个主要任务。

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    // LayoutInflater: takes ID from layout defined in XML.
    // Instantiates the layout XML into corresponding View objects.
    // Use context from main app -> also supplies theme layout values!
    val inflater = LayoutInflater.from(parent.context)
    // Inflate XML. Last parameter: don't immediately attach new view to the parent view group
    val view = inflater.inflate(R.layout.part_list_item, parent, false)
    return PartViewHolder(view)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    // Populate ViewHolder with data that corresponds to the position in the list
    // which we are told to load
    (holder as PartViewHolder).bind(partItemList[position])
}

override fun getItemCount() = partItemList.size
复制代码
  • onCreateViewHolder():扩展视图项的布局XML。inflate() 方法的最后一个参数 确保新视图不会立即附加到父视图组。相反,它只在缓存中,不应该是可见的。
  • onBindViewHolder():在 RecyclerView 提供所有必要的信息-在 ViewHolder 实例应显示的数据,以及在数据列表中的(新)位置。使用我们的 Adapter 类的 partItemList 属性,我们调用我们添加到 ViewHolder 的自定义 bind()函数。
  • getItemCount():在我们的例子中简单 - 列表/数组的大小。在 Kotlin中,我们可以进一步将其简化为内联实现; 如果我们直接返回一个值/一个简单的表达式,我们不需要函数的 {} 和 return 语句。

RecyclerViews的活动布局

我们差不多完成了。接下来,我们创建 RecyclerView。第一步是将其添加到 MainActivity 的 XML 布局定义中。

如果您使用 Android Studio 的新项目向导并创建了一个空活动,则会添加一个 “Hello World” TextView。删除它。

而是为具有ID “rv_parts” 的滚动全屏 RecyclerView 列表添加以下定义:

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_parts"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
复制代码

实例化RecyclerView

在MainActivity的 onCreate() 中只需要三行代码 。

  1. 分配LayoutManager:它测量和定位项目视图。不同的布局管理器支持各种布局。我们选择最常见的:LinearLayoutManager。默认情况下,它会创建垂直滚动列表布局。
rv_parts.layoutManager = LinearLayoutManager(this)
复制代码
  1. 优化:所述 RecyclerView 的大小是不会被影响适配器内容。RecyclerView 的大小仅取决于父级(在我们的示例中,全屏可用)。 因此,我们可以调用 setHasFixedSize() 来激活 RecyclerView 中的一些优化。在 Kotlin 中,您不需要 “set”-prefix 来访问属性。
rv_parts.hasFixedSize()
复制代码
  1. 分配适配器:这最终连接所有内容:我们创建并分配我们编写的Adapter类的新实例(“PartAdapter”)。该适配器需要的数据项,这是我们在本文开头创建的列表。
rv_parts.adapter = PartAdapter(testData)
复制代码

RecyclerView:工作!

现在按下播放,您的RecyclerView应该可以正常工作!使用我们提供的三个示例数据项,列表有点太短,无法完全理解滚动。要测试它,只需使用一些额外的项目扩展列表。

现在您已经掌握了基础知识,可以很容易地扩展项目布局和数据类以及其他信息。

如果某些内容不适合您,请将您的代码与 GitHub 上的完成解决方案进行比较。请注意,解决方案比我们在此阶段更先进 - 例如,它已经包含单击处理程序。

下一步:单击“处理程序”

缺少一个经常使用的部分:点击处理程序。与 ListView 相比,这些在 RecyclerViews 中要复杂得多。但是,如果做得好,他们很容易添加 Kotlin。这只是了解一些更先进的 Kotlin 概念的问题。我们将在下一部分看一下这个!

欢迎关注 Kotlin 中文社区!

中文官网:www.kotlincn.net/

中文官方博客:www.kotliner.cn/

公众号:Kotlin

知乎专栏:Kotlin

CSDN:Kotlin中文社区

掘金:Kotlin中文社区

简书:Kotlin中文社区

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[译]Android 中 Kotlin 与 RecyclerView 高性能列表 的相关文章

  • 按键时关闭 ModalWindow

    我希望能够在用户按下某个键 在我的例子中是 ESC 时关闭 ModalWindow 我有一个用于按键的 Javascript 侦听器 它调用取消按钮 ID 的单击事件 jQuery modalWindowInfo closeButtonId
  • Java中有没有一种方法可以通过名称实例化一个类?

    我正在寻找问题 从字符串名称实例化一个类 https stackoverflow com questions 9854900 instantiate an class from its string name它描述了如何在有名称的情况下实例
  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 序列的排列?

    我有具体数量的数字 现在我想以某种方式显示这个序列的所有可能的排列 例如 如果数字数量为3 我想显示 0 0 0 0 0 1 0 0 2 0 1 0 0 1 1 0 1 2 0 2 0 0 2 1 0 2 2 1 0 0 1 0 1 1 0
  • Junit:如何测试从属性文件读取属性的方法

    嗨 我有课ReadProperty其中有一个方法ReadPropertyFile返回类型的Myclass从属性文件读取参数值并返回Myclass目的 我需要帮助来测试ReadPropertyFile方法与JUnit 如果可能的话使用模拟文件
  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • 如何更改javaFX中按钮的图像?

    我正在使用javaFX 我制作了一个按钮并为此设置了图像 代码是 Image playI new Image file c Users Farhad Desktop icons play2 jpg ImageView iv1 new Ima
  • 谷歌应用程序引擎会话

    什么是java应用程序引擎 默认会话超时 如果我们将会话超时设置为非常非常长的时间 会不会产生不良影响 因为谷歌应用程序引擎会话默认情况下仅存储在数据存储中 就像facebook一样 每次访问该页面时 会话仍然永远存在 默认会话超时设置为
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • volatile、final 和synchronized 安全发布的区别

    给定一个带有变量 x 的 A 类 变量 x 在类构造函数中设置 A x 77 我们想将 x 发布到其他线程 考虑以下 3 种变量 x 线程安全 发布的情况 1 x is final 2 x is volatile 3 x 设定为同步块 sy
  • 如何在用户输入数据后重新运行java代码

    嘿 我有一个基本的java 应用程序 显示人们是成年人还是青少年等 我从java开始 在用户输入年龄和字符串后我找不到如何制作它它们被归类为 我希望它重新运行整个过程 以便其他人可以尝试 的节目 我一直在考虑做一个循环 但这对我来说没有用
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • Cucumber 0.4.3 (cuke4duke) 与 java + maven gem 问题

    我最近开始为 Cucumber 安装一个示例项目 并尝试使用 maven java 运行它 我遵循了这个指南 http www goodercode com wp using cucumber tests with maven and ja
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • 使用 CXF-RS 组件时,为什么我们使用 而不是普通的

    作为后续这个问题 https stackoverflow com questions 20598199 对于如何正确使用CXF RS组件我还是有点困惑 我很困惑为什么我们需要
  • Spring Boot 无法更新 azure cosmos db(MongoDb) 上的分片集合

    我的数据库中存在一个集合 documentDev 其分片键为 dNumber 样本文件 id 12831221wadaee23 dNumber 115 processed false 如果我尝试使用以下命令通过任何查询工具更新此文档 db

随机推荐

  • vector::erase() vector中如何删除元素?

    vector erase 从指定容器删除指定位置的元素或某段范围内的元素 vector erase 方法有两种重载形式 如下 1 iterator erase iterator Where v1 erase v1 begin 删除v1中的第
  • MongDB解决Authentication Failed导致的不能连接问题

    MongoDB的安装 Step1 打开MongoDB产品下载页面https www mongodb com download center jmp nav community 选择Windows Server 2008 R2 64 bit
  • linux标准库unistd.h

    unistd h是unix std的意思 是POSIX标准定义的unix类系统定义符号常量的头文件 包含了许多UNIX系统服务的函数原型 unistd h在unix中类似于window中的windows h ifdef WIN32 incl
  • 微信小程序秀才成语接龙趣味答题小游戏带流量主无授权源码

    介绍 流量主带 横屏广告 视频广告 插屏广告 激励广告 趣味性很强 会推广运营肯定能赚一笔 这套源码也是淘宝买的 网盘下载地址 http kekewangLuo cc j27EjMGhsPL0 图片
  • 微信小程序 Spdier - OfferShow 反编译逆向(一)

    微信小程序 Spdier OfferShow 反编译逆向 一 文章目录 微信小程序 Spdier OfferShow 反编译逆向 一 前言 一 任务说明 1 尝试反编译分析出js code参数的生成方式 用来获取token 2 将小程序搜索
  • Flask 打包 PostgreSQL/PyTorch 的图像推荐系统在 Heroku 云平台上运行

    前文 推荐图像理论简述 PyTorch 推荐相似图像实现 图像推荐 Flask App 步骤 我已经为这个项目设定并满足了以下要求 该网络应用程序可在互联网上访问 该应用程序有一个带分页的图像库 仅加载页面的相关数据 图库和推荐的数据是从数
  • redis常用数据类型的场景,你真的用对了么?

    关注微信公众号 虾米聊吧 每天更新一篇技术文章 文章内容涵盖架构师成长必经之路应掌握的技术 一起学习 一起交流 redis常用数据类型的场景 你真的用对了么 redis常用数据类型包含string hashmap list set sort
  • 使用Transformers离线模型(以bert模型为例)

    首先需要安装transformers pip install transformers 以bert base uncased为例 进入网站 https huggingface co bert base uncased tree main 可
  • qt/e在arm板上显示中文的方法

    QTCN社区 gt Qt嵌入式开发 gt 求qt e在arm板上显示中文的方法 打印本页 登录 注册 回复主题 发表主题 newalan 2008 04 15 00 38 求qt e在arm板上显示中文的方法 求qt e在arm板上显示中文
  • mysql 高效复制存储数据到另一张表

    最近有个需求 我们老大说让我写个存储过程把一张表的数据分组之后按时间取最近的三条数据 复制到另一张表中 刚开始他也没有说这么明白 我就用存储过程写了 写完之后他才说每天取最近三条数据 我一想 这根本用不到存储过程 直接两条SQL搞定如下 d
  • windows server 2008 R2使用nginx代理https访问,外网访问失败

    windows server 2008 R2使用nginx代理https访问 外网访问失败 问题 因为公司固定了ip 在公司服务器Windows server 2008 R2部署项目后 使用nginx代理了https 出现 使用公司的网络
  • 无需Root 手机装电脑系统 虚拟机

    不需要ROOT 安装也有简单方法 比较适合小白 也有大神操作方法 特别说下这不是云电脑 不需要网都可以跑起来 下面具体看下面操作 来自网上教程 非本站原创 我们需要一个软件 windows模拟器 链接 https pan baidu com
  • vue把el-dialog提取出来作为子组件,并实现父组件和子组件相互传值

    最近做项目遇到了一个需求是 为了防止组件中代码冗长 需要将el dialog对话弹出框单独封装成子组件 并将父组件中的id传递给子组件 子组件再根据id刷新父组件 具体项目代码太长 这里仅记录一下实现思路 01 把el dialog提取出来
  • 初识kafka---Kafka从入门到精通(一)

    Kafka中的消息是以topic进行分类的 生产者生产消息 消费者消费消息都是面向topic 而每个topic里面都是有分区的概念 一个topic有三个分区 而每个分区都是有分区leader partition leader 和partit
  • http协议详解

    本篇博文主要介绍HTTP请求 响应的系列过程 包括四个部分 是在陆续学习中觉着之间有关联总结下来的 以便自己今后忘记后可以快速查看也为各位看到这篇文章的朋友们梳理一下知识 下面 正文开始 作为一个前端开发人员 我们每天都在与页面打交道 那么
  • 源码编译安装httpd-2.4.39

    环境 apr 1 7 0 tar bz2 apr util 1 6 1 tar bz2 httpd 2 4 39 tar bz2 1 合并源码文件 tar xvf apr 1 7 0 tar bz2 tar xvf apr util 1 6
  • 2020-12-13 51单片机笔记

    单片机型号选择错误 该STC软件左上角的单片机型号 显示不出来 LCD1602显示屏插反 extern uchar time 报错没定义 在其他文件运用时也要加上uchar time 不能直接使用 下图是错误案例 在 c文件定义变量 然后在
  • 华为eNSP配置PPP协议中的CHAP认证

    CHAP认证 CHAP认证过程比较复杂 三次握手机制 使用密文格式发送CHAP认证信息 由认证方发起CHAP认证 有效避免暴力破解 在链路建立成功后具有再次认证检测机制 目前在企业网的远程接入环境中用的比较常见 两个路由器都要加模块 路由器
  • 把照片改成指定像素基于PS(证件照修改为制定像素大小)

    下图所示的图片当前宽度为1064像素 高度为1972像素 右键图片打开 属性 对话框可查看尺寸 接下来我们想将图片尺寸改为宽度1000像素 高度1000像素 同时图片不能变形 操作步骤 1 打开PS软件 按Ctrl N组合键打开 新建文档
  • [译]Android 中 Kotlin 与 RecyclerView 高性能列表

    翻译说明 原标题 Kotlin RecyclerView for High Performance Lists in Android 原文地址 www andreasjakl com 原文作者 Andreas Jakl RecyclerVi