一、什么是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,
ViewHolder
是RecyclerView
的一个静态内部类,用于缓存列表项视图中的子视图引用。通过ViewHolder
,RecyclerView
可以避免重复查找视图 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.实现结果