在 Android 项目开发中,RecyclerView 是最常用的列表控件,但每次都要写 Adapter 和 ViewHolder,容易重复造轮子。本文整理一套通用 RecyclerView Adapter 实现,支持:
- 泛型数据类型
- ViewBinding
- 数据操作(设置、追加、删除、更新)
- 点击/长按事件监听
- Payload 局部刷新支持
适合大部分简单列表场景,极大减少重复代码。
一、类说明
java
public abstract class BaseRecyclerViewAdapter<T, VB extends ViewBinding>
extends RecyclerView.Adapter<BaseRecyclerViewAdapter.BaseViewHolder<VB>>
泛型说明
- T:数据类型
- VB:ViewBinding 类型
通过泛型和 ViewBinding,减少了 findViewById,让 Adapter 更安全、简洁。
二、主要功能
1. 数据管理
提供完整的数据操作方法:
java
// 设置数据并刷新
public void setData(List<T> list)
// 追加数据
public void addData(List<T> list)
// 插入数据到顶部
public void insertItemToTop(T data)
public void insertItemToTop(T data, RecyclerView recyclerView)
// 删除指定位置的数据
public void removeItem(int position)
// 更新指定位置数据(全量更新或局部刷新)
public void updateItem(int position, T newData)
public void updateItem(int position, T newData, Object payload)
// 清空数据
public void clearData()
// 获取数据列表或指定位置数据
public List<T> getData()
public T getItem(int position)
优势:统一数据操作,保证 RecyclerView 刷新和位置更新正确,避免因 position 错乱导致的 UI 问题。
2. 点击和长按事件
通过接口方式支持通用的点击事件:
java
public interface OnItemClickListener<T> {
void onItemClick(View view, T data, int position);
void onItemLongClick(View view, T data, int position);
}
public void setOnItemClickListener(OnItemClickListener<T> listener)
使用示例:
bash
adapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener<MyData>() {
@Override
public void onItemClick(View view, MyData data, int position) {
Toast.makeText(context, "点击:" + data.getName(), Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, MyData data, int position) {
Toast.makeText(context, "长按:" + data.getName(), Toast.LENGTH_SHORT).show();
}
});
优势:统一处理点击事件,子类无需重复绑定。
3. ViewHolder 与 ViewBinding
bash
public static class BaseViewHolder<VB extends ViewBinding> extends RecyclerView.ViewHolder {
public final VB binding;
public BaseViewHolder(@NonNull VB binding) {
super(binding.getRoot());
this.binding = binding;
}
}
- 持有 ViewBinding 对象,直接通过 binding 操作视图
- 子类只需实现 onCreateViewBinding 和 onBindData 即可
4. 支持局部刷新(Payload)
在 onBindViewHolder 中,支持 Payload 更新:
bash
@Override
public void onBindViewHolder(@NonNull BaseViewHolder<VB> holder,
int position,
@NonNull List<Object> payloads) {
T mData = getItem(position);
onBindData(holder.binding, mData, position, payloads);
}
子类可以根据 payloads 做局部更新,提升 RecyclerView 性能。
5. 插入顶部数据并滚动
支持新消息/刷新场景:
bash
public void insertItemToTop(T data, RecyclerView recyclerView) {
mDataList.add(0, data);
notifyItemInserted(0);
recyclerView.scrollToPosition(0);
}
优势:适合聊天列表、动态消息流等场景。
三、Adapter 使用示例
假设有一个简单的数据类 User:
bash
public class User {
public String name;
public int age;
}
实现 Adapter:
bash
public class UserAdapter extends BaseRecyclerViewAdapter<User, ItemUserBinding> {
public UserAdapter(Context context) {
super(context);
}
@Override
protected ItemUserBinding onCreateViewBinding(LayoutInflater inflater, ViewGroup parent, int viewType) {
return ItemUserBinding.inflate(inflater, parent, false);
}
@Override
protected void onBindData(ItemUserBinding binding, User data, int position, @NonNull List<Object> payloads) {
binding.tvName.setText(data.name);
binding.tvAge.setText(String.valueOf(data.age));
}
}
在 Activity 中使用:
bash
UserAdapter adapter = new UserAdapter(this);
recyclerView.setAdapter(adapter);
adapter.setData(userList);
adapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener<User>() {
@Override
public void onItemClick(View view, User data, int position) {
Toast.makeText(MainActivity.this, data.name, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, User data, int position) {
Toast.makeText(MainActivity.this, "长按:" + data.name, Toast.LENGTH_SHORT).show();
}
});
四、 RecyclerView Adapter 代码示例
bash
package com.xxxx.xxxx;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import java.util.ArrayList;
import java.util.List;
/**
* Author: Su
* Date: 2025/4/24
* Description:
* 通用 BaseRecyclerViewAdapter,适用于多数简单 RecyclerView 列表。
* 支持:
* - 数据设置、追加、清空
* - 点击、长按事件
* - 简洁的 ViewHolder 缓存方式
*
* @param <T> 数据泛型
*/
/**
* 通用支持 ViewBinding 的 BaseRecyclerViewAdapter
*
* @param <T> 数据类型
* @param <VB> ViewBinding 类型
*/
public abstract class BaseRecyclerViewAdapter<T, VB extends ViewBinding> extends RecyclerView.Adapter<BaseRecyclerViewAdapter.BaseViewHolder<VB>> {
protected List<T> mDataList;
protected Context mContext;
private final LayoutInflater mInflater;
private VB mViewBinding;
private T mData;
public BaseRecyclerViewAdapter(Context context) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
this.mDataList = new ArrayList<>();
}
/**
* 设置数据并刷新
*/
public void setData(List<T> list) {
this.mDataList.clear();
if (list != null) {
this.mDataList.addAll(list);
}
notifyDataSetChanged();
}
/**
* 设置数据并刷新指定position
*/
public void setData(List<T> list, int position) {
this.mDataList.clear();
if (list != null) {
this.mDataList.addAll(list);
}
notifyItemChanged(position);
}
/**
* 追加数据
*/
public void addData(List<T> list) {
if (list != null) {
int start = mDataList.size();
mDataList.addAll(list);
notifyItemRangeInserted(start, list.size());
}
}
/**
* 插入一条数据到顶部
*
* @param data 要插入的数据
*/
public void insertItemToTop(T data) {
if (data != null) {
mDataList.add(0, data);
notifyDataSetChanged(); // 🔥 必须整体刷新,保证 position 更新
}
}
/**
* 插入一条数据到顶部并刷新
*
* @param data 要插入的数据
* @param recyclerView 关联的 RecyclerView
*/
public void insertItemToTop(T data, RecyclerView recyclerView) {
if (data != null) {
mDataList.add(0, data);
notifyItemInserted(0);
recyclerView.scrollToPosition(0);
}
}
/**
* 获取当前列表中所有的数据项。
*
* @return 当前数据源列表,类型为 List<T>。
*/
public List<T> getData() {
return mDataList;
}
/**
* 删除指定数据
*
* @param position 要删除的下标
*/
public void removeItem(int position) {
mDataList.remove(position);
notifyDataSetChanged(); // 🔥 必须整体刷新,保证 position 更新
}
/**
* 清空数据
*/
public void clearData() {
mDataList.clear();
notifyDataSetChanged();
}
/**
* 获取某个位置的数据
*/
public T getItem(int position) {
return position >= 0 && position < mDataList.size() ? mDataList.get(position) : null;
}
/**
* 更新指定位置的数据项并刷新该项视图
*
* @param position 要更新的数据项的位置
* @param newData 新的数据项
*/
public void updateItem(int position, T newData) {
if (position >= 0 && position < mDataList.size()) {
mDataList.set(position, newData);
notifyItemChanged(position);
}
}
/**
* 更新指定位置的数据项的特定部分并刷新该项视图
*
* @param position 要更新的数据项的位置
* @param newData 新的数据项
* @param payload 用于指示更新内容的标识
*/
public void updateItem(int position, T newData, Object payload) {
if (position >= 0 && position < mDataList.size()) {
mDataList.set(position, newData);
notifyItemChanged(position, payload);
}
}
@NonNull
@Override
public BaseViewHolder<VB> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mViewBinding = onCreateViewBinding(mInflater, parent, viewType);
return new BaseViewHolder<>(mViewBinding);
}
// @Override
// public void onBindViewHolder(@NonNull BaseViewHolder<VB> holder, int position) {
// mData = getItem(position);
// onBindData(holder.binding, mData, position);
//
// // 设置点击事件监听(如果设置了监听器)
// if (mListener != null) {
// holder.itemView.setOnClickListener(v -> mListener.onItemClick(v, mData, position));
// holder.itemView.setOnLongClickListener(v -> {
// mListener.onItemLongClick(v, mData, position);
// return true;
// });
// }
// }
@Override
public void onBindViewHolder(@NonNull BaseViewHolder<VB> holder, int position, @NonNull List<Object> payloads) {
mData = getItem(position);
onBindData(holder.binding, mData, position, payloads);
// 设置点击事件监听(如果设置了监听器)
if (mListener != null) {
holder.itemView.setOnClickListener(v -> mListener.onItemClick(v, getItem(position), position));
holder.itemView.setOnLongClickListener(v -> {
mListener.onItemLongClick(v, getItem(position), position);
return true;
});
}
}
@Override
public void onBindViewHolder(@NonNull BaseViewHolder<VB> holder, int position) {
}
@Override
public int getItemCount() {
return mDataList != null ? mDataList.size() : 0;
}
/**
* 子类实现:创建 ViewBinding
*/
protected abstract VB onCreateViewBinding(LayoutInflater inflater, ViewGroup parent, int viewType);
/**
* 子类实现:绑定数据
*/
protected abstract void onBindData(VB binding, T data, int position, @NonNull List<Object> payloads);
/**
* 通用 ViewHolder,持有 ViewBinding 对象
*/
public static class BaseViewHolder<VB extends ViewBinding> extends RecyclerView.ViewHolder {
public final VB binding;
public BaseViewHolder(@NonNull VB binding) {
super(binding.getRoot());
this.binding = binding;
}
}
/**
* Item 点击事件接口(含点击和长按)
*/
public interface OnItemClickListener<T> {
void onItemClick(View view, T data, int position);
void onItemLongClick(View view, T data, int position);
}
public OnItemClickListener<T> mListener;
/**
* 设置点击事件监听器
*/
public void setOnItemClickListener(OnItemClickListener<T> listener) {
this.mListener = listener;
}
@Override
public void onViewRecycled(@NonNull BaseViewHolder<VB> holder) {
super.onViewRecycled(holder);
}
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
}
五、优势总结
1. 通用性强:支持任何数据类型和 ViewBinding 布局
2. 功能完整:数据操作、点击事件、局部刷新、顶部插入等功能齐全
3. 减少重复代码:子类只需实现创建和绑定数据
4. 性能优化:支持 Payload 局部刷新,避免全量刷新
5. 适用场景广:聊天列表、消息流、商品列表、动态数据列表等