1. 概述
在 Android 开发中,ListView
和 RecyclerView
是展示列表数据的核心控件。本文将从 代码实现 、性能优化 、功能扩展 等角度,全面对比两者的差异,并提供详细的实现步骤和最佳实践。
2. ViewHolder 模式实现对比
2.1 ListView 的 Adapter 实现(需手动优化)
java
public class ListViewAdapter extends BaseAdapter {
private List<String> mData;
private LayoutInflater mInflater;
public ListViewAdapter(Context context, List<String> data) {
mData = data;
mInflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 手动实现 ViewHolder 模式
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_list, parent, false);
holder = new ViewHolder();
holder.textView = convertView.findViewById(R.id.tv_item);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(mData.get(position));
return convertView;
}
static class ViewHolder {
TextView textView;
}
}
2.2 RecyclerView 的 Adapter 实现(强制使用 ViewHolder)
java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private List<String> mData;
public RecyclerAdapter(List<String> data) {
mData = data;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_recycler, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.textView.setText(mData.get(position));
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv_item);
}
}
}
关键点总结:
RecyclerView
强制封装 ViewHolder,避免开发者遗漏复用逻辑。ListView
需要手动优化,若未正确复用convertView
会导致性能问题。
3. 布局管理:灵活 vs 局限
3.1 RecyclerView 的多布局实现
java
// 设置线性布局(垂直)
recyclerView.setLayoutManager(new LinearLayoutManager(context));
// 设置网格布局(3列)
recyclerView.setLayoutManager(new GridLayoutManager(context, 3));
// 设置瀑布流布局(3列,垂直方向)
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
3.2 ListView 的局限性
- 原生仅支持垂直滚动,若需网格布局需自定义
Adapter
,但复用逻辑复杂且性能差。
4. 数据更新机制对比
4.1 ListView 的全局刷新
java
// 数据变更后必须全量刷新
mData.add("New Item");
adapter.notifyDataSetChanged(); // 性能低下!
4.2 RecyclerView 的局部更新
java
// 插入单条数据
mData.add(position, "New Item");
adapter.notifyItemInserted(position); // 仅更新单个项
// 使用 DiffUtil 高效刷新(推荐!)
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new MyDiffCallback(oldList, newList));
result.dispatchUpdatesTo(adapter);
DiffUtil 实现示例:
java
public class MyDiffCallback extends DiffUtil.Callback {
private List<String> oldList, newList;
public MyDiffCallback(List<String> oldList, List<String> newList) {
this.oldList = oldList;
this.newList = newList;
}
@Override
public int getOldListSize() { return oldList.size(); }
@Override
public int getNewListSize() { return newList.size(); }
@Override
public boolean areItemsTheSame(int oldPos, int newPos) {
return oldList.get(oldPos).equals(newList.get(newPos));
}
@Override
public boolean areContentsTheSame(int oldPos, int newPos) {
return oldList.get(oldPos).equals(newList.get(newPos));
}
}
5. 动画与交互实现
5.1 RecyclerView 的内置动画
java
// 默认动画(删除、插入、移动)
recyclerView.setItemAnimator(new DefaultItemAnimator());
// 自定义动画(示例:缩放动画)
public class ScaleItemAnimator extends DefaultItemAnimator {
@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
holder.itemView.setScaleX(0);
holder.itemView.setScaleY(0);
ObjectAnimator.ofPropertyValuesHolder(holder.itemView,
PropertyValuesHolder.ofFloat(View.SCALE_X, 1),
PropertyValuesHolder.ofFloat(View.SCALE_Y, 1))
.setDuration(300)
.start();
return super.animateAdd(holder);
}
}
5.2 ListView 的动画缺失
- 需手动添加动画(如使用
ObjectAnimator
实现),代码复杂且兼容性差。
6. 分隔线与点击事件
6.1 RecyclerView 的分隔线自定义
java
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Paint mPaint;
private int mDividerHeight;
public DividerItemDecoration(Context context) {
mPaint = new Paint();
mPaint.setColor(Color.GRAY);
mDividerHeight = context.getResources().getDimensionPixelSize(R.dimen.divider_height);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);
int top = child.getBottom() + ((RecyclerView.LayoutParams) child.getLayoutParams()).bottomMargin;
c.drawRect(child.getLeft(), top, child.getRight(), top + mDividerHeight, mPaint);
}
}
}
6.2 点击事件处理对比
- ListView :直接设置
OnItemClickListener
- RecyclerView :需在
ViewHolder
中绑定
java
public class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onItemClick(getAdapterPosition());
}
});
}
}
7. 使用步骤总结
7.1 RecyclerView 使用流程
-
添加依赖 :
implementation 'androidx.recyclerview:recyclerview:1.3.2'
-
布局文件 :
xml<androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"/>
-
设置 LayoutManager :
javarecyclerView.setLayoutManager(new LinearLayoutManager(this));
-
实现 Adapter 和 ViewHolder(参考第2节)
-
绑定数据 :
javaRecyclerAdapter adapter = new RecyclerAdapter(dataList); recyclerView.setAdapter(adapter);
7.2 ListView 使用流程
-
布局文件 :
xml<ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent"/>
-
实现 Adapter(参考第2节)
-
绑定数据 :
javalistView.setAdapter(adapter);
8. 关键点总结
特性 | ListView | RecyclerView |
---|---|---|
复用机制 | 需手动优化,易出错 | 强制 ViewHolder,内置高效复用池 |
布局扩展性 | 仅支持垂直列表 | 支持线性/网格/瀑布流,灵活组合 |
数据刷新性能 | 全局刷新,性能差 | 局部刷新 + DiffUtil,高效更新 |
动画支持 | 需自定义实现 | 内置动画 API,支持深度定制 |
开发复杂度 | 简单场景快速实现 | 初期学习成本略高,长期维护更便捷 |
9. 结论与选型建议
- 优先选择 RecyclerView:适用于所有新项目,尤其在需要复杂布局、动画或高性能的场景。
- 仅考虑 ListView:仅用于维护旧项目或极其简单的静态列表,但建议逐步迁移至 RecyclerView。
源码级优化建议:
- 使用
RecyclerView
时,通过setHasFixedSize(true)
提升性能(当 Item 尺寸固定时)。 - 避免在
onBindViewHolder
中执行耗时操作(如加载图片),应使用异步加载库(如 Glide)。
通过本文的详细对比和代码实践,读者可全面掌握两者的差异与应用场景。建议收藏本文代码片段,作为日常开发的参考模板。