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:
- RecyclerView的基本使用
- LayoutManager布局管理
- ItemDecoration装饰器
- Item动画效果
- DiffUtil高效更新
- 多类型Item实现
- 点击事件处理
RecyclerView是Android开发中最常用的控件之一,掌握其使用方法对于开发高质量应用至关重要。
最后更新:2026-03-26