首页 / Android 入门教程 / RecyclerView

Android 入门教程

RecyclerView

本教程共 100 篇 · 第 60 篇 · 更新于 2026-07-28 · 约 9 分钟阅读

AndroidAndroid 入门教程RecyclerViewAdapterViewHolderLayoutManager

60. RecyclerView

本节目标:掌握 RecyclerView 的三大组成部分(Adapter、ViewHolder、LayoutManager),能从零实现一个可滚动的列表,理解视图复用原理。

为什么用 RecyclerView

传统 ListView 性能差、不支持网格、动画难做。RecyclerView 是它的升级版,通过复用 ViewHolder大幅提升性能,还支持横向滑动、网格、瀑布流等多种布局。

打个比方:ListView 像一次性筷子,每行都造一个新的;RecyclerView 像可重复使用的碗,滚出去的碗洗干净给新行用。

三大组成部分

RecyclerView 需要三个角色配合:

  1. Adapter(适配器):把数据绑定到视图上。
  2. ViewHolder(视图持有者):缓存每行的控件引用,避免重复 findViewById
  3. LayoutManager(布局管理器):决定每行怎么排列(线性、网格、瀑布流)。

添加依赖

dependencies {
    implementation("androidx.recyclerview:recyclerview:1.3.2")
}

完整实现

第一步:数据类

data class Contact(val id: Int, val name: String, val phone: String)

第二步:列表项布局

res/layout/item_contact.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/tvName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="16sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tvPhone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#666666" />

</LinearLayout>

第三步:ViewHolder

class ContactViewHolder(
    private val binding: ItemContactBinding,
    private val onItemClick: (Contact) -> Unit
) : RecyclerView.ViewHolder(binding.root) {

    fun bind(contact: Contact) {
        binding.tvName.text = contact.name
        binding.tvPhone.text = contact.phone
        binding.root.setOnClickListener { onItemClick(contact) }
    }
}

第四步:Adapter

class ContactAdapter(
    private val contacts: List<Contact>,
    private val onItemClick: (Contact) -> Unit
) : RecyclerView.Adapter<ContactViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder {
        val binding = ItemContactBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ContactViewHolder(binding, onItemClick)
    }

    override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
        holder.bind(contacts[position])
    }

    override fun getItemCount(): Int = contacts.size
}

第五步:使用

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val contacts = listOf(
            Contact(1, "张三", "13800001111"),
            Contact(2, "李四", "13800002222"),
            Contact(3, "王五", "13800003333")
        )

        binding.recyclerView.apply {
            layoutManager = LinearLayoutManager(this@MainActivity)
            adapter = ContactAdapter(contacts) { contact ->
                Toast.makeText(this@MainActivity, contact.name, Toast.LENGTH_SHORT).show()
            }
        }
    }
}

XML 里加 RecyclerView:

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

LayoutManager 布局管理器

LayoutManager效果
LinearLayoutManager线性列表(竖直/水平)
GridLayoutManager网格(指定列数)
StaggeredGridLayoutManager瀑布流(不等高)
// 网格,3 列
recyclerView.layoutManager = GridLayoutManager(this, 3)

// 水平列表
recyclerView.layoutManager = LinearLayoutManager(
    this, LinearLayoutManager.HORIZONTAL, false
)

// 瀑布流,2 列
recyclerView.layoutManager = StaggeredGridLayoutManager(
    2, StaggeredGridLayoutManager.VERTICAL
)

ItemDecoration 分隔线

class DividerDecoration(context: Context) : RecyclerView.ItemDecoration() {
    private val divider = ContextCompat.getDrawable(context, R.drawable.divider)

    override fun getItemOffsets(
        outRect: Rect, view: View,
        parent: RecyclerView, state: RecyclerView.State
    ) {
        outRect.bottom = 1  // 每项底部留 1px 间距
    }

    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val left = parent.paddingLeft
        val right = parent.width - parent.paddingRight
        for (i in 0 until parent.childCount) {
            val child = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            val top = child.bottom + params.bottomMargin
            val bottom = top + divider?.intrinsicHeight ?: 0
            divider?.setBounds(left, top, right, bottom)
            divider?.draw(canvas)
        }
    }
}
recyclerView.addItemDecoration(DividerDecoration(this))
Tip

不想手写可以用 DividerItemDecoration,它自带默认分隔线:

val decoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
recyclerView.addItemDecoration(decoration)

数据更新

直接改数据后调 notifyDataSetChanged() 会让整个列表重建,性能差且没有动画。用 ListAdapter + DiffUtil 更高效(下一章详讲)。

简单方式:

// 通知指定位置插入
adapter.notifyItemInserted(contacts.size - 1)
// 通知指定位置删除
adapter.notifyItemRemoved(position)
// 通知指定位置变化
adapter.notifyItemChanged(position)
// 通知范围变化
adapter.notifyItemRangeChanged(0, contacts.size)
Note
  • notifyItemXxx() 系列方法比 notifyDataSetChanged() 好得多:它们只更新变化的项,还能触发默认的增删动画。但你要自己维护 position,容易出错。下一章的 ListAdapter 自动处理这些。

setHasFixedSize

如果列表大小不随内容变化(固定高度的 RecyclerView),设这个标志能优化性能:

recyclerView.setHasFixedSize(true)
Warning
  • setHasFixedSize(true) 只在 RecyclerView 的高度不依赖内容时用。如果列表内容会改变 RecyclerView 自身的高度(比如嵌在 ScrollView 里),别设 true。

小结

RecyclerView 是传统 View 体系的高性能列表方案。三大组成:Adapter 绑数据、ViewHolder 缓存控件、LayoutManager 排列布局。用 notifyItemXxx() 精准更新而非 notifyDataSetChanged()。在 Compose 项目里,列表直接用 LazyColumn,但理解 RecyclerView 原理对维护老代码和用 AndroidView 嵌入有帮助。下一节学习 RecyclerView 进阶。