RecyclerView 是 Android 开发中用于高效展示大量数据的视图组件,它通过回收和复用视图来优化内存使用,提升性能。
基本概念和优势
- 作用:替代 ListView/GridView,高效展示大数据列表。
- 优势:
- 视图复用机制更高效(ViewHolder 强制使用)。
- 支持多种布局管理器(线性、网格、瀑布流)。
- 内置动画支持(添加、删除、移动项)。
- 可定制的 ItemDecoration 和 ItemAnimator。
核心组件详解
-
RecyclerView 本身
- 继承自 ViewGroup,负责管理 ViewHolder 的缓存池(Recycler)。
-
Adapter(适配器)
-
核心职责:创建 ViewHolder、绑定数据、管理 item 类型。
-
关键方法:
onCreateViewHolder() // 创建新的 ViewHolder onBindViewHolder() // 将数据绑定到 ViewHolder getItemCount() // 返回数据总数 getItemViewType() // 支持多种 item 类型(可选)
-
-
ViewHolder(视图持有者)
- 作用:缓存 View 引用,避免频繁 findViewById。
- 实现方式:必须继承 RecyclerView.ViewHolder。
-
LayoutManager(布局管理器)
-
内置实现:
LinearLayoutManager(线性布局)GridLayoutManager(网格布局)StaggeredGridLayoutManager(瀑布流布局)
-
自定义要求:需继承
RecyclerView.LayoutManager。
-
-
ItemDecoration(装饰器)
-
用途:绘制分隔线、边距、悬浮标签等。
-
关键方法:
onDraw() // 在 item 绘制前调用 onDrawOver() // 在 item 绘制后调用 getItemOffsets() // 设置 item 的偏移量
-
-
ItemAnimator(动画器)
- 默认实现:
DefaultItemAnimator支持添加、删除、移动动画。 - 自定义方式:继承
SimpleItemAnimator或RecyclerView.ItemAnimator。
- 默认实现:
基本使用流程
1. 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
2. Activity/Fragment 初始化
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 初始化模拟数据
dataList = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
dataList.add("填充模拟数据 - " + i);
}
recyclerView.setAdapter(new MyAdapter(dataList));
3. Adapter 实现步骤
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<String> dataList;
// 构造函数
public MyAdapter(List<String> dataList) { this.dataList = dataList; }
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_layout, parent, false);
return new MyViewHolder(view);
}
// 绑定数据
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.textView.setText(dataList.get(position));
}
// 返回数据量
@Override
public int getItemCount() { return dataList.size(); }
// ViewHolder 静态内部类
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
}
}
}
内置布局实现
RecyclerView 提供了三种常见的布局管理器来实现不同的布局方式:
1. LinearLayoutManager
用于实现垂直或水平的线性布局。
// 创建垂直线性布局
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// 创建水平线性布局
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
2. GridLayoutManager
用于实现网格布局。
// 创建网格布局,每行显示3个item
GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
recyclerView.setLayoutManager(layoutManager);
3. StaggeredGridLayoutManager
用于实现瀑布流布局。
// 创建瀑布流布局,每行显示3个item
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
- 布局方向设置:
LinearLayoutManager可通过设置setOrientation方法来选择垂直或水平方向。 - 网格布局的跨度设置:
GridLayoutManager和StaggeredGridLayoutManager支持设置跨列特性,通过实现GridLayoutManager.SpanSizeLookup可以自定义每个 item 的跨度。 - 性能优化:在使用网格布局时,确保数据集较大时进行适当的优化,合理设置每个 item 的大小和加载策略,避免过度计算和渲染。
进阶功能
1. 多种 item 类型支持
- 重写
getItemViewType()返回不同类型值,在onCreateViewHolder()中创建对应 ViewHolder。
public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ItemData> dataList;
public MultiTypeAdapter(List<ItemData> dataList) {
this.dataList = dataList;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if(viewType == ItemData.TYPE_TEXT){
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_text, parent, false);
return new TextViewHolder(view);
}else if(viewType == ItemData.TYPE_IMAGE){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ItemData itemData = dataList.get(position);
if (holder instanceof TextViewHolder) {
// 绑定文本数据
((TextViewHolder) holder).textView.setText(itemData.getContent());
} else if (holder instanceof ImageViewHolder) {
// 绑定图片数据
// 这里可以设置图片资源等
((ImageViewHolder) holder).imageView.setImageResource(R.drawable.img);
}
}
// 重写 getItemViewType 方法,返回不同的类型值
// 如果不重写这个方法不会生效
@Override
public int getItemViewType(int position) {
return dataList.get(position).getType();
}
@Override
public int getItemCount() {
return dataList.size(); // 返回数据项的数量
}
// 这里可以定义不同类型的 ViewHolder
public static class TextViewHolder extends RecyclerView.ViewHolder {
// 定义 ViewHolder 的视图组件
TextView textView;
public TextViewHolder(View itemView) {
super(itemView);
// 初始化视图组件
textView = itemView.findViewById(R.id.textView);
}
}
public static class ImageViewHolder extends RecyclerView.ViewHolder {
// 定义 ViewHolder 的视图组件
ImageView imageView;
public ImageViewHolder(View itemView) {
super(itemView);
// 初始化视图组件
imageView = itemView.findViewById(R.id.imageView);
}
}
}
2. 点击 / 长按事件处理
- 在 ViewHolder 构造函数中设置监听器,通过
getAdapterPosition()获取位置。
public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ItemData> dataList;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
public MultiTypeAdapter(List<ItemData> dataList) {
this.dataList = dataList;
}
@Override
public int getItemViewType(int position) {
return dataList.get(position).getType();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ItemData.TYPE_TEXT) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false);
return new TextViewHolder(view);
} else if (viewType == ItemData.TYPE_IMAGE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ItemData item = dataList.get(position);
if (holder instanceof TextViewHolder) {
((TextViewHolder) holder).textView.setText(item.getContent());
} else if (holder instanceof ImageViewHolder) {
((ImageViewHolder) holder).imageView.setImageResource(R.drawable.img);
}
}
@Override
public int getItemCount() {
return dataList.size();
}
// 定义不同类型的 ViewHolder
public class TextViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public TextViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
// 设置点击事件监听器
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
// 设置长按事件监听器
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true; // 返回 true 表示事件已消费
});
}
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
public ImageViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
// 设置点击事件监听器
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
// 设置长按事件监听器
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true; // 返回 true 表示事件已消费
});
}
}
// 设置点击事件接口
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
// 设置长按事件接口
public interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
// 设置点击事件监听器
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
// 设置长按事件监听器
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
this.onItemLongClickListener = listener;
}
}
3. 添加 Header/Footer
- 通过
getItemViewType()区分 Header/Footer 类型,单独处理布局。
public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = -2;
private static final int TYPE_FOOTER = -1;
private static final int TYPE_TEXT = 0;
private static final int TYPE_IMAGE = 1;
private List<ItemData> dataList;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
public MultiTypeAdapter(List<ItemData> dataList) {
this.dataList = dataList;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEADER;
} else if (position == getItemCount() - 1) {
return TYPE_FOOTER;
} else {
return dataList.get(position - 1).getType();
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
View headerView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_header, parent, false);
return new HeaderViewHolder(headerView);
case TYPE_FOOTER:
View footerView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer, parent, false);
return new FooterViewHolder(footerView);
case TYPE_TEXT:
View textView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false);
return new TextViewHolder(textView);
case TYPE_IMAGE:
View imageView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(imageView);
default:
return null;
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case TYPE_HEADER:
// Header 的绑定逻辑
break;
case TYPE_FOOTER:
// Footer 的绑定逻辑
break;
case TYPE_TEXT:
ItemData textItem = dataList.get(position - 1);
((TextViewHolder) holder).textView.setText(textItem.getContent());
break;
case TYPE_IMAGE:
ItemData imageItem = dataList.get(position - 1);
((ImageViewHolder) holder).imageView.setImageResource(R.drawable.img);
break;
}
}
@Override
public int getItemCount() {
return dataList.size() + 2; // 包括 Header 和 Footer
}
// 定义 Header 和 Footer 的 ViewHolder
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(@NonNull View itemView) {
super(itemView);
// 初始化 Header 的视图组件
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
public class FooterViewHolder extends RecyclerView.ViewHolder {
public FooterViewHolder(@NonNull View itemView) {
super(itemView);
// 初始化 Footer 的视图组件
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
// 保持之前的 TextViewHolder 和 ImageViewHolder 不变
public class TextViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public TextViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
public ImageViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
// 保持之前的接口不变
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
this.onItemLongClickListener = listener;
}
}
4. 数据更新与动画
- 使用
notifyItemInserted()/notifyItemRemoved()触发精细动画,避免notifyDataSetChanged()全量刷新。
public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = -2;
private static final int TYPE_FOOTER = -1;
private static final int TYPE_TEXT = 0;
private static final int TYPE_IMAGE = 1;
private List<ItemData> dataList;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
public MultiTypeAdapter(List<ItemData> dataList) {
this.dataList = dataList;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEADER;
} else if (position == getItemCount() - 1) {
return TYPE_FOOTER;
} else {
return dataList.get(position - 1).getType();
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
View headerView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_header, parent, false);
return new HeaderViewHolder(headerView);
case TYPE_FOOTER:
View footerView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer, parent, false);
return new FooterViewHolder(footerView);
case TYPE_TEXT:
View textView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false);
return new TextViewHolder(textView);
case TYPE_IMAGE:
View imageView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(imageView);
default:
return null;
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case TYPE_HEADER:
// Header 的绑定逻辑
break;
case TYPE_FOOTER:
// Footer 的绑定逻辑
break;
case TYPE_TEXT:
ItemData textItem = dataList.get(position - 1);
((TextViewHolder) holder).textView.setText(textItem.getContent());
break;
case TYPE_IMAGE:
ItemData imageItem = dataList.get(position - 1);
((ImageViewHolder) holder).imageView.setImageResource(R.drawable.img);
break;
}
}
@Override
public int getItemCount() {
return dataList.size() + 2; // 包括 Header 和 Footer
}
public void addItem(ItemData itemData,int position) {
if(position >= 0 && position <= dataList.size()){
dataList.add(position,itemData);
notifyItemInserted(position + 1); // +1 是因为 position 0 是 Header
}
}
public void removeItem(int position){
if(position >= 1 && position <= dataList.size()){
dataList.remove(position - 1); // -1 是因为 position 0 是 Header
notifyItemRemoved(position);
}
}
// 定义 Header 和 Footer 的 ViewHolder
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(@NonNull View itemView) {
super(itemView);
// 初始化 Header 的视图组件
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
public class FooterViewHolder extends RecyclerView.ViewHolder {
public FooterViewHolder(@NonNull View itemView) {
super(itemView);
// 初始化 Footer 的视图组件
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
// 保持之前的 TextViewHolder 和 ImageViewHolder 不变
public class TextViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public TextViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
public ImageViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
// 保持之前的接口不变
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
this.onItemLongClickListener = listener;
}
}
5. 嵌套滚动
- 配合
NestedScrollView或CoordinatorLayout使用,需调用setNestedScrollingEnabled(false)优化性能。
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/addButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加数据" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
// 禁用 RecyclerView 的嵌套滚动功能
recyclerView.setNestedScrollingEnabled(false);
性能优化
1. DiffUtil 高效更新
DiffUtil 是 Android 提供的一个工具类,用于计算新旧数据列表之间的差异。通过使用 DiffUtil,可以高效地更新 RecyclerView 的数据,只刷新变化的 item,而不是全量刷新整个列表。这种方式可以显著提高性能,特别是在数据集较大时。
2. 视图预加载
在使用 RecyclerView 时,LinearLayoutManager 默认已开启视图预加载功能,可以通过 setInitialPrefetchItemCount 方法来调整预加载的数量,从而优化性能。
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setInitialPrefetchItemCount(2);// 预加载2个item
recyclerView.setLayoutManager(linearLayoutManager);
- 提升滚动流畅性:提前加载部分不可见项目,在用户滚动时减少因突然加载新内容而导致的卡顿。
- 优化用户体验:使滚动操作更加平滑,内容展示更加及时。
3. 避免内存泄漏
Adapter 中使用弱引用持有 Context,避免 Activity/Fragment 无法释放。
public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = -2;
private static final int TYPE_FOOTER = -1;
private static final int TYPE_TEXT = 0;
private static final int TYPE_IMAGE = 1;
private WeakReference<Context> contextRef;
private List<ItemData> dataList;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
public MultiTypeAdapter(Context context, List<ItemData> dataList) {
this.contextRef = new WeakReference<>(context);
this.dataList = dataList;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEADER;
} else if (position == getItemCount() - 1) {
return TYPE_FOOTER;
} else {
return dataList.get(position - 1).getType();
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Context context = contextRef.get();
if (context == null) {
throw new IllegalStateException("Context is no longer available");
}
switch (viewType) {
case TYPE_HEADER:
View headerView = LayoutInflater.from(context).inflate(R.layout.item_header, parent, false);
return new HeaderViewHolder(headerView);
case TYPE_FOOTER:
View footerView = LayoutInflater.from(context).inflate(R.layout.item_footer, parent, false);
return new FooterViewHolder(footerView);
case TYPE_TEXT:
View textView = LayoutInflater.from(context).inflate(R.layout.item_text, parent, false);
return new TextViewHolder(textView);
case TYPE_IMAGE:
View imageView = LayoutInflater.from(context).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(imageView);
default:
return null;
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case TYPE_HEADER:
// Header 的绑定逻辑
break;
case TYPE_FOOTER:
// Footer 的绑定逻辑
break;
case TYPE_TEXT:
ItemData textItem = dataList.get(position - 1);
((TextViewHolder) holder).textView.setText(textItem.getContent());
break;
case TYPE_IMAGE:
ItemData imageItem = dataList.get(position - 1);
((ImageViewHolder) holder).imageView.setImageResource(R.drawable.img);
break;
}
}
@Override
public int getItemCount() {
return dataList.size() + 2; // 包括 Header 和 Footer
}
public void addItem(ItemData itemData, int position) {
if (position >= 0 && position <= dataList.size()) {
dataList.add(position, itemData);
notifyItemInserted(position + 1); // +1 是因为 position 0 是 Header
}
}
public void removeItem(int position) {
if (position >= 1 && position <= dataList.size()) {
dataList.remove(position - 1); // -1 是因为 position 0 是 Header
notifyItemRemoved(position);
}
}
// 定义 Header 和 Footer 的 ViewHolder
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(@NonNull View itemView) {
super(itemView);
// 初始化 Header 的视图组件
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
public class FooterViewHolder extends RecyclerView.ViewHolder {
public FooterViewHolder(@NonNull View itemView) {
super(itemView);
// 初始化 Footer 的视图组件
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
// 保持之前的 TextViewHolder 和 ImageViewHolder 不变
public class TextViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public TextViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
public ImageViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
onItemClickListener.onItemClick(itemView, position);
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onItemLongClickListener != null) {
onItemLongClickListener.onItemLongClick(itemView, position);
}
return true;
});
}
}
// 保持之前的接口不变
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
public interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
this.onItemLongClickListener = listener;
}
}
优点
- 防止内存泄漏:通过使用弱引用来引用
Context,确保当Activity或Fragment被销毁时,Adapter不会继续持有对它们的强引用,从而避免内存泄漏。 - 灵活性:可以随时检查
Context是否仍然有效,从而避免在Context已被销毁后尝试访问其资源。
注意事项
- 确保
Context有效性:在使用Context之前,必须检查WeakReference.get()返回的Context是否为null。 - 合理使用:仅在确实需要
Context的场景中使用弱引用来引用它。如果Adapter的生命周期与Context的生命周期一致,可能不需要使用弱引用。
4. 图片加载优化
在 RecyclerView 的滑动监听器中,根据滑动状态调用 Glide.with(context).pauseRequests() 暂停图片加载,滑动停止时调用 Glide.with(context).resumeRequests() 恢复图片加载
public class RecyclerViewUtils {
/**
* @desc 在RecyclerView滑动时停止加载图片,在滑动停止时开始加载图片(基于RecyclerView跟Glide)
*/
public static void ScrollSuspend(Context context, RecyclerView recyclerView) {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) {
Glide.with(context).pauseRequests();
} else if (newState == RecyclerView.SCROLL_STATE_IDLE) {
Glide.with(context).resumeRequests();
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
}
}
RecyclerViewUtils.ScrollSuspend(context, recyclerView);
- 生命周期管理:Glide 对每个页面维护了一个单独的
RequestManager。当Activity或Fragment的生命周期状态变化时,RequestManager会自动暂停或恢复图片加载请求。例如,在onStop()方法中会暂停所有图片加载请求,在onDestroy()方法中会取消所有未完成的图片加载请求,避免资源浪费和内存泄漏。 - 适用场景:适用于需要加载大量图片或频繁切换页面的场景,可以有效避免不必要的资源消耗和内存泄漏
常见问题及解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| RecyclerView 高度异常 | LayoutManager 未正确测量或 Adapter 返回的 item 数量为 0。 | 检查 LayoutManager 配置,确保 Adapter 的 getItemCount() 返回正确值。 |
| ViewHolder 复用导致数据错乱 | 未在 onBindViewHolder() 中重置所有视图状态(如 checkbox 状态、图片加载)。 | 在 onBindViewHolder() 中显式设置所有视图属性,避免复用残留状态。 |
| 滚动卡顿 | 图片加载阻塞主线程、onBindViewHolder() 执行耗时操作。 | 使用异步图片加载库(如 Glide),避免在 onBindViewHolder() 中进行复杂计算。 |
| 嵌套 RecyclerView 滑动冲突 | 子 RecyclerView 拦截了触摸事件。 | 在父布局中重写 onInterceptTouchEvent() 或调用子 RecyclerView 的 setNestedScrollingEnabled(false)。 |