IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> RecycleView的基本使用 -> 正文阅读

[移动开发]RecycleView的基本使用

前言

为了解决 ListView 存在的拓展性差、需要手动优化性能等问题,Android 提供了滚动组件 RecycleView。本篇博客用于梳理 RecycleView 的使用方法

RecycleView 的优点

  1. RecycleView 仅会处理当前现实在屏幕上的项。假如列表中有 1000个元素,而页面只显示其中 10 个,那么 RecycleView 仅处理这 10 个项
  2. 当某个项滚出屏幕时,RecycleView 会回收其视图。这个项被回收,用于填充新进入屏幕的内容。
  3. 当某一项发生变化时,仅重新绘制变化的那一项。

使用方法

添加依赖

新建一个 UiWidgetTest 文件

在 build.gradle (Moudule) 的 dependencies 加入以下内容,最新的版本可在 Recyclerview | Android Developers 中查看。

implementation("androidx.recyclerview:recyclerview:1.2.1")
// For control over item selection of both touch and mouse driven selection
implementation("androidx.recyclerview:recyclerview-selection:1.1.0")

加入代码后,点击 Sync Now 重新同步 gradle。

image-20220513195831301

定制 RecycleView 界面

修改 activity_main.xml 中的代码。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycleView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

在 layout 文件夹下新建一个 girl_item.xml 文件,这个文件用于展示 RecycleView 中的每个数据项。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <ImageView
        android:id="@+id/girlImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp" />

    <TextView
        android:id="@+id/fruitName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp" />

</LinearLayout>

这个子布局很简单,就是一张图片加一个标签

准备图片资源

准备一些图片资源,放入 res -> drawable 文件夹下

image-20220513203128085

创建实体类

RecycleView 中每一个 item 中的元素都来自于一个对象,即RecycleView 中的数据来自一个集合。

新建一个 Girl 类,代码如下所示。

package com.example.uiwidgettest

class Girl(val name: String,val imageId:Int)

imageId 为 Int 型,存储对应图片的资源 id。

适配器 Adapter

前面说到 RecycleView 中的数据来源是 List<Object>,但是数组并不能直接应用到 RecycleView 中。

假设数组是传统的 mirco usb 数据线,RecycleView 是一个 type-C 类型的手机。

Micro usb 数据线是无法在 type-C 类型手机上使用,所以需要一个转接头,把 mirco-usb 转成 type-C

Adapter 翻译成适配器,用于把 List 集合适配成 RecycleView 的可用类型。

新建一个 GirlAdapter 类 ,让它继承 RecycleView.Adapter 类,将范型制定为 GirlAdapter.ViewHolder

package com.example.uiwidgettest

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class GirlAdapter(private val girls: List<Girl>) : RecyclerView.Adapter<GirlAdapter.ViewHolder>() {

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val girlName: TextView = view.findViewById(R.id.girlName)
        val girlImage: ImageView = view.findViewById(R.id.girlImage)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.girl_item, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val girl = girls[position]
        holder.girlName.text = girl.name
        holder.girlImage.setImageResource(girl.imageId)
    }

    override fun getItemCount() = girls.size
}

代码解析

  • GirlAdapter 参数为 Girl 集合,GirlAdapter 将此集合转化为 RecycleView 的可用类型
  • ViewHolder 翻译为 View 持有者,它用于描述一个子 View 中的数据,以及其在 RecycleView 的位置信息
  • 内部类 ViewHolder继承自 RecycleView.ViewHolder()。ViewHolder 需要一个非空的 View 对象作为参数
  • RecycleView.Adapter 是一个抽象类,继承该类需要实现 onCreateViewHolder(),onBindViewHolder() 和 getItemCount() 方法
  • onCreateViewHolder() 用于创建 ViewHolder,参数 view 是 子项 XML 对应的类。用布局加载器将 girl_item 加载成一个 View 类
  • onBindViewHolder() 用于对 RecycleView 的子项赋值,它在每个子项滚动到屏幕中时执行。根据 position 获取到对象实例,将数据设置到 ViewHolder 的元素中。
  • getItemCount() 用于告诉 RecycleView 一共有多少个子项。

使用 RecycleView

修改 MainActivity 中的代码

package com.example.uiwidgettest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.uiwidgettest.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private val girls = ArrayList<Girl>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initGirls()
        val layoutManager = LinearLayoutManager(this)
        binding.recycleView.layoutManager = layoutManager
        val girlAdapter = GirlAdapter(girls)
        binding.recycleView.adapter = girlAdapter
    }

    private fun initGirls() {
        repeat(3) {
            girls.add(Girl("Girl1", R.drawable.girl1))
            girls.add(Girl("Girl2", R.drawable.girl2))
            girls.add(Girl("Girl3", R.drawable.girl3))
            girls.add(Girl("Girl4", R.drawable.girl4))
            girls.add(Girl("Girl5", R.drawable.girl5))
        }
    }
}

代码中,先初始化了一个 girls 集合,然后在 onCreate() 方法中创建了一个 LinearLayoutManager对象,把它设置到 RecycleView 中。

layoutManager 用于设置 RecycleView 的布局方,LinearLayoutManager 表示线性布局。

创建一个 GirlAdapter 实例,传入 girls 集合到 GirlAdapter 的构造器中。

最后调用 RecycleView 的 setAdapter() 方法来完成适配器设置。

至此,运行 app 就能看到 RecycleView 的运行效果。

实现横向滚动和瀑布流布局

相比于 ListView ,RecycleView 的另一个优点就是它能很容易的实现横向滚动及修改布局方式。

这得益于 LayoutManager ,通过对 LayoutManager 的设置就能轻松修改 RecycleView 的布局和滚动方向。

实现横向滚动

修改 girl_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="80dp"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/girlImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp" />

    <TextView
        android:id="@+id/girlName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp" />

</LinearLayout>

把 LinearLayout 改为垂直方向,宽度改为 80 dp,高度改为 “wrap_content”。

因为这里实现的是横向滚动,所以最好让他们的宽度保持一致,这样看起来比较美观。

两个子 View 的 layout_gravity 改为 “center_horizontal”。

在 MainActivity 中设置 layoutManager 的排列方向

layoutManager.orientation = LinearLayoutManager.HORIZONTAL

实现瀑布流布局

瀑布流布局,是一种多行等宽不等高元素实现的参差不齐的排列,如下图所示:

瀑布流布局

首先修改子项布局文件 girl_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp">

    <ImageView
        android:id="@+id/girlImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="10dp" />

    <TextView
        android:id="@+id/girlName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp" />

</LinearLayout>

代码里做了以下几项更改:

  1. LinearLayout 的宽度由 80 dp 改为 match_parent 。因为瀑布流的宽度由布局的列数来自动适配,而不是一个固定值。
  2. 给 LinearLayout 设置了 5 dp 的外边距,使得每个子项之间看起来不会那么拥挤。
  3. 将 TextView 的对齐属性由居中改为居左。因为后面要通过改变文字的长度来使每个子元素不等高。
package com.example.uiwidgettest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.example.uiwidgettest.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private val girls = ArrayList<Girl>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initGirls()
        val layoutManager = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)
        binding.recycleView.layoutManager = layoutManager
        val girlAdapter = GirlAdapter(girls)
        binding.recycleView.adapter = girlAdapter
    }

    private fun initGirls() {
        repeat(5) {
            girls.add(Girl(getRandomName("Girl1"), R.drawable.girl1))
            girls.add(Girl(getRandomName("Girl2"), R.drawable.girl2))
            girls.add(Girl(getRandomName("Girl3"), R.drawable.girl3))
            girls.add(Girl(getRandomName("Girl4"), R.drawable.girl4))
            girls.add(Girl(getRandomName("Girl5"), R.drawable.girl5))
        }
    }

    private fun getRandomName(name: String): String {
        val n = (1..20).random()
        val builder = StringBuilder()
        repeat(n){
                builder.append(name)
        }
        return builder.toString()
    }
}

首先将 layoutManager 的布局方式由线性布局改为瀑布流布局

val layoutManager = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)

StaggeredGridLayoutManager 的第一个参数用于指定布局的列数,第二个参数用于指定布局的排列方向

其次,为了使不同元素拥有不同的高度,添加了一个 getRandomName() 方法。

private fun getRandomName(name: String): String {
        val n = (1..20).random() 	// 通过随机数的方式拓展名字的长度
        val builder = StringBuilder()
        repeat(n){ // 将原来的名字重复 n 次
                builder.append(name)
        }
        return builder.toString()
    }

RecycleView 的点击事件

修改适配器类 GirlAdapter 的代码

package com.example.uiwidgettest

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView

class GirlAdapter(private val girls: List<Girl>) : RecyclerView.Adapter<GirlAdapter.ViewHolder>() {

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val girlName: TextView = view.findViewById(R.id.girlName)
        val girlImage: ImageView = view.findViewById(R.id.girlImage)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.girl_item, parent, false)
        val viewHolder = ViewHolder(view)
        viewHolder.girlName.setOnClickListener{
            val position = viewHolder.adapterPosition
            val girl = girls[position]
            Toast.makeText(parent.context,"你点击了文字 ${girl.name}$",Toast.LENGTH_SHORT).show()
        }
        viewHolder.girlImage.setOnClickListener{
            val position = viewHolder.adapterPosition
            val girl = girls[position]
            Toast.makeText(parent.context,"你点击了图片 ${girl.name}$",Toast.LENGTH_SHORT).show()
        }
        return viewHolder
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val girl = girls[position]
        holder.girlName.text = girl.name
        holder.girlImage.setImageResource(girl.imageId)
    }

    override fun getItemCount() = girls.size
}

在 onCreateViewHolder()方法中,对 viewholder 的子 view 注册点击事件。

viewholder.adapterPosition 用于获取该子项的位置,根据该位置就能操作数组中的对象

然而,该方法已经被废弃。因为当 Adapter 存在嵌套时,调用此方法会引发歧义

Google 提出了两个新方法 getBindingAdapterPosition() 和 getAbsoluteAdapterPosition() 来解决可能存在的

Adapter 嵌套混淆问题。

// 如果你希望访问的是 Adapter 中内容的位置 ,使用 bindingAdapterPosition
val position = viewHolder.bindingAdapterPosition
// 如果你希望访问的是 适配器相对于 RecycleView 的位置,使用 bindingAdapterPosition
val position = viewHolder.absoluteAdapterPosition
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 19:06:35  更:2022-05-21 19:07:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 1:42:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码