【Android】RecyclerView 核心知识点全面解析:从基础实现到性能优化

132 阅读12分钟

RecyclerView 是 Android 开发中用于高效展示大量数据的视图组件,它通过回收和复用视图来优化内存使用,提升性能。

基本概念和优势

  • 作用:替代 ListView/GridView,高效展示大数据列表。
  • 优势
    • 视图复用机制更高效(ViewHolder 强制使用)。
    • 支持多种布局管理器(线性、网格、瀑布流)。
    • 内置动画支持(添加、删除、移动项)。
    • 可定制的 ItemDecoration 和 ItemAnimator。

核心组件详解

  1. RecyclerView 本身

    • 继承自 ViewGroup,负责管理 ViewHolder 的缓存池(Recycler)。
  2. Adapter(适配器)

    • 核心职责:创建 ViewHolder、绑定数据、管理 item 类型。

    • 关键方法

      onCreateViewHolder() // 创建新的 ViewHolder 
      onBindViewHolder() // 将数据绑定到 ViewHolder 
      getItemCount() // 返回数据总数 
      getItemViewType() // 支持多种 item 类型(可选)
      
  3. ViewHolder(视图持有者)

    • 作用:缓存 View 引用,避免频繁 findViewById。
    • 实现方式:必须继承 RecyclerView.ViewHolder。
  4. LayoutManager(布局管理器)

    • 内置实现

      • LinearLayoutManager(线性布局)
      • GridLayoutManager(网格布局)
      • StaggeredGridLayoutManager(瀑布流布局)
    • 自定义要求:需继承 RecyclerView.LayoutManager

  5. ItemDecoration(装饰器)

    • 用途:绘制分隔线、边距、悬浮标签等。

    • 关键方法

      onDraw()      // 在 item 绘制前调用
      onDrawOver()  // 在 item 绘制后调用
      getItemOffsets() // 设置 item 的偏移量
      
  6. 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 方法来选择垂直或水平方向。
  • 网格布局的跨度设置GridLayoutManagerStaggeredGridLayoutManager 支持设置跨列特性,通过实现 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;
    }
}

image.png

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,确保当 ActivityFragment 被销毁时,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。当 ActivityFragment 的生命周期状态变化时,RequestManager 会自动暂停或恢复图片加载请求。例如,在 onStop() 方法中会暂停所有图片加载请求,在 onDestroy() 方法中会取消所有未完成的图片加载请求,避免资源浪费和内存泄漏。
  • 适用场景:适用于需要加载大量图片或频繁切换页面的场景,可以有效避免不必要的资源消耗和内存泄漏

常见问题及解决方案

问题原因解决方案
RecyclerView 高度异常LayoutManager 未正确测量或 Adapter 返回的 item 数量为 0。检查 LayoutManager 配置,确保 Adapter 的 getItemCount() 返回正确值。
ViewHolder 复用导致数据错乱未在 onBindViewHolder() 中重置所有视图状态(如 checkbox 状态、图片加载)。在 onBindViewHolder() 中显式设置所有视图属性,避免复用残留状态。
滚动卡顿图片加载阻塞主线程、onBindViewHolder() 执行耗时操作。使用异步图片加载库(如 Glide),避免在 onBindViewHolder() 中进行复杂计算。
嵌套 RecyclerView 滑动冲突子 RecyclerView 拦截了触摸事件。在父布局中重写 onInterceptTouchEvent() 或调用子 RecyclerView 的 setNestedScrollingEnabled(false)