一、什么是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布局
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"/>
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"/>
2.每个item展示的布局
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">
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" />
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" />
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(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() {
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
ViewHolder
是
RecyclerView
的一个静态内部类,用于缓存列表项视图中的子视图引用。
通过
ViewHolder
,
RecyclerView
可以避免重复查找视图 ID,提高性能。
每个列表项都拥有一个
ViewHolder
对象,用来存储视图状态和数据;
FlowerDiffCallback是listadpter构造函数里的:protected ListAdapter(@NonNull DiffUtil.ItemCallback
DiffUtil.ItemCallback
,用于定义列表项的差异计算逻辑。
通过这个回调,
ListAdapter
可以根据新旧数据集的差异来计算最小的更改量,而不是完全刷新整个列表,从而提高性能。
3.可以发现对于item的setOnClickListener监听,放在了FlowerViewHolder的init里面,为啥不是放在onBindViewHolder里面,因为在recyclerView的缓存和刷新机制里,会经常调用onBindViewHolder,所以会多次进行setOnClickListener这样其实是非常不好的。
4.列表绑定adapter
private val flowersListViewModel by viewModels {
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)
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- ) : RecyclerView.Adapter
() {
// 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- ) {
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.实现结果
