安卓基础(适配器和RecyclerView )

发布于:2025-05-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

RecyclerView 

// 🍎 第一步:准备水果数据(假装这是你的数据)
List<String> fruitList = new ArrayList<>();
fruitList.add("苹果");
fruitList.add("香蕉");
fruitList.add("橘子"); // ...继续添加其他水果

// 🚚 第二步:创建 RecyclerView 的 Adapter(卡车司机)
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.FruitHolder> {
    private List<String> fruits;

    // 构造函数:传入水果数据
    public FruitAdapter(List<String> fruits) {
        this.fruits = fruits;
    }

    // 🛠️ 工作1:创建装水果的盒子(ViewHolder)
    @NonNull
    @Override
    public FruitHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // 用布局文件 item_fruit.xml 生成一个盒子
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_fruit, parent, false);
        return new FruitHolder(view);
    }

    // 🎁 工作2:把水果放进盒子
    @Override
    public void onBindViewHolder(@NonNull FruitHolder holder, int position) {
        String fruit = fruits.get(position);
        holder.fruitName.setText(fruit); // 设置水果名字
    }

    // 📊 工作3:告诉卡车有多少水果要运
    @Override
    public int getItemCount() {
        return fruits.size();
    }

    // 📦 盒子的设计(静态内部类,避免内存泄漏)
    public static class FruitHolder extends RecyclerView.ViewHolder {
        TextView fruitName;

        public FruitHolder(@NonNull View itemView) {
            super(itemView);
            fruitName = itemView.findViewById(R.id.tv_fruit_name); // 找到盒子里的文字区域
        }
    }
}

// 🌟 第三步:在 Activity 中绑定 RecyclerView
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 设置排列方式(竖着排)
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);

🧩 配套的布局文件(item_fruit.xml

<!-- 每个水果盒子的样式 -->
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_fruit_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="20dp"
    android:textSize="18sp"/>

🌈 代码比喻

  • FruitAdapter​:就像快递公司的 ​​“装箱机器人”​​,负责把水果装进标准盒子里。
  • onCreateViewHolder​:机器人第一次看到盒子时,​​从模板(item_fruit.xml)造新盒子​​。
  • onBindViewHolder​:机器人把具体的水果(数据)塞进盒子里。
  • FruitHolder​:盒子的设计图纸,规定每个盒子长什么样。

下次滑动列表时,记得对手机说:“机器人又在回收盒子啦!” 😆

  • ​Adapter 是乐高大师​​:把乱糟糟的积木变成漂亮的小火车!
  • ​RecyclerView 是轨道​​:只放得下 5 节车厢,但 Adapter 会偷偷回收滑出屏幕的车厢,改装成新车厢~

🚚 ​​Adapter 代码(Java版)​

// 🍎 快递员:水果搬运工(Adapter)
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.FruitHolder> {

    // 三个重要东西:
    private Context context;       // 📍 位置(在哪显示)
    private List<String> fruits;   // 🍇 水果清单(数据)
    private ItemClickListener listener; // 🖐️ 小手(点击监听)

    // 快递员的工具包(构造函数)
    public FruitAdapter(Context context, List<String> fruits, ItemClickListener listener) {
        this.context = context;
        this.fruits = fruits;
        this.listener = listener;
    }

    // 🌟 超能力1:造车厢!(用模板拼装)
    @Override
    public FruitHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 用图纸 item_fruit.xml 造车厢
        View view = LayoutInflater.from(context).inflate(R.layout.item_fruit, parent, false);
        return new FruitHolder(view); // 返回一个装好的车厢
    }

    // 🌟 超能力2:贴水果标签!(绑定数据)
    @Override
    public void onBindViewHolder(FruitHolder holder, int position) {
        String fruit = fruits.get(position); // 拿到第几个水果
        holder.tvFruit.setText(fruit); // 贴在车厢上

        // 点击车厢时,告诉"小手"(监听器)
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemClick(position);
            }
        });
    }

    // 🌟 超能力3:数水果!(告诉火车有多少节)
    @Override
    public int getItemCount() {
        return fruits.size();
    }

    // 🚂 车厢设计图(ViewHolder)
    public static class FruitHolder extends RecyclerView.ViewHolder {
        TextView tvFruit; // 车厢里的文字标签

        public FruitHolder(View itemView) {
            super(itemView);
            tvFruit = itemView.findViewById(R.id.tv_fruit); // 找到标签的位置
        }
    }

    // 🖐️ 小手接口(处理点击)
    public interface ItemClickListener {
        void onItemClick(int position);
    }
}

📦 ​​配套的 XML 布局(item_fruit.xml)​

<!-- 每个小车厢的样式 -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- 显示水果名字的标签 -->
    <TextView
        android:id="@+id/tv_fruit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000000"/>

</LinearLayout>

🌟 ​getItemCount() 的作用​

就是快递员对着包裹山大喊:​​“老板!今天要送多少件货啊?”​
然后老板回答:​​“有 3 个水果包裹,5 本书包裹,总共 8 件!”​

在代码中,这个方法告诉 RecyclerView ​​需要显示多少项数据​​!

public class SimpleAdapter extends RecyclerView.Adapter<...> {
    private List<String> dataList; // 数据源(比如水果列表)

    @Override
    public int getItemCount() {
        // 返回数据总数,比如 dataList 有 10 个水果,就显示 10 项
        return dataList.size(); 
    }
}

💥 ​​如果忘记写这个方法(或者返回 0)​

RecyclerView 会懵圈:​​“到底要显示多少项?0 项?那我不显示了!”​
结果就是 ​​空白页面​​,什么也看不到!

🌈 ​​一句话总结​

getItemCount() = 告诉 RecyclerView 数据有多少条,一条数据对应一个列表项!


// 🐯 动物园动物信息适配器(处理两种类型:食肉动物、食草动物)
public class ZooAdapter extends RecyclerView.Adapter<ZooAdapter.AnimalHolder> {
    
    // 点击监听接口
    public interface OnAnimalClickListener {
        void onAnimalClick(int position); // 点击动物项
    }
    
    // 数据
    private List<Object> animals; // 混合类型数据(Lion/Tiger 或 Deer)
    private OnAnimalClickListener listener;

    public ZooAdapter(List<Object> animals, OnAnimalClickListener listener) {
        this.animals = animals;
        this.listener = listener;
    }

    // 🦁 类型判断
    @Override
    public int getItemViewType(int position) {
        Object animal = animals.get(position);
        if (animal instanceof Lion || animal instanceof Tiger) {
            return 0; // 类型0:食肉动物
        } else {
            return 1; // 类型1:食草动物
        }
    }

    // 🛠️ 创建视图(两种不同布局)
    @NonNull
    @Override
    public AnimalHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view;
        if (viewType == 0) {
            view = inflater.inflate(R.layout.item_carnivore, parent, false); // 食肉动物布局
        } else {
            view = inflater.inflate(R.layout.item_herbivore, parent, false); // 食草动物布局
        }
        return new AnimalHolder(view);
    }

    // 🎨 绑定数据
    @Override
    public void onBindViewHolder(@NonNull AnimalHolder holder, int position) {
        Object animal = animals.get(position);
        
        if (animal instanceof Lion) {
            Lion lion = (Lion) animal;
            holder.bindCarnivore(lion.getName(), "🦁 食肉动物");
        } else if (animal instanceof Tiger) {
            Tiger tiger = (Tiger) animal;
            holder.bindCarnivore(tiger.getName(), "🐯 食肉动物");
        } else if (animal instanceof Deer) {
            Deer deer = (Deer) animal;
            holder.bindHerbivore(deer.getName(), "🌿 每天吃草");
        }

        // 点击事件
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onAnimalClick(position);
            }
        });
    }

    // 📊 数据总数
    @Override
    public int getItemCount() {
        return animals.size();
    }

    // 🐾 视图承载器
    static class AnimalHolder extends RecyclerView.ViewHolder {
        TextView tvName;
        TextView tvType;
        ImageView ivIcon;

        public AnimalHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
            tvType = itemView.findViewById(R.id.tvType);
            ivIcon = itemView.findViewById(R.id.ivIcon);
        }

        // 绑定食肉动物数据
        void bindCarnivore(String name, String type) {
            tvName.setText(name);
            tvType.setText(type);
            ivIcon.setImageResource(R.drawable.ic_lion); // 狮子图标
        }

        // 绑定食草动物数据
        void bindHerbivore(String name, String diet) {
            tvName.setText(name);
            tvType.setText(diet);
            ivIcon.setImageResource(R.drawable.ic_deer); // 鹿图标
        }
    }
}

🧩 ​​对应数据结构模型​

// 🦁 食肉动物基类
abstract class Carnivore {
    abstract String getName();
}

class Lion extends Carnivore {
    @Override String getName() { return "辛巴 Lion"; }
}

class Tiger extends Carnivore {
    @Override String getName() { return "理查德 Tiger"; }
}

// 🦌 食草动物
class Deer {
    String getName() { return "斑比 Deer"; }
}

📦 ​​布局文件示例(item_carnivore.xml)​

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:background="#FFF0F0"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/ivIcon"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@drawable/ic_lion"/>

    <LinearLayout android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="8dp">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:textColor="#FF0000"/>

        <TextView
            android:id="@+id/tvType"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="14sp"/>
    </LinearLayout>
</LinearLayout>

🎮 ​​使用示例​

List<Object> animals = new ArrayList<>();
animals.add(new Lion());
animals.add(new Deer());
animals.add(new Tiger());

ZooAdapter adapter = new ZooAdapter(animals, position -> {
    Toast.makeText(this, "点击了:" + position, Toast.LENGTH_SHORT).show();
});

recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

通过这个动物园例子,可以清晰看到适配器的核心逻辑:
1️⃣ ​​分类数据​​ → 判断是狮子还是鹿
2️⃣ ​​选择模板​​ → 用不同布局文件
3️⃣ ​​填充内容​​ → 设置名字和图标
4️⃣ ​​处理交互​​ → 点击反馈

就像动物园管理员根据动物种类,选择不同的展示牌和饲养方式 🦁→🌿


RecyclerView.ViewHolder
  • 问题背景​​:
    在列表滚动时,若每次绑定数据都通过 findViewById 查找子视图(如 TextViewButton),会频繁触发资源查找和对象创建,导致性能下降(卡顿)。

  • ​ViewHolder 的解决方案​​:
    将列表项的子视图(如 TextView)​​缓存​​在 ViewHolder 中。
    这样在滚动时,只需在 ​​首次创建列表项​​ 时调用 findViewById,后续滚动时直接复用缓存的视图组件,避免重复查找。

  • ​RecyclerView 的复用机制​​:
    当列表项滚出屏幕时,其对应的 ViewHolder 会被放入 ​​回收池​​(Recycler Pool)。当新列表项需要显示时,优先从回收池中获取旧的 ViewHolder 并绑定新数据(无需重新创建视图)。

  • ​关键方法​​:

    • onCreateViewHolder():仅当没有可复用的 ViewHolder 时调用,用于首次创建。
    • onBindViewHolder():每次数据绑定时调用,复用已有的 ViewHolder
​方法​ onCreateViewHolder onBindViewHolder
​核心职责​ 创建列表项的视图和 ViewHolder 将数据绑定到视图
​调用频率​ 低(仅在需要新视图时调用) 高(每次数据变化或滚动时都可能调用)
​性能优化点​ 减少视图创建次数(通过复用 ViewHolder 减少 findViewById 调用(通过 ViewHolder 缓存视图)
​典型操作​ 初始化布局文件,创建 ViewHolder 设置文本、图片、点击事件等
​是否强制实现​

RecyclerView.Adapter 是 ​​连接数据与 RecyclerView 列表的桥梁​​,它的核心作用是 ​​管理列表项的显示逻辑和数据绑定​​。

通过 Adapter 通知 RecyclerView 刷新界面。

​问题​ ​原因​ ​解决方案​
列表显示空白 getItemCount() 返回 0 或数据未正确传递 检查数据源是否为空,确保正确赋值给 Adapter
滚动卡顿 onBindViewHolder 中有耗时操作 避免在数据绑定时执行复杂逻辑(如网络请求)。
视图状态错乱(复用问题) 未在 onBindViewHolder 中重置视图状态 确保每次绑定数据时更新所有相关视图属性。

网站公告

今日签到

点亮在社区的每一天
去签到