发布于 

ConcatAdapter源码浅析(一):RecyclerView机制回顾

分析ConcatAdapter源码之前,先来回顾一下RecyclerView的基本机制:

核心机制

RecyclerView 是 Android 开发中常用的用于展示列表和网格布局的控件。它的核心机制主要包括三个组件:LayoutManager(布局管理器)、Adapter(适配器)和ViewHolder(视图持有者)。

LayoutManager(布局管理器)负责决定RecyclerView中子项的排列方式。它负责测量和定位每个子项的位置,并决定它们如何在屏幕上展示。RecyclerView 提供了几种内置的布局管理器,如线性布局管理器(LinearLayoutManager)、网格布局管理器(GridLayoutManager)和瀑布流布局管理器(StaggeredGridLayoutManager)。开发者也可以自定义布局管理器来满足特定需求。

Adapter(适配器)负责为RecyclerView提供数据,并创建对应的ViewHolder。它将数据集绑定到视图上,并负责创建、复用和更新ViewHolder。开发者需要继承RecyclerView.Adapter类并实现必要的方法,如创建ViewHolder、绑定数据和确定数据集大小等。Adapter还可以通过添加点击事件监听器来处理用户与子项的交互。

ViewHolder(视图持有者)用于缓存RecyclerView的子项视图。当子项滚出屏幕时,ViewHolder会将其缓存起来,以便在需要时重新利用。这样可以减少视图创建和销毁的次数,提高列表的滚动性能。ViewHolder一般包含了子项视图的引用,可以通过调用findViewById等方法获取子项视图的各个组件,并将其与数据进行绑定。

使用RecyclerView时,首先需要创建一个RecyclerView实例,并设置相应的布局管理器和适配器。然后,将数据集传递给适配器,并将适配器设置到RecyclerView中。RecyclerView会根据布局管理器的要求,自动调用适配器的方法来创建、绑定和更新子项视图。

ViewHolder作用

ViewHolder 的作用是缓存子项视图的引用,以便在需要时快速访问和更新视图内容。它是 RecyclerView 在处理大量子项时提高性能的关键机制之一。

当 RecyclerView 中的子项滚出屏幕时,它们会被回收并可供后续的子项重复使用。ViewHolder 的主要作用是持有每个子项视图中的控件的引用,以便在需要时可以快速获取它们,而不必每次都通过 findViewById 等方法来查找。

通过 ViewHolder 的引用,可以直接访问和更新子项视图的各个组件,例如文本视图、图像视图等。这样可以避免每次滚动或数据更新时都重新查找视图控件,提高了列表的滚动性能和渲染效率。

ViewHolder 类通常定义为 RecyclerView.Adapter 的内部类。在适配器的 onCreateViewHolder 方法中,我们使用布局资源创建 ViewHolder,并将子项布局中的控件与 ViewHolder 的成员变量进行绑定。然后,在 onBindViewHolder 方法中,我们可以直接通过 ViewHolder 来访问和更新子项视图的内容,以展示正确的数据。

通过使用 ViewHolder,RecyclerView 在滚动和数据更新时可以更高效地重用子项视图,减少了视图的创建和销毁次数,从而提升了列表的性能和响应速度。

通过将 ViewHolder 对象与特定的子项视图绑定,RecyclerView 可以在滚动和数据更新时高效地管理和重用子项视图,以提升列表的性能和响应速度。

回收和重用机制

当 RecyclerView 中的子项滚出屏幕时,为了节省资源和提高性能,它会采用回收和重用机制来管理 ViewHolder 对象的生命周期。下面是关于 RecyclerView 回收和重用机制的详细讲解:

  1. 初次创建 ViewHolder:当 RecyclerView 需要显示新的子项时,它会调用 RecyclerView.Adapter 的 onCreateViewHolder() 方法来创建 ViewHolder 对象。在这个方法中,我们通过布局资源创建 ViewHolder,并将子项布局中的视图组件与 ViewHolder 的成员变量进行绑定。
  2. 绑定 ViewHolder 数据:在 RecyclerView.Adapter 的 onBindViewHolder() 方法中,RecyclerView 将数据绑定到 ViewHolder 对应的子项视图上。我们可以通过 ViewHolder 的引用来访问和更新子项视图的内容。
  3. 子项滚出屏幕:当子项滚出屏幕时,RecyclerView 会将对应的 ViewHolder 对象放入内部的 ViewHolder 池中,以便后续重用。这个过程发生在 RecyclerView 的布局管理器(LayoutManager)中,当子项不再可见或超出可见范围时触发。
  4. 重用 ViewHolder:当需要显示新的子项时,RecyclerView 会首先尝试从 ViewHolder 池中获取可重用的 ViewHolder 对象,而不是重新创建新的对象。它会调用 RecyclerView.Adapter 的 onBindViewHolder() 方法,将新的数据绑定到重用的 ViewHolder 上。
  5. 更新数据:在 onBindViewHolder() 方法中,我们根据新的位置(position)获取对应的数据项,并通过 ViewHolder 的引用来更新子项视图的内容。这确保了重用的 ViewHolder 显示正确的数据。

通过回收和重用机制,RecyclerView 避免了重复创建和销毁子项视图以及对应的 ViewHolder,提高了列表的渲染性能和滚动的流畅性。通过复用已有的 ViewHolder 对象,它可以减少不必要的内存分配和对象创建开销,提高应用的性能和响应速度。

需要注意的是,RecyclerView 的回收和重用机制是由其内部的布局管理器(LayoutManager)来实现和控制的。不同的布局管理器可能有不同的策略和行为,以适应不同的布局需求和子项视图的变化。因此,具体的回收和重用机制可能会因布局管理器的不同而有所差异。

ItemViewType

RecyclerView回顾最后一部分来介绍一下和我们今天这次的主角ConcatAdapter相关的ItemViewType,在很多场景下RecyclerView会包含多种类型的子项,我们可以使用 getItemViewType() 方法来返回每个子项的类型。根据子项类型的不同,我们可以在 onCreateViewHolder() 方法中创建不同类型的 ViewHolder,并在 onBindViewHolder() 方法中根据类型进行数据绑定。

进一步假设,如果在同一个RecyclerView中的这两种类型的数据获取到的时机不一样,那么我们更新数据的代码会变成类似如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Item1> itemList1;
private List<Item2> itemList2;

// 定义不同的数据类型
private static final int TYPE_ITEM1 = 1;
private static final int TYPE_ITEM2 = 2;

public MyAdapter() {
this.itemList1 = new ArrayList<>();
this.itemList2 = new ArrayList<>();
}

public void updateItem1List(List<Item1> newDataList) {
itemList1.clear();
itemList1.addAll(newDataList);
notifyDataSetChanged();
}

public void updateItem2List(List<Item2> newDataList) {
itemList2.clear();
itemList2.addAll(newDataList);
notifyDataSetChanged();
}

// ...

@Override
public int getItemViewType(int position) {
if (position < itemList1.size()) {
return TYPE_ITEM1;
} else {
return TYPE_ITEM2;
}
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
RecyclerView.ViewHolder viewHolder;

switch (viewType) {
case TYPE_ITEM1:
View itemView1 = inflater.inflate(R.layout.item_layout1, parent, false);
viewHolder = new ViewHolder1(itemView1);
break;
case TYPE_ITEM2:
View itemView2 = inflater.inflate(R.layout.item_layout2, parent, false);
viewHolder = new ViewHolder2(itemView2);
break;
default:
// 处理其他数据类型的 ViewHolder
View itemView = inflater.inflate(R.layout.default_item_layout, parent, false);
viewHolder = new DefaultViewHolder(itemView);
break;
}

return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolder1) {
((ViewHolder1) holder).bindData(itemList1.get(position));
} else if (holder instanceof ViewHolder2) {
((ViewHolder2) holder).bindData(itemList2.get(position - itemList1.size()));
} else {
// 处理其他数据类型的 ViewHolder
// ...
}
}

@Override
public int getItemCount() {
return itemList1.size() + itemList2.size();
}

// ...
}

在上述示例代码中,我们通过 updateItem1List()updateItem2List() 方法分别更新 itemList1itemList2 数据集。在 onBindViewHolder() 方法中,根据 getItemViewType() 返回的类型来判断要绑定的数据是来自 itemList1 还是 itemList2

当你获取到对应类型的数据后,调用相应的更新方法,并传入新的数据列表。这样会更新对应的数据集,并自动刷新 RecyclerView 的显示。

需要注意的是,在更新数据后,你需要调用适当的通知方法(如 notifyDataSetChanged()notifyItemInserted()notifyItemRangeChanged() 等)来告知 RecyclerView 数据发生变化,以便更新 RecyclerView 的显示。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 @tsparrot 创建,使用 Stellar 作为主题。