目录
2.1 ArrayAdapter:最简单的 “文本列表” 实现
布局文件(activity_array_adapter.xml)
2.2 SimpleAdapter:快速实现 “图文混合” 列表
列表项布局(item_simple_adapter.xml)
2.3 BaseAdapter:自定义程度最高的 “万能基础 Adapter”
步骤 2:列表项布局(item_base_adapter.xml)
三、进阶必备:RecyclerView.Adapter 万能实战
3.1 RecyclerView.Adapter 的核心优势
步骤 2:Activity 布局(activity_recycler_base.xml)
步骤 3:列表项布局(item_recycler_base.xml)
步骤 5:Activity 中初始化 RecyclerView
3.3 高级用法 1:多类型布局(如 Feed 流、首页混合列表)
步骤 1:定义数据模型(需实现 equals 和 hashCode)
4.2 进阶优化:使用 DiffUtil 替代 notifyDataSetChanged ()
5.2 问题 2:notifyDataSetChanged () 不生效
5.3 问题 3:RecyclerView 没有 ItemClickListener
5.4 问题 4:刷新后 setBackgroundColor 不生效

class 卑微码农:
def __init__(self):
self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
self.发量 = 100 # 初始发量
self.咖啡因耐受度 = '极限'
def 修Bug(self, bug):
try:
# 试图用玄学解决问题
if bug.严重程度 == '离谱':
print("这一定是环境问题!")
else:
print("让我看看是谁又没写注释...哦,是我自己。")
except Exception as e:
# 如果try块都救不了,那就...
print("重启一下试试?")
self.发量 -= 1 # 每解决一个bug,头发-1
# 实例化一个我
我 = 卑微码农()
前言:
作为 Android 开发中连接数据与视图的核心组件,Adapter 是每个开发者绕不开的知识点。新手常被 “convertView 复用”“ViewHolder 优化”“多类型布局” 等概念劝退,老手也可能在性能优化或复杂场景中踩坑。这篇文章从实战角度出发,用通俗的语言 + 可直接运行的代码,带你从基础到高级,彻底掌握 Android Adapter 的全部核心用法。
一、先搞懂:Adapter 到底是什么?

很多新手刚接触时会觉得 Adapter 抽象,其实一句话就能说透:Adapter 是 “数据” 和 “视图” 之间的 “翻译官”+“搬运工”。
数据可能是接口返回的列表、本地数据库的查询结果,视图可能是 ListView、RecyclerView 这类列表控件。数据不懂视图的 “语言”(不知道怎么展示),视图也不懂数据的 “格式”(不知道怎么读取),而 Adapter 要做两件事:一是把数据转换成视图能识别的格式,二是高效地把转换后的视图交给列表控件展示。
1.1 Adapter 的核心工作流程
用一个生活化的例子理解:把列表控件想象成超市货架,数据是仓库里的商品,Adapter 就是理货员。
- 货架(ListView/RecyclerView)告诉理货员(Adapter):我需要多少商品(getCount ());
- 理货员去仓库(数据源)取出对应商品(getItem ());
- 理货员把商品装进统一的包装盒(View),贴上价格标签(绑定数据);
- 理货员把包装好的商品放到货架指定位置(展示视图);
- 商品卖完或补货时,理货员及时更新货架(notifyDataSetChanged ())。
1.2 Adapter 与 AdapterView 的关系
Adapter 不能单独工作,必须配合 “容器控件” 使用,这些容器控件统称为 AdapterView,常见的有:
- ListView:早期主流列表控件,支持线性布局;
- GridView:网格布局列表(如九宫格);
- Spinner:下拉选择框;
- RecyclerView:Android 5.0 后推出的万能列表控件,支持线性、网格、瀑布流等多种布局。
其中 RecyclerView 凭借模块化设计和优秀性能,现已成为开发首选,后续实战部分会重点围绕它展开。
二、新手入门:3 个基础 Adapter 快速上手
Android 系统提供了多个现成的基础 Adapter,无需自定义就能实现简单列表需求。新手可以从这几个开始,感受 Adapter 的工作逻辑。
2.1 ArrayAdapter:最简单的 “文本列表” 实现

ArrayAdapter 是最基础的 Adapter,专门用于展示单一文本类型的列表,无需自定义布局(也可自定义),一行代码就能搞定。
核心特点
- 支持字符串数组、List<String> 等简单数据源;
- 默认使用系统自带的文本布局(android.R.layout.simple_list_item_1);
- 适合快速实现纯文本列表(如设置选项、菜单列表)。
实战代码(Kotlin/Java 双版本)
// Kotlin 版本
class ArrayAdapterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_array_adapter)
val listView = findViewById<ListView>(R.id.lv_array)
// 1. 数据源:字符串列表
val dataList = listOf("Android 基础", "Java 核心", "Kotlin 进阶", "RecyclerView 实战", "性能优化")
// 2. 创建 ArrayAdapter:参数依次是上下文、布局资源、数据源
val adapter = ArrayAdapter(
this,
android.R.layout.simple_list_item_1, // 系统默认文本布局
dataList
)
// 3. 给 ListView 设置 Adapter
listView.adapter = adapter
// 4. 列表项点击事件
listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, "选中了:${dataList[position]}", Toast.LENGTH_SHORT).show()
}
}
}
// Java 版本
public class ArrayAdapterActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_array_adapter);
ListView listView = findViewById(R.id.lv_array);
// 1. 数据源
List<String> dataList = new ArrayList<>();
dataList.add("Android 基础");
dataList.add("Java 核心");
dataList.add("Kotlin 进阶");
dataList.add("RecyclerView 实战");
dataList.add("性能优化");
// 2. 创建 ArrayAdapter
ArrayAdapter<String> adapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
dataList
);
// 3. 设置 Adapter
listView.setAdapter(adapter);
// 4. 点击事件
listView.setOnItemClickListener((parent, view, position, id) -> {
Toast.makeText(ArrayAdapterActivity.this, "选中了:" + dataList.get(position), Toast.LENGTH_SHORT).show();
});
}
}
布局文件(activity_array_adapter.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"
android:orientation="vertical">
<ListView
android:id="@+id/lv_array"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
注意事项
- 系统默认布局仅支持文本,如果需要展示图片 + 文本,需自定义布局并使用 ArrayAdapter 的重载构造;
- 数据源变化时,需调用
adapter.notifyDataSetChanged()通知视图更新(后续所有 Adapter 均适用)。
2.2 SimpleAdapter:快速实现 “图文混合” 列表

如果需要展示图文混合的列表(如新闻标题 + 缩略图、联系人头像 + 姓名),SimpleAdapter 是比 ArrayAdapter 更合适的选择。它支持将 Map 类型的数据源映射到 XML 布局的控件中,无需自定义 Adapter。
核心特点
- 支持多控件展示(TextView、ImageView 等);
- 数据源为 List<Map<String, Object>>,键值对形式便于映射;
- 无需重写 Adapter,仅需配置映射关系即可。
实战代码:联系人列表(图文混合)
// Kotlin 版本
class SimpleAdapterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_simple_adapter)
val listView = findViewById<ListView>(R.id.lv_simple)
// 1. 数据源:List<Map> 形式,每个 Map 对应一个列表项
val dataList = mutableListOf<Map<String, Any>>()
dataList.add(mapOf(
"avatar" to R.drawable.avatar1, // 头像资源ID
"name" to "张三",
"phone" to "13800138000"
))
dataList.add(mapOf(
"avatar" to R.drawable.avatar2,
"name" to "李四",
"phone" to "13900139000"
))
dataList.add(mapOf(
"avatar" to R.drawable.avatar3,
"name" to "王五",
"phone" to "13700137000"
))
// 2. 定义布局控件的 key(与 Map 的 key 对应)
val from = arrayOf("avatar", "name", "phone")
// 3. 定义布局控件的 ID(与 from 数组顺序对应)
val to = intArrayOf(R.id.iv_avatar, R.id.tv_name, R.id.tv_phone)
// 4. 创建 SimpleAdapter
val adapter = SimpleAdapter(
this,
dataList,
R.layout.item_simple_adapter, // 自定义列表项布局
from,
to
)
// 5. 设置 Adapter
listView.adapter = adapter
}
}
列表项布局(item_simple_adapter.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="80dp"
android:gravity="center_vertical"
android:paddingHorizontal="16dp"
android:orientation="horizontal">
<!-- 头像 -->
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="centerCrop"
android:background="@drawable/shape_circle" />
<!-- 姓名和电话容器 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="14sp"
android:textColor="@color/gray" />
</LinearLayout>
</LinearLayout>
注意事项
- SimpleAdapter 仅支持静态数据映射,不适合动态修改控件样式(如根据状态改变颜色);
- ImageView 仅支持本地资源(drawable/mipmap),若需加载网络图片,需自定义 Adapter 或使用图片加载库(如 Glide)。
2.3 BaseAdapter:自定义程度最高的 “万能基础 Adapter”

ArrayAdapter 和 SimpleAdapter 适合简单场景,但实际开发中,列表项往往包含复杂布局、动态样式或交互逻辑(如按钮点击、状态切换),这时就需要自定义 BaseAdapter。
BaseAdapter 是所有 Adapter 的基类,需要重写 4 个核心方法,灵活性最高,也是新手必须掌握的基础技能。
核心方法说明
| 方法名 | 作用 |
|---|---|
getCount() | 返回数据源的长度(列表项数量) |
getItem(position: Int) | 返回指定位置的数据源对象 |
getItemId(position: Int) | 返回指定位置的列表项 ID(通常返回 position) |
getView(position: Int, convertView: View?, parent: ViewGroup) | 创建 / 复用列表项视图,并绑定数据 |
实战代码:带点赞功能的新闻列表
步骤 1:定义数据模型
// 新闻数据模型
data class NewsModel(
val title: String, // 新闻标题
val content: String, // 新闻摘要
val likeCount: Int, // 点赞数
var isLiked: Boolean // 是否已点赞(可变状态)
)
步骤 2:列表项布局(item_base_adapter.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:padding="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp"
android:textColor="@color/gray"
android:maxLines="2"
android:ellipsize="end" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_like"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_like_normal" />
<TextView
android:id="@+id/tv_like_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
步骤 3:自定义 BaseAdapter
class NewsBaseAdapter(
private val context: Context,
private val dataList: MutableList<NewsModel>
) : BaseAdapter() {
// 1. 返回列表项数量
override fun getCount(): Int = dataList.size
// 2. 返回指定位置的数据源
override fun getItem(position: Int): NewsModel = dataList[position]
// 3. 返回列表项 ID
override fun getItemId(position: Int): Long = position.toLong()
// 4. 创建/复用视图并绑定数据(核心方法)
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
// ViewHolder 用于缓存控件,避免重复 findViewById
val holder: ViewHolder
val view: View
if (convertView == null) {
// 第一次创建视图:加载布局、初始化控件、绑定 ViewHolder
view = LayoutInflater.from(context).inflate(R.layout.item_base_adapter, parent, false)
holder = ViewHolder()
holder.tvTitle = view.findViewById(R.id.tv_title)
holder.tvContent = view.findViewById(R.id.tv_content)
holder.ivLike = view.findViewById(R.id.iv_like)
holder.tvLikeCount = view.findViewById(R.id.tv_like_count)
// 将 ViewHolder 存储到 View 中
view.tag = holder
} else {
// 复用已有视图:直接从 View 中获取 ViewHolder
view = convertView
holder = view.tag as ViewHolder
}
// 绑定数据
val news = getItem(position)
holder.tvTitle?.text = news.title
holder.tvContent?.text = news.content
holder.tvLikeCount?.text = news.likeCount.toString()
// 设置点赞状态
if (news.isLiked) {
holder.ivLike?.setImageResource(R.drawable.ic_like_selected)
holder.tvLikeCount?.setTextColor(ContextCompat.getColor(context, R.color.red))
} else {
holder.ivLike?.setImageResource(R.drawable.ic_like_normal)
holder.tvLikeCount?.setTextColor(ContextCompat.getColor(context, R.color.gray))
}
// 点赞点击事件
holder.ivLike?.setOnClickListener {
news.isLiked = !news.isLiked
val newCount = if (news.isLiked) news.likeCount + 1 else news.likeCount - 1
news.likeCount = newCount
// 通知 Adapter 数据变化,更新视图
notifyDataSetChanged()
}
return view
}
// ViewHolder 静态内部类:缓存列表项控件
private class ViewHolder {
var tvTitle: TextView? = null
var tvContent: TextView? = null
var ivLike: ImageView? = null
var tvLikeCount: TextView? = null
}
}
步骤 4:Activity 中使用 Adapter
class BaseAdapterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_base_adapter)
val listView = findViewById<ListView>(R.id.lv_base)
// 初始化数据源
val dataList = mutableListOf<NewsModel>().apply {
add(NewsModel("Android 14 正式发布,这些新特性值得关注", "谷歌推出的 Android 14 带来了更灵活的权限管理、更好的性能优化...", 128, false))
add(NewsModel("RecyclerView 性能优化实战", "掌握这 5 个技巧,让你的列表滑动如丝般顺滑...", 89, false))
add(NewsModel("Kotlin 协程入门到精通", "协程是 Kotlin 中处理异步任务的核心工具,本文带你快速上手...", 215, true))
}
// 创建 Adapter 并设置
val adapter = NewsBaseAdapter(this, dataList)
listView.adapter = adapter
}
}
关键优化点:ViewHolder 模式
这是 BaseAdapter 中最核心的优化手段,作用是缓存列表项的控件实例,避免每次调用 getView () 时都执行 findViewById ()。
- 没有 ViewHolder 时:每次滑动列表都会重新查找控件,findViewById () 是耗时操作,会导致滑动卡顿;
- 有 ViewHolder 时:控件实例被缓存到 ViewHolder 中,复用视图时直接取出,大幅提升性能。
三、进阶必备:RecyclerView.Adapter 万能实战

RecyclerView 是 Android 官方推荐的列表控件,它的 Adapter 设计更先进、性能更优,支持线性、网格、瀑布流等多种布局,是目前开发的首选。
3.1 RecyclerView.Adapter 的核心优势
相比 BaseAdapter,RecyclerView.Adapter 有 3 个关键升级:
- 强制 ViewHolder 模式:无需手动判断 convertView 是否为 null,系统自动管理视图复用;
- 职责分离:布局排列(LayoutManager)、分割线(ItemDecoration)、动画(ItemAnimator)等功能独立,灵活扩展;
- 分级缓存机制:设计了三级缓存池,视图复用效率远超 ListView+BaseAdapter。
3.2 基础实战:简单列表实现
步骤 1:添加依赖(AndroidX 项目)
RecyclerView 属于 AndroidX 库,需在 build.gradle(Module 级)中添加依赖:
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.3.2'
}
步骤 2:Activity 布局(activity_recycler_base.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_base"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="16dp" />
</LinearLayout>
步骤 3:列表项布局(item_recycler_base.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="60dp"
android:gravity="center_vertical"
android:borderBottomWidth="1dp"
android:borderBottomColor="@color/gray_light">
<TextView
android:id="@+id/tv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp" />
</LinearLayout>
步骤 4:自定义 RecyclerView.Adapter
class BaseRecyclerAdapter(
private val dataList: List<String>
) : RecyclerView.Adapter<BaseRecyclerAdapter.BaseViewHolder>() {
// 1. 创建 ViewHolder(对应 BaseAdapter 的 convertView == null 时)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_recycler_base, parent, false)
return BaseViewHolder(view)
}
// 2. 绑定数据(对应 BaseAdapter 的 getView 中绑定数据部分)
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
holder.tvItem.text = dataList[position]
// 列表项点击事件
holder.itemView.setOnClickListener {
Toast.makeText(holder.itemView.context, "选中:${dataList[position]}", Toast.LENGTH_SHORT).show()
}
}
// 3. 返回列表项数量
override fun getItemCount(): Int = dataList.size
// 自定义 ViewHolder(与 BaseAdapter 的 ViewHolder 作用一致)
class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvItem: TextView = itemView.findViewById(R.id.tv_item)
}
}
步骤 5:Activity 中初始化 RecyclerView
class RecyclerBaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler_base)
val recyclerView = findViewById<RecyclerView>(R.id.rv_base)
// 1. 设置布局管理器(线性布局,垂直方向)
recyclerView.layoutManager = LinearLayoutManager(this)
// 2. 初始化数据源
val dataList = listOf("首页", "分类", "发现", "我的", "收藏", "历史", "设置")
// 3. 创建并设置 Adapter
val adapter = BaseRecyclerAdapter(dataList)
recyclerView.adapter = adapter
}
}
3.3 高级用法 1:多类型布局(如 Feed 流、首页混合列表)
实际开发中常遇到 “一个列表包含多种布局” 的场景(如首页的 Banner 轮播 + 文章列表 + 推荐卡片),这就需要通过 getItemViewType() 实现多类型布局。
实战代码:包含 Banner、文章、Footer 的混合列表
步骤 1:定义数据模型(密封类统一管理多类型数据)
// 密封类:统一多类型数据模型
sealed class FeedItem {
// Banner 类型:包含轮播图图片列表
data class BannerItem(val images: List<String>) : FeedItem()
// 文章类型:包含标题、作者、阅读数
data class ArticleItem(val title: String, val author: String, val readCount: Int) : FeedItem()
// Footer 类型:包含底部提示文本
data class FooterItem(val text: String) : FeedItem()
}
步骤 2:定义三种布局
- Banner 布局(item_feed_banner.xml):
<ImageView
android:id="@+id/iv_banner"
android:layout_width="match_parent"
android:layout_height="180dp"
android:scaleType="centerCrop" />
- 文章布局(item_feed_article.xml):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_article_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_article_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/gray" />
<TextView
android:id="@+id/tv_article_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textSize="14sp"
android:textColor="@color/gray" />
</LinearLayout>
</LinearLayout>
- Footer 布局(item_feed_footer.xml):
<TextView
android:id="@+id/tv_footer"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:textSize="14sp"
android:textColor="@color/gray" />
步骤 3:自定义多类型 Adapter
class FeedMultiTypeAdapter(
private val dataList: List<FeedItem>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// 定义布局类型常量
companion object {
private const val TYPE_BANNER = 1
private const val TYPE_ARTICLE = 2
private const val TYPE_FOOTER = 3
}
// 1. 返回当前位置的布局类型
override fun getItemViewType(position: Int): Int {
return when (dataList[position]) {
is FeedItem.BannerItem -> TYPE_BANNER
is FeedItem.ArticleItem -> TYPE_ARTICLE
is FeedItem.FooterItem -> TYPE_FOOTER
}
}
// 2. 根据布局类型创建对应的 ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
TYPE_BANNER -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_feed_banner, parent, false)
BannerViewHolder(view)
}
TYPE_ARTICLE -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_feed_article, parent, false)
ArticleViewHolder(view)
}
TYPE_FOOTER -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_feed_footer, parent, false)
FooterViewHolder(view)
}
else -> throw IllegalArgumentException("未知布局类型")
}
}
// 3. 根据 ViewHolder 类型绑定对应数据
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = dataList[position]
when (holder) {
is BannerViewHolder -> {
val bannerItem = item as FeedItem.BannerItem
// 这里用 Glide 加载网络图片(需添加 Glide 依赖)
Glide.with(holder.itemView.context)
.load(bannerItem.images[0]) // 简化:加载第一张图
.into(holder.ivBanner)
}
is ArticleViewHolder -> {
val articleItem = item as FeedItem.ArticleItem
holder.tvTitle.text = articleItem.title
holder.tvAuthor.text = articleItem.author
holder.tvRead.text = "阅读 ${articleItem.readCount}"
}
is FooterViewHolder -> {
val footerItem = item as FeedItem.FooterItem
holder.tvFooter.text = footerItem.text
}
}
}
// 4. 返回列表项数量
override fun getItemCount(): Int = dataList.size
// Banner 对应的 ViewHolder
class BannerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivBanner: ImageView = itemView.findViewById(R.id.iv_banner)
}
// 文章对应的 ViewHolder
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvTitle: TextView = itemView.findViewById(R.id.tv_article_title)
val tvAuthor: TextView = itemView.findViewById(R.id.tv_article_author)
val tvRead: TextView = itemView.findViewById(R.id.tv_article_read)
}
// Footer 对应的 ViewHolder
class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvFooter: TextView = itemView.findViewById(R.id.tv_footer)
}
}
步骤 4:Activity 中初始化
class FeedMultiTypeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_feed_multi_type)
val recyclerView = findViewById<RecyclerView>(R.id.rv_feed)
recyclerView.layoutManager = LinearLayoutManager(this)
// 初始化多类型数据源
val dataList = listOf<FeedItem>(
FeedItem.BannerItem(listOf(
"https://picsum.photos/800/400?1",
"https://picsum.photos/800/400?2",
"https://picsum.photos/800/400?3"
)),
FeedItem.ArticleItem("Android Adapter 完全指南", "技术干货君", 1234),
FeedItem.ArticleItem("RecyclerView 性能优化实战", "Android 开发笔记", 897),
FeedItem.ArticleItem("Kotlin 密封类在多类型布局中的应用", "编程小助手", 562),
FeedItem.FooterItem("已加载全部内容")
)
val adapter = FeedMultiTypeAdapter(dataList)
recyclerView.adapter = adapter
}
}
3.4 高级用法 2:局部刷新(避免整列表重绘)
传统的 notifyDataSetChanged() 会刷新整个列表,导致不必要的性能消耗,还可能出现视图闪烁。RecyclerView 提供了 notifyItemChanged() 等精准刷新方法,结合 DiffUtil 可以实现 “只刷新变化的数据”。
核心工具:DiffUtil
DiffUtil 是 Android 支持库提供的工具类,能自动计算新旧数据集的差异,只更新变化的项,无需手动判断哪些数据变了。
实战代码:用 DiffUtil 实现局部刷新
步骤 1:定义数据模型(需实现 equals 和 hashCode)
data class UserModel(
val id: Long, // 唯一标识(关键)
val name: String,
val age: Int
)
步骤 2:创建 DiffUtil.Callback 实现类
class UserDiffCallback(
private val oldList: List<UserModel>,
private val newList: List<UserModel>
) : DiffUtil.Callback() {
// 1. 判断两个 item 是否是同一个对象(通过唯一标识 id)
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
// 2. 判断两个 item 的内容是否相同
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
// 3. 返回变化的 payload(可选,用于局部刷新特定字段)
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
// 只返回变化的字段(如姓名或年龄)
val payload = mutableMapOf<String, Any>()
if (oldItem.name != newItem.name) {
payload["name"] = newItem.name
}
if (oldItem.age != newItem.age) {
payload["age"] = newItem.age
}
return if (payload.isEmpty()) null else payload
}
// 4. 返回旧列表长度
override fun getOldListSize(): Int = oldList.size
// 5. 返回新列表长度
override fun getNewListSize(): Int = newList.size
}
步骤 3:优化 Adapter,支持局部刷新
class UserAdapter : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
private var dataList: List<UserModel> = emptyList()
// 更新数据:使用 DiffUtil 计算差异并刷新
fun submitList(newList: List<UserModel>) {
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(dataList, newList))
dataList = newList
diffResult.dispatchUpdatesTo(this) // 只刷新变化的项
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
// 重载 onBindViewHolder,支持 payload 局部刷新
override fun onBindViewHolder(holder: UserViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isEmpty()) {
// payload 为空:完整绑定数据
super.onBindViewHolder(holder, position, payloads)
} else {
// payload 不为空:只刷新变化的字段
val user = dataList[position]
val payload = payloads[0] as Map<String, Any>
payload.forEach { (key, value) ->
when (key) {
"name" -> holder.tvName.text = value as String
"age" -> holder.tvAge.text = "年龄:${value as Int}"
}
}
}
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = dataList[position]
holder.tvId.text = "ID:${user.id}"
holder.tvName.text = user.name
holder.tvAge.text = "年龄:${user.age}"
}
override fun getItemCount(): Int = dataList.size
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvId: TextView = itemView.findViewById(R.id.tv_id)
val tvName: TextView = itemView.findViewById(R.id.tv_name)
val tvAge: TextView = itemView.findViewById(R.id.tv_age)
}
}
步骤 4:Activity 中测试局部刷新
class DiffUtilActivity : AppCompatActivity() {
private lateinit var adapter: UserAdapter
private val initialList = listOf(
UserModel(1, "张三", 25),
UserModel(2, "李四", 28),
UserModel(3, "王五", 30)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_diff_util)
val recyclerView = findViewById<RecyclerView>(R.id.rv_user)
recyclerView.layoutManager = LinearLayoutManager(this)
adapter = UserAdapter()
adapter.submitList(initialList)
recyclerView.adapter = adapter
// 点击按钮更新数据(只修改张三的年龄和李四的姓名)
findViewById<Button>(R.id.btn_update).setOnClickListener {
val newList = listOf(
UserModel(1, "张三", 26), // 年龄变化
UserModel(2, "李四同学", 28), // 姓名变化
UserModel(3, "王五", 30) // 无变化
)
adapter.submitList(newList)
}
}
}
效果说明
点击按钮后,只有张三的年龄和李四的姓名会刷新,王五的视图不会重新绘制,相比 notifyDataSetChanged() 大幅提升性能,尤其适合大数据量列表。
四、性能优化:让列表滑动如丝般顺滑

Adapter 的性能直接影响列表的滑动体验,以下是 5 个实战性极强的优化技巧,覆盖从基础到高级的全场景。
4.1 必做优化:复用视图 + ViewHolder 模式
这是最基础也是最重要的优化,前面的示例中已经用到,核心原则是:
- 避免重复创建 View:通过 convertView(BaseAdapter)或 RecyclerView 自带的复用机制,重复使用已创建的视图;
- 避免重复 findViewById:用 ViewHolder 缓存控件实例,一次查找多次使用。
4.2 进阶优化:使用 DiffUtil 替代 notifyDataSetChanged ()
如 3.4 节所示,notifyDataSetChanged() 会强制刷新整个列表,而 DiffUtil 只刷新变化的项,减少 UI 重绘和数据绑定的开销。
4.3 高级优化:分批加载数据(分页加载)
当列表数据量较大(如几百条、几千条)时,一次性加载所有数据会导致初始化缓慢、内存占用过高。分批加载(分页)是解决这个问题的关键:
- 首次加载前 N 条数据(如 20 条);
- 列表滑动到底部时,加载下一页数据;
- 用
notifyItemRangeInserted()刷新新增数据(避免整列表刷新)。
核心代码示例
class PagingAdapter : RecyclerView.Adapter<PagingAdapter.PagingViewHolder>() {
private val dataList = mutableListOf<String>()
private var isLoading = false // 避免重复请求
// 添加分页数据
fun addData(newData: List<String>) {
val startPosition = dataList.size
dataList.addAll(newData)
notifyItemRangeInserted(startPosition, newData.size)
isLoading = false
}
// 标记加载中状态
fun setLoading(loading: Boolean) {
isLoading = loading
}
// ... 其他方法(onCreateViewHolder、onBindViewHolder 等)
class PagingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvContent: TextView = itemView.findViewById(R.id.tv_content)
}
}
// Activity 中监听滑动到底部
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
// 滑动到底部且不在加载中,加载下一页
if (!adapter.isLoading && visibleItemCount + firstVisibleItemPosition >= totalItemCount - 5) {
adapter.setLoading(true)
loadNextPage() // 加载下一页数据(网络请求/数据库查询)
}
}
})
// 模拟网络请求加载下一页
private fun loadNextPage() {
Handler(Looper.getMainLooper()).postDelayed({
val newData = List(20) { "第 ${dataList.size + it + 1} 条数据" }
adapter.addData(newData)
}, 1000)
}
4.4 图片优化:延迟加载 + 图片缓存
列表中的图片是性能消耗的重灾区,优化方案:
- 使用图片加载库:Glide、Picasso 等库自带延迟加载、图片压缩、缓存功能;
- 压缩图片尺寸:根据列表项大小加载对应分辨率的图片,避免加载过大图片;
- 占位图 + 渐入效果:提升用户体验,避免图片加载完成后突然闪现。
示例代码(Glide 加载优化)
Glide.with(holder.itemView.context)
.load(imageUrl)
.placeholder(R.drawable.ic_placeholder) // 占位图
.error(R.drawable.ic_error) // 加载失败图
.override(300, 200) // 压缩图片尺寸
.centerCrop() // 缩放模式
.diskCacheStrategy(DiskCacheStrategy.ALL) // 缓存策略
.into(holder.ivImage)
4.5 布局优化:减少层级 + 避免过度绘制
- 减少布局层级:用 ConstraintLayout 替代多层 LinearLayout,避免嵌套过深;
- 移除不必要的背景:如果父布局和子布局的背景重叠,移除子布局的背景;
- 使用 merge 标签:减少根布局层级(如列表项布局的根标签用 merge,复用父布局的布局参数)。
五、常见问题与避坑指南

开发中遇到的 Adapter 问题,90% 都是以下几种情况,提前掌握能少踩很多坑。
5.1 问题 1:列表滑动时数据错乱
原因
视图复用导致的状态混乱(如点赞状态、选中状态未正确保存)。
解决方案
- 状态存储在数据源中:不要把状态存在 ViewHolder 或 View 中,要存在数据模型里;
- 每次绑定数据时重置状态:在
getView()或onBindViewHolder()中,无论视图是否复用,都明确设置状态(如点赞图标、选中颜色)。
5.2 问题 2:notifyDataSetChanged () 不生效
原因
- 数据源未真正修改(如用 val 定义的列表,重新赋值后未通知 Adapter);
- Adapter 引用的数据源对象未更新(如修改了列表元素但未调用 notify)。
解决方案
- 用可变集合(MutableList)存储数据,修改后调用 notify 方法;
- 如果替换整个数据源,确保 Adapter 引用的是新的列表对象,再调用 notify。
5.3 问题 3:RecyclerView 没有 ItemClickListener
原因
RecyclerView 没有像 ListView 那样的 setOnItemClickListener 方法,需要手动实现。
解决方案
- 在 ViewHolder 中给
itemView设置点击事件; - 通过接口回调将点击事件暴露给 Activity/Fragment。
代码示例
// 定义点击事件接口
interface OnItemClickListener {
fun onItemClick(position: Int)
}
class MyAdapter(
private val dataList: List<String>,
private val listener: OnItemClickListener
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
// ...
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.itemView.setOnClickListener {
listener.onItemClick(position)
}
}
// ...
}
// Activity 中使用
adapter = MyAdapter(dataList, object : OnItemClickListener {
override fun onItemClick(position: Int) {
Toast.makeText(this, "选中:${dataList[position]}", Toast.LENGTH_SHORT).show()
}
})
5.4 问题 4:刷新后 setBackgroundColor 不生效
原因
视图复用导致旧的背景色未被覆盖,或样式设置顺序错误。
解决方案
- 每次绑定数据时先重置背景色(如设置为透明),再设置目标颜色;
- 避免在
onCreateViewHolder()中设置动态变化的样式,统一在onBindViewHolder()中处理。
代码示例
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// 先重置背景色
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
// 再设置目标颜色
if (dataList[position].isSelected) {
holder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.blue))
}
}
六、实战综合案例:完整的网络数据列表

整合前面的知识点,实现一个 “网络请求 + 分页加载 + 下拉刷新 + 局部刷新” 的完整列表,模拟真实开发场景。
6.1 案例需求
- 调用公开 API 获取文章列表数据;
- 支持下拉刷新(刷新第一页);
- 支持上拉加载更多(分页加载);
- 点击列表项进入详情页;
- 支持点赞功能(局部刷新点赞数)。
6.2 核心依赖
// 网络请求
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 图片加载
implementation 'com.github.bumptech.glide:glide:4.16.0'
kapt 'com.github.bumptech.glide:compiler:4.16.0'
// 下拉刷新
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
6.3 核心代码
步骤 1:网络请求相关(API 接口、数据模型)
// 文章数据模型
data class ArticleResponse(
val data: List<ArticleModel>
)
data class ArticleModel(
val id: Long,
val title: String,
val author: String,
val cover: String,
val likeCount: Int,
var isLiked: Boolean
)
// Retrofit API 接口
interface ApiService {
@GET("article/list")
suspend fun getArticleList(@Query("page") page: Int, @Query("size") size: Int): Response<ArticleResponse>
}
// 网络请求工具类
object RetrofitClient {
private const val BASE_URL = "https://api.example.com/" // 替换为真实 API 地址
val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
步骤 2:Adapter 实现(支持点赞局部刷新)
class ArticleAdapter(
private val onLikeClick: (Int, Boolean) -> Unit // 点赞点击回调
) : RecyclerView.Adapter<ArticleAdapter.ArticleViewHolder>() {
private var dataList: List<ArticleModel> = emptyList()
fun submitList(newList: List<ArticleModel>) {
val diffResult = DiffUtil.calculateDiff(ArticleDiffCallback(dataList, newList))
dataList = newList
diffResult.dispatchUpdatesTo(this)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_article, parent, false)
return ArticleViewHolder(view)
}
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
val article = dataList[position]
holder.tvTitle.text = article.title
holder.tvAuthor.text = article.author
holder.tvLikeCount.text = article.likeCount.toString()
// 加载封面图
Glide.with(holder.itemView.context)
.load(article.cover)
.placeholder(R.drawable.ic_cover_placeholder)
.error(R.drawable.ic_cover_error)
.centerCrop()
.into(holder.ivCover)
// 设置点赞状态
if (article.isLiked) {
holder.ivLike.setImageResource(R.drawable.ic_like_selected)
holder.tvLikeCount.setTextColor(ContextCompat.getColor(holder.itemView.context, R.color.red))
} else {
holder.ivLike.setImageResource(R.drawable.ic_like_normal)
holder.tvLikeCount.setTextColor(ContextCompat.getColor(holder.itemView.context, R.color.gray))
}
// 点赞点击事件
holder.ivLike.setOnClickListener {
val newLiked = !article.isLiked
val newLikeCount = if (newLiked) article.likeCount + 1 else article.likeCount - 1
// 局部刷新点赞状态和数量
val newArticle = article.copy(isLiked = newLiked, likeCount = newLikeCount)
val newList = dataList.toMutableList().apply {
set(position, newArticle)
}
submitList(newList)
// 回调给 Activity 发送网络请求(实际开发中)
onLikeClick(article.id.toInt(), newLiked)
}
// 列表项点击事件(进入详情页)
holder.itemView.setOnClickListener {
val intent = Intent(holder.itemView.context, ArticleDetailActivity::class.java)
intent.putExtra("article_id", article.id)
holder.itemView.context.startActivity(intent)
}
}
override fun getItemCount(): Int = dataList.size
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivCover: ImageView = itemView.findViewById(R.id.iv_cover)
val tvTitle: TextView = itemView.findViewById(R.id.tv_title)
val tvAuthor: TextView = itemView.findViewById(R.id.tv_author)
val ivLike: ImageView = itemView.findViewById(R.id.iv_like)
val tvLikeCount: TextView = itemView.findViewById(R.id.tv_like_count)
}
// DiffUtil 回调
class ArticleDiffCallback(
oldList: List<ArticleModel>,
newList: List<ArticleModel>
) : DiffUtil.Callback() {
private val oldList = oldList
private val newList = newList
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
val payload = mutableMapOf<String, Any>()
if (oldItem.isLiked != newItem.isLiked) {
payload["isLiked"] = newItem.isLiked
}
if (oldItem.likeCount != newItem.likeCount) {
payload["likeCount"] = newItem.likeCount
}
return if (payload.isEmpty()) null else payload
}
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
}
}
步骤 3:Activity 实现(下拉刷新 + 上拉加载)
class ArticleListActivity : AppCompatActivity() {
private lateinit var adapter: ArticleAdapter
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
private lateinit var recyclerView: RecyclerView
private var currentPage = 1
private val pageSize = 20
private var isLoading = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_article_list)
// 初始化控件
swipeRefreshLayout = findViewById(R.id.srl_refresh)
recyclerView = findViewById(R.id.rv_article)
recyclerView.layoutManager = LinearLayoutManager(this)
// 初始化 Adapter
adapter = ArticleAdapter { articleId, isLiked ->
// 点赞回调:发送网络请求(实际开发中实现)
Toast.makeText(this, "点赞状态:$isLiked", Toast.LENGTH_SHORT).show()
}
recyclerView.adapter = adapter
// 下拉刷新
swipeRefreshLayout.setOnRefreshListener {
currentPage = 1
loadArticleList()
}
// 上拉加载更多
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (!isLoading && !swipeRefreshLayout.isRefreshing
&& visibleItemCount + firstVisibleItemPosition >= totalItemCount - 5) {
currentPage++
loadArticleList()
}
}
})
// 首次加载数据
loadArticleList()
}
// 加载文章列表
private fun loadArticleList() {
isLoading = true
if (currentPage == 1) {
swipeRefreshLayout.isRefreshing = true
}
// 协程发起网络请求
lifecycleScope.launch {
try {
val response = RetrofitClient.apiService.getArticleList(currentPage, pageSize)
if (response.isSuccessful) {
val articleList = response.body()?.data ?: emptyList()
if (currentPage == 1) {
// 第一页:替换数据
adapter.submitList(articleList)
} else {
// 分页:追加数据
val oldList = (adapter as ArticleAdapter).dataList.toMutableList()
oldList.addAll(articleList)
adapter.submitList(oldList)
}
} else {
Toast.makeText(this@ArticleListActivity, "加载失败", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this@ArticleListActivity, "网络错误", Toast.LENGTH_SHORT).show()
} finally {
isLoading = false
swipeRefreshLayout.isRefreshing = false
}
}
}
}
步骤 4:布局文件
- Activity 布局(activity_article_list.xml):
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/srl_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_article"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="16dp"
android:paddingTop="8dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
- 列表项布局(item_article.xml):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:marginBottom="16dp"
android:background="@drawable/shape_rounded_corner"
android:elevation="2dp">
<!-- 封面图 -->
<ImageView
android:id="@+id/iv_cover"
android:layout_width="match_parent"
android:layout_height="160dp"
android:scaleType="centerCrop"
android:background="@color/gray_light" />
<!-- 标题和作者 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"
android:maxLines="2"
android:ellipsize="end" />
<TextView
android:id="@+id/tv_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp"
android:textColor="@color/gray" />
</LinearLayout>
<!-- 点赞区域 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_like"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_like_normal" />
<TextView
android:id="@+id/tv_like_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textSize="14sp"
android:textColor="@color/gray" />
</LinearLayout>
</LinearLayout>
七、总结与拓展
7.1 核心知识点回顾
- Adapter 的核心作用:连接数据与视图,负责数据转换和视图复用;
- 基础 Adapter:ArrayAdapter(纯文本)、SimpleAdapter(简单图文)、BaseAdapter(自定义);
- 主流 Adapter:RecyclerView.Adapter(支持多布局、性能优、可扩展);
- 高级用法:多类型布局、局部刷新(DiffUtil)、分页加载;
- 性能优化:ViewHolder、DiffUtil、图片优化、布局优化;
- 避坑指南:数据错乱、刷新不生效、点击事件、样式问题。
7.2 拓展学习方向
- 更多 RecyclerView 高级功能:ItemDecoration(自定义分割线)、ItemAnimator(动画)、侧滑删除;
- 数据状态管理:结合 ViewModel、LiveData 实现数据与 UI 分离;
- 第三方 Adapter 框架:如 BRVAH(快速开发列表)、Epoxy(复杂多类型列表);
- Jetpack Compose 中的列表适配:LazyColumn 替代 RecyclerView,无需 Adapter。
7.3 实战建议
- 新手从 BaseAdapter 入手,理解视图复用和 ViewHolder 原理;
- 实际开发优先使用 RecyclerView.Adapter,配合 DiffUtil 和分页加载;
- 代码复用:封装通用 Adapter 模板,减少重复开发;
- 注重性能:列表滑动卡顿是常见问题,提前做好优化。
这篇文章从基础到高级,覆盖了 Android Adapter 的全部核心用法,所有示例代码均可直接复制运行。如果需要进一步学习,可以结合官方文档和实际项目反复练习,真正掌握列表适配的精髓。
620

被折叠的 条评论
为什么被折叠?



