RecyclerView #

一、RecyclerView概述 #

RecyclerView是Android官方推荐的高性能列表控件,用于展示大量数据。相比ListView,RecyclerView更加灵活高效,支持多种布局方式。

1.1 RecyclerView的优势 #

  • 性能优化:强制使用ViewHolder模式
  • 布局灵活:支持多种LayoutManager
  • 动画支持:内置Item动画
  • 装饰器:支持ItemDecoration
  • 异步更新:支持DiffUtil

1.2 添加依赖 #

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

二、基本使用 #

2.1 布局文件 #

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

2.2 Item布局 #

xml
<!-- res/layout/item_user.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="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp">
    
    <TextView
        android:id="@+id/tvName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="16sp" />
    
    <TextView
        android:id="@+id/tvEmail"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="14sp" />
        
</LinearLayout>

2.3 数据类 #

kotlin
data class User(
    val id: Int,
    val name: String,
    val email: String
)

2.4 ViewHolder #

kotlin
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val tvName: TextView = view.findViewById(R.id.tvName)
    val tvEmail: TextView = view.findViewById(R.id.tvEmail)
}

2.5 Adapter #

kotlin
class UserAdapter(
    private val users: MutableList<User>,
    private val onItemClick: (User) -> Unit
) : RecyclerView.Adapter<UserViewHolder>() {
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val user = users[position]
        holder.tvName.text = user.name
        holder.tvEmail.text = user.email
        
        holder.itemView.setOnClickListener {
            onItemClick(user)
        }
    }
    
    override fun getItemCount(): Int = users.size
    
    fun updateData(newUsers: List<User>) {
        users.clear()
        users.addAll(newUsers)
        notifyDataSetChanged()
    }
}

2.6 使用RecyclerView #

kotlin
class MainActivity : AppCompatActivity() {
    
    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: UserAdapter
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        recyclerView = findViewById(R.id.recyclerView)
        
        adapter = UserAdapter(mutableListOf()) { user ->
            Toast.makeText(this, "点击: ${user.name}", Toast.LENGTH_SHORT).show()
        }
        
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)
        
        // 加载数据
        loadData()
    }
    
    private fun loadData() {
        val users = listOf(
            User(1, "张三", "zhangsan@example.com"),
            User(2, "李四", "lisi@example.com"),
            User(3, "王五", "wangwu@example.com")
        )
        adapter.updateData(users)
    }
}

三、LayoutManager #

LayoutManager负责RecyclerView的布局方式。

3.1 LinearLayoutManager #

线性布局,支持水平和垂直方向:

kotlin
// 垂直列表(默认)
recyclerView.layoutManager = LinearLayoutManager(this)

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

// 反向布局
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)

3.2 GridLayoutManager #

网格布局:

kotlin
// 3列网格
recyclerView.layoutManager = GridLayoutManager(this, 3)

// 水平网格
recyclerView.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.HORIZONTAL, false)

3.3 StaggeredGridLayoutManager #

瀑布流布局:

kotlin
// 垂直瀑布流
recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)

// 水平瀑布流
recyclerView.layoutManager = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.HORIZONTAL)

四、ItemDecoration #

ItemDecoration用于为Item添加装饰,如分割线。

4.1 添加分割线 #

kotlin
class DividerItemDecoration(
    context: Context,
    orientation: Int
) : RecyclerView.ItemDecoration() {
    
    private val divider: Drawable?
    private var orientation = orientation
    
    init {
        val a = context.obtainStyledAttributes(intArrayOf(android.R.attr.listDivider))
        divider = a.getDrawable(0)
        a.recycle()
    }
    
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        if (orientation == LinearLayoutManager.VERTICAL) {
            drawVertical(c, parent)
        } else {
            drawHorizontal(c, parent)
        }
    }
    
    private fun drawVertical(c: Canvas, parent: RecyclerView) {
        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(c)
        }
    }
    
    private fun drawHorizontal(c: Canvas, parent: RecyclerView) {
        // 水平分割线绘制
    }
}
kotlin
recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))

4.2 使用官方分割线 #

kotlin
recyclerView.addItemDecoration(
    DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
)

4.3 自定义间距 #

kotlin
class SpaceItemDecoration(private val space: Int) : RecyclerView.ItemDecoration() {
    
    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        outRect.left = space
        outRect.right = space
        outRect.bottom = space
        
        if (parent.getChildAdapterPosition(view) == 0) {
            outRect.top = space
        }
    }
}

recyclerView.addItemDecoration(SpaceItemDecoration(16))

五、Item动画 #

5.1 默认动画 #

kotlin
recyclerView.itemAnimator = DefaultItemAnimator()

5.2 添加和删除Item #

kotlin
class UserAdapter : RecyclerView.Adapter<UserViewHolder>() {
    
    fun addItem(user: User, position: Int) {
        users.add(position, user)
        notifyItemInserted(position)
    }
    
    fun removeItem(position: Int) {
        users.removeAt(position)
        notifyItemRemoved(position)
    }
    
    fun updateItem(user: User, position: Int) {
        users[position] = user
        notifyItemChanged(position)
    }
    
    fun moveItem(from: Int, to: Int) {
        val user = users.removeAt(from)
        users.add(to, user)
        notifyItemMoved(from, to)
    }
}

六、DiffUtil #

DiffUtil用于高效计算列表差异,避免全量刷新。

6.1 使用DiffUtil #

kotlin
class UserDiffCallback(
    private val oldList: List<User>,
    private val newList: List<User>
) : DiffUtil.Callback() {
    
    override fun getOldListSize(): Int = oldList.size
    
    override fun getNewListSize(): Int = newList.size
    
    override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos].id == newList[newPos].id
    }
    
    override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos] == newList[newPos]
    }
}

fun updateData(newUsers: List<User>) {
    val diffResult = DiffUtil.calculateDiff(UserDiffCallback(users, newUsers))
    users.clear()
    users.addAll(newUsers)
    diffResult.dispatchUpdatesTo(adapter)
}

6.2 使用ListAdapter #

ListAdapter内置了DiffUtil:

kotlin
class UserAdapter(
    private val onItemClick: (User) -> Unit
) : ListAdapter<User, UserViewHolder>(UserDiffCallback()) {
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val user = getItem(position)
        holder.bind(user)
    }
}

class UserDiffCallback : DiffUtil.ItemCallback<User>() {
    override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
        return oldItem.id == newItem.id
    }
    
    override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
        return oldItem == newItem
    }
}

// 使用
adapter.submitList(newUsers)

七、多类型Item #

7.1 实现多类型 #

kotlin
class MultiTypeAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    companion object {
        const val TYPE_HEADER = 0
        const val TYPE_ITEM = 1
    }
    
    private val items = mutableListOf<Any>()
    
    override fun getItemViewType(position: Int): Int {
        return when (items[position]) {
            is Header -> TYPE_HEADER
            is User -> TYPE_ITEM
            else -> TYPE_ITEM
        }
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            TYPE_HEADER -> {
                val view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.item_header, parent, false)
                HeaderViewHolder(view)
            }
            else -> {
                val view = LayoutInflater.from(parent.context)
                    .inflate(R.layout.item_user, parent, false)
                UserViewHolder(view)
            }
        }
    }
    
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is HeaderViewHolder -> holder.bind(items[position] as Header)
            is UserViewHolder -> holder.bind(items[position] as User)
        }
    }
    
    override fun getItemCount(): Int = items.size
}

八、点击事件 #

8.1 在Adapter中处理 #

kotlin
class UserAdapter(
    private val onItemClick: (User) -> Unit,
    private val onItemLongClick: (User) -> Boolean
) : RecyclerView.Adapter<UserViewHolder>() {
    
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val user = users[position]
        
        holder.itemView.setOnClickListener {
            onItemClick(user)
        }
        
        holder.itemView.setOnLongClickListener {
            onItemLongClick(user)
        }
    }
}

8.2 使用addOnItemTouchListener #

kotlin
recyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
    
    private val gestureDetector = GestureDetector(
        context,
        object : GestureDetector.SimpleOnGestureListener() {
            override fun onSingleTapUp(e: MotionEvent): Boolean {
                return true
            }
            
            override fun onLongPress(e: MotionEvent) {
                val child = recyclerView.findChildViewUnder(e.x, e.y)
                child?.let {
                    val position = recyclerView.getChildAdapterPosition(it)
                    // 长按事件
                }
            }
        }
    )
    
    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
        val child = rv.findChildViewUnder(e.x, e.y)
        if (child != null && gestureDetector.onTouchEvent(e)) {
            val position = rv.getChildAdapterPosition(child)
            // 点击事件
            return true
        }
        return false
    }
    
    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
    
    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})

九、总结 #

本章详细介绍了RecyclerView:

  1. RecyclerView的基本使用
  2. LayoutManager布局管理
  3. ItemDecoration装饰器
  4. Item动画效果
  5. DiffUtil高效更新
  6. 多类型Item实现
  7. 点击事件处理

RecyclerView是Android开发中最常用的控件之一,掌握其使用方法对于开发高质量应用至关重要。

最后更新:2026-03-26