ListView与RecyclerView区别总结

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 使用流程

  1. 添加依赖 :implementation 'androidx.recyclerview:recyclerview:1.3.2'

  2. 布局文件 :

    xml 复制代码
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
  3. 设置 LayoutManager :

    java 复制代码
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
  4. 实现 Adapter 和 ViewHolder(参考第2节)

  5. 绑定数据 :

    java 复制代码
    RecyclerAdapter adapter = new RecyclerAdapter(dataList);
    recyclerView.setAdapter(adapter);

7.2 ListView 使用流程

  1. 布局文件 :

    xml 复制代码
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
  2. 实现 Adapter(参考第2节)

  3. 绑定数据 :

    java 复制代码
    listView.setAdapter(adapter);

8. 关键点总结

特性 ListView RecyclerView
复用机制 需手动优化,易出错 强制 ViewHolder,内置高效复用池
布局扩展性 仅支持垂直列表 支持线性/网格/瀑布流,灵活组合
数据刷新性能 全局刷新,性能差 局部刷新 + DiffUtil,高效更新
动画支持 需自定义实现 内置动画 API,支持深度定制
开发复杂度 简单场景快速实现 初期学习成本略高,长期维护更便捷

9. 结论与选型建议

  • 优先选择 RecyclerView:适用于所有新项目,尤其在需要复杂布局、动画或高性能的场景。
  • 仅考虑 ListView:仅用于维护旧项目或极其简单的静态列表,但建议逐步迁移至 RecyclerView。

源码级优化建议:

  • 使用 RecyclerView 时,通过 setHasFixedSize(true) 提升性能(当 Item 尺寸固定时)。
  • 避免在 onBindViewHolder 中执行耗时操作(如加载图片),应使用异步加载库(如 Glide)。

通过本文的详细对比和代码实践,读者可全面掌握两者的差异与应用场景。建议收藏本文代码片段,作为日常开发的参考模板。

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work3 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work3 天前
ch21 签名、校验与发版
kotlin
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui