RecyclerView详解——(一)基本使用

一、什么是RecycleView?

官方注释:

RecyclerView 可以让您轻松高效地显示大量数据。您提供数据并定义每个列表项的外观,而 RecyclerView 库会根据需要动态创建元素。

顾名思义,RecyclerView 会回收这些单个的元素。当列表项滚动出屏幕时,RecyclerView 不会销毁其视图。相反,RecyclerView 会对屏幕上滚动的新列表项重用该视图。RecyclerView 可提升性能和应用的响应速度,并降低功耗。

二、关键类

  • RecyclerView是包含与您的数据对应的视图的ViewGroup ViewGroup。它本身就是视图,因此向布局添加 RecyclerView 的方式与添加任何其他界面元素的方式相同。

  • 列表中的每个独立元素都由一个 ViewHolder 对象进行定义。创建 ViewHolder 时,它并没有任何关联的数据。创建 ViewHolder 后,RecyclerView 会将其绑定到其数据。您可以通过扩展 RecyclerView.ViewHolder 来定义 ViewHolder。

  • RecyclerView 会请求视图,并通过在 adapter 中调用方法,将视图绑定到其数据。您可以通过扩展 RecyclerView.Adapter 来定义 Adapter。

  • LayoutManager 负责排列列表中的各个元素。您可以使用 RecyclerView 库提供的某个布局管理器,也可以定义自己的布局管理器。

三、基本使用

1.实现xml布局

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="LinearLayoutManager"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:contentDescription="@string/fab_content_description"
        android:src="@drawable/ic_add_black_24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 2.每个item展示的布局

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools">

    <ImageView
        android:id="@+id/flower_image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        tools:src="@drawable/rose"
        android:layout_margin="8dp"
        android:contentDescription="@string/flower_image_content_description"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/flower_text"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/flower_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:text="@string/flower1_name"
        android:textAppearance="?attr/textAppearanceHeadline5"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/flower_image"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.自定义Adapter

RecyclerView.Adapter 是一个抽象类,用于管理和绑定数据到 RecyclerView 中的视图项。它的主要职责是创建 ViewHolder 对象(onCreateViewHolder),将数据绑定到这些视图项(onBindViewHolder),并提供列表项的数量(getItemCount)。通过这些方法,RecyclerView.Adapter 支持高效的、可重用的列表项展示。

首先是数据实体类

data class Flower(
    val id: Long,
    val name: String,
    @DrawableRes
    val image: Int?,
    val description: String
)

用于渲染item子项UI和属性等

然后是Adapter 

class FlowersAdapter(private val onClick: (Flower) -> Unit) :
    ListAdapter<Flower, FlowersAdapter.FlowerViewHolder>(FlowerDiffCallback) {

    /* ViewHolder for Flower, takes in the inflated view and the onClick behavior. */
    class FlowerViewHolder(itemView: View, val onClick: (Flower) -> Unit) :
        RecyclerView.ViewHolder(itemView) {
        private val flowerTextView: TextView = itemView.findViewById(R.id.flower_text)
        private val flowerImageView: ImageView = itemView.findViewById(R.id.flower_image)
        private var currentFlower: Flower? = null
        init {
            itemView.setOnClickListener {
                currentFlower?.let {
                    onClick(it)
                }
            }
        }
        /* Bind flower name and image. */
        fun bind(flower: Flower) {
            currentFlower = flower
            flowerTextView.text = flower.name
            if (flower.image != null) {
                flowerImageView.setImageResource(flower.image)
            } else {
                flowerImageView.setImageResource(R.drawable.rose)
            }
        }
    }
    /* Creates and inflates view and return FlowerViewHolder. */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FlowerViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.flower_item, parent, false)
        return FlowerViewHolder(view, onClick)
    }
    /* Gets current flower and uses it to bind view. */
    override fun onBindViewHolder(holder: FlowerViewHolder, position: Int) {
        val flower = getItem(position)
        holder.bind(flower)

    }
}
//areItemsTheSame:用于判断两个数据项是否表示相同的对象。在这个例子中,
//oldItem == newItem 判断 Flower 对象是否完全相同。
//如果相同,RecyclerView 将认为这是同一个项目,避免刷新。

//areContentsTheSame:用于判断两个对象的内容是否相同。
//这里通过比较 Flower 对象的 id 字段来实现。
//如果内容相同,RecyclerView 将不更新该视图,因为内容没有变化。
object FlowerDiffCallback : DiffUtil.ItemCallback<Flower>() {
    override fun areItemsTheSame(oldItem: Flower, newItem: Flower): Boolean {
        return oldItem == newItem
    }

    override fun areContentsTheSame(oldItem: Flower, newItem: Flower): Boolean {
        return oldItem.id == newItem.id
    }
}

tips:

1.上面使用的是ListAdapter,是基于 RecyclerView.Adapter,并通过 AsyncListDiffer来实现列表项的增量更新,从而提高性能。如果一个列表很简单,且并不会经常有刷新等操作,也可以直接用RecyclerView.Adapter。

 2.上面的ListAdapter<Flower, FlowersAdapter.FlowerViewHolder>(FlowerDiffCallback),其中, 

  • Flower参数代表其渲染的实体类;

  • 参数ViewHolder,ViewHolderRecyclerView 的一个静态内部类,用于缓存列表项视图中的子视图引用。通过 ViewHolderRecyclerView 可以避免重复查找视图 ID,提高性能。每个列表项都拥有一个 ViewHolder 对象,用来存储视图状态和数据;

  • FlowerDiffCallback是listadpter构造函数里的:protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback),DiffUtil.ItemCallback,用于定义列表项的差异计算逻辑。通过这个回调,ListAdapter 可以根据新旧数据集的差异来计算最小的更改量,而不是完全刷新整个列表,从而提高性能。

3.可以发现对于item的setOnClickListener监听,放在了FlowerViewHolder的init里面,为啥不是放在onBindViewHolder里面,因为在recyclerView的缓存和刷新机制里,会经常调用onBindViewHolder,所以会多次进行setOnClickListener这样其实是非常不好的。

4.列表绑定adapter

 private val flowersListViewModel by viewModels<FlowersListViewModel> {
        FlowersListViewModelFactory(this)
    }
override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //创建一个adapter
        val flowersAdapter = FlowersAdapter { flower -> adapterOnClick(flower) }
        //绑定adapter
        val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView.adapter = concatAdapter
        //数据变化后,调用submitList
        //submitList:提交要比较并显示的新列表。如果已经显示了一个列表,
        //那么将在后台线程上计算一个 diff,这将调度 Adapter。在主线程更新item
        flowersListViewModel.flowersLiveData.observe(this) {
            it?.let {
                flowersAdapter.submitList(it as MutableList<Flower>)
                headerAdapter.updateFlowerCount(it.size)
            }
        }

}

tips:

1.因为我在xml里设置了app:layoutManager="LinearLayoutManager"列表的layoutManager,如果xml里没有设置,记得在代码里进行设置。

2.flowersListViewModel.flowersLiveData其实就是造了个假数据,然后postvalue触发监听进行赋值。

5.基于RecyclerView.Adapter基本实现

class ItemAdapter(private val itemList: List<Item>) : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

    // ViewHolder 用于保存每个 item 的视图
    class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val nameTextView: TextView = itemView.findViewById(R.id.itemNameTextView)
    }

    // 创建 ViewHolder 并返回它
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return ItemViewHolder(view)
    }

    // 绑定数据到 ViewHolder
    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val item = itemList[position]
        holder.nameTextView.text = item.name
    }

    // 返回列表的大小
    override fun getItemCount(): Int = itemList.size
// 刷新数据方法
    fun refreshData(itemListData: List<Item>) {
        itemList.clear()
        itemList.addAll(itemListData)
        notifyDataSetChanged()
    }

    // 插入数据方法
    fun addData(tem: Item) {
        itemList.add(tem)
        notifyItemInserted(itemList.size - 1)
    }

    // 删除数据方法
    fun removeItem(position: Int) {
        if (position in itemList.indices) {
            itemList.removeAt(position)
            notifyItemRemoved(position)
        }
    }
}

在 Activity 的 onCreate 方法中初始化 RecyclerView 并设置 Adapter

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 准备数据
        val items = listOf(
            Item(1, "Item 1"),
            Item(2, "Item 2"),
            Item(3, "Item 3"),
            Item(4, "Item 4")
        )

        // 初始化 RecyclerView
        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this) // 设置布局管理器
        recyclerView.adapter = ItemAdapter(items) // 设置适配器
    }
}

6.实现结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&岁月不待人&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值