在 Android 开发中,列表控件是承载大量结构化数据展示的核心组件,从早期 Android 系统推出的ListView,到 Android 5.0(API 21)官方推出的RecyclerView,两款控件都是开发中高频使用的列表实现方案。ListView奠定了 Android 列表的核心设计思想,但在复杂场景下暴露出性能、扩展性的诸多痛点;而RecyclerView作为官方定位的ListView 升级版,通过解耦设计、性能优化、功能拓展,完美解决了前者的弊端,成为目前 Android 开发中主流的列表控件。
ListView
ListView是 Android 早期推出的基础线性列表控件,基于Adapter 设计模式实现数据与视图的解耦,是入门 Android 开发接触的第一个复杂控件,其核心设计思想为后续 RecyclerView 奠定了基础。
1. ListView 核心设计思想
ListView 的核心是 「视图复用」:列表中可见区域的 ItemView 数量是固定的(等于屏幕能显示的 Item 数 + 1~2),当列表滚动时,离开屏幕的 ItemView 不会被销毁,而是被放入缓存池(ScrapView),新进入屏幕的 ItemView 会从缓存池中取出复用,重新绑定新数据,从而避免频繁创建 / 销毁 View 对象 ,减少内存占用和 UI 绘制开销。
2. ListView 核心组成
ListView 的实现依赖三大核心部分,三者相互配合完成列表展示,缺一不可:
- ListView 控件:视图容器,负责 ItemView 的布局、滚动、触摸事件处理;
- Adapter 适配器:数据与视图的桥梁,负责将数据集合转换为对应的 ItemView,核心是
BaseAdapter(抽象基类),常用子类有ArrayAdapter(简单数据)、SimpleAdapter(复杂布局); - ViewHolder(可选):视图持有者,用于缓存 ItemView 中的子控件(如 TextView、ImageView),避免每次刷新 Item 时重复调用
findViewById(耗时操作)。
3. 示例代码
布局文件(ListView 布局 + Item 布局)
XML
<!-- 主布局:activity_list_view.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="match_parent">
<ListView
android:id="@+id/lv_data"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#E5E5E5"
android:dividerHeight="1dp"/>
</LinearLayout>
<!-- Item布局:item_list_view.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_item"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:textSize="16sp"/>
自定义 Adapter
Kotlin
// 自定义ListView适配器,继承BaseAdapter
class MyListViewAdapter(private val dataList: List<String>) : BaseAdapter() {
// 获取数据总数
override fun getCount(): Int = dataList.size
// 获取指定位置数据
override fun getItem(position: Int): Any = dataList[position]
// 获取Item的ID
override fun getItemId(position: Int): Long = position.toLong()
// 核心:获取ItemView,实现视图复用+ViewHolder缓存
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val viewHolder: ViewHolder
val itemView: View
// 判断缓存View是否为空,为空则创建,否则复用
if (convertView == null) {
itemView = LayoutInflater.from(parent?.context)
.inflate(R.layout.item_list_view, parent, false)
// 初始化ViewHolder,缓存子控件
viewHolder = ViewHolder(itemView.findViewById(R.id.tv_item))
// 将ViewHolder绑定到ItemView,避免重复创建
itemView.tag = viewHolder
} else {
// 复用缓存View
itemView = convertView
// 从View中取出ViewHolder
viewHolder = itemView.tag as ViewHolder
}
// 绑定数据到ViewHolder的控件,无需再次findViewById
viewHolder.tvItem.text = dataList[position]
return itemView
}
// 视图持有者:缓存Item子控件,私有化构造
private class ViewHolder(val tvItem: TextView)
}
Activity 中初始化 ListView
Kotlin
class ListViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_view)
// 模拟测试数据
val dataList = List(50) { "ListView Item $it" }
// 获取ListView控件,设置适配器
val listView = findViewById<ListView>(R.id.lv_data)
listView.adapter = MyListViewAdapter(dataList)
}
}
4. ListView的痛点
尽管 ListView 实现了基础的视图复用,但在复杂业务场景(如网格布局、瀑布流、item 动画、局部刷新) 中,暴露出诸多性能和扩展性问题,也是官方推出 RecyclerView 的根本原因:
- ViewHolder 复用非强制:ViewHolder 是开发者手动实现的可选方案,新手易忽略,导致频繁
findViewById,性能低下; - 布局管理器单一:仅支持垂直线性布局,无法实现网格、瀑布流等复杂布局,需自定义控件(如 GridView、StaggeredGridView),且各控件无法兼容;
- 缺乏内置动画支持:Item 的增、删、改操作无默认动画,自定义动画需重写触摸事件和布局逻辑,开发成本高;
- 局部刷新困难:仅提供
notifyDataSetChanged()全量刷新方法,即使只有一个 Item 数据变化,也会刷新整个列表,导致 UI 闪烁、性能浪费; - 缓存机制简单:仅提供一级缓存(ScrapView),视图复用效率有限,快速滚动时仍会出现卡顿;
- 分割线样式单一:仅能通过
android:divider设置纯色分割线,无法实现自定义样式(如渐变、带边距的分割线); - 交互拓展性差:Item 的侧滑、拖拽等交互,需手动处理触摸事件,代码繁琐且易出现兼容性问题。
RecyclerView
RecyclerView是 Android 5.0(API 21)随 Material Design 一起推出的高级列表控件,官方定位为ListView/GridView 的替代者,其核心设计思想是「极致解耦 + 高性能复用」------ 将列表的布局、复用、装饰、动画等核心功能拆分为独立组件,让开发者可灵活定制,同时优化了缓存机制和刷新逻辑,从根源解决了 ListView 的痛点。
1. RecyclerView 的核心设计思想
RecyclerView 继承了 ListView 的视图复用核心,同时提出「组件化解耦」思想:将列表的通用功能抽象为独立的可配置组件,开发者可根据需求灵活替换,无需修改核心代码,实现 "一套核心代码,适配多种列表样式"。
2. RecyclerView 的核心组成
- RecyclerView 控件:负责列表的滚动、触摸事件、组件协调,是核心载体;
- Adapter 适配器:结合 ViewHolder 实现数据绑定,强制实现 ViewHolder;
- ViewHolder:抽象类,必须继承实现,强制缓存 Item 子空间,从根源避免findViewById性能问题;
- LayoutManager 布局管理器:负责 ItemView 的布局排列,决定列表样式;
- ItemDecoration 项装饰:负责 Item的分割线、边距、悬浮头定制;
- ItemAnimator 项动画:负责 Item 增、删、改的默认动画,一键开启,支持自定义。
3. 示例代码
布局文件
XML
<!-- 主布局:activity_recycler_view.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="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_data"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<!-- Item布局直接复用ListView的item_list_view.xml,无需修改 -->
自定义 Adapter+ViewHolder
Kotlin
// 自定义RecyclerView适配器,继承RecyclerView.Adapter,泛型为自定义ViewHolder
class MyRvAdapter(private val dataList: List<String>) :
RecyclerView.Adapter<MyRvAdapter.MyRvViewHolder>() {
// 自定义ViewHolder:必须继承RecyclerView.ViewHolder,强制缓存控件
inner class MyRvViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvItem: TextView = itemView.findViewById(R.id.tv_item)
}
// 创建ViewHolder:加载Item布局,创建ViewHolder实例
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyRvViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.item_list_view, parent, false)
return MyRvViewHolder(itemView)
}
// 绑定数据:将数据绑定到ViewHolder的控件,无需findViewById
override fun onBindViewHolder(holder: MyRvViewHolder, position: Int) {
holder.tvItem.text = dataList[position]
}
// 获取数据总数
override fun getItemCount(): Int = dataList.size
}
Activity 中初始化 RecyclerView
Kotlin
class RecyclerViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler_view)
// 模拟测试数据
val dataList = List(50) { "RecyclerView Item $it" }
// 获取RecyclerView控件
val recyclerView = findViewById<RecyclerView>(R.id.rv_data)
// 设置布局管理器(线性布局,垂直方向),ListView无需配置(默认垂直)
recyclerView.layoutManager = LinearLayoutManager(this)
// 设置适配器
recyclerView.adapter = MyRvAdapter(dataList)
// 设置默认Item动画
recyclerView.itemAnimator = DefaultItemAnimator()
// 添加分割线(默认灰色1dp,适配线性布局)
recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
}
}
4. 相较于ListView的优势
- 强制 ViewHolder 复用,从根源解决性能问题;
- 解耦的布局管理器,支持多布局样式,一套代码适配所有;
- 内置 Item 动画支持,一键实现增删改动画,无需自定义;
- 提供一系列精准的局部刷新方法,避免全量刷新浪费性能;
- 升级的四级缓存机制,视图复用更高效。
但不得不提,在 Jetpack Compose 中,RecyclerView 的地位被 LazyColumn 和 LazyRow 取代了。基于 Compose 懒加载思想,后者实现了列表的按需加载和视图复用,且代码更简洁、开发效率更高。