ViewHolder
# 什么是ViewHolder
要想使用 ListView 就需要编写一个 Adapter 将数据适配到 ListView上,而为了节省资源提高运行效率,一般自定义类 ViewHolder 来减少 findViewById() 的使用以及避免过多地 inflate view,从而实现目标。
findViewById()
通过将结果缓存在Holder对象中,它可以帮助您最大程度地减少调用次数。- 如果使用ListViews和RecyclerViews,所有视图都使用一个布局文件,则
findViewById()
一次又一次地访问可能很耗时。 ViewHolder
可以通过为每个listItem提供一个标记来减少这种情况,这样您就可以立即访问它们,而无需重复查找它们。
# ViewHolder优化
将findViewById()
反射出来的控件对象进行存储
ViewHolde类:存储需要赋值的控件(可定义为Adapter内部类)
public class ViewHolder {
ImageView logo;
TextView app_name;
TextView app_version;
TextView app_package;
}
1
2
3
4
5
6
2
3
4
5
6
ViewHolder的使用
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// [二级优化] ViewHolder
ViewHolder holder = null;
// [一级优化] convertView减少inflate的次数
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_view, null);
// 获取控件
holder = new ViewHolder();
holder.logo = convertView.findViewById(R.id.logo); // ImageView
holder.app_name = convertView.findViewById(R.id.app_name); // TextView
//
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.logo.setImageResource((Integer) list.get(position).get("logo"));
holder.app_name.setText((String) list.get(position).get("title"));
// 返回实实在在的view供系统渲染item
return convertView;
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ViewHolder原理
将ViewHolder作为整体放入缓存池,类中包含的是findViewById
反射出来的控件对象;
item7
在复用 item1
时,还携带了 item1
反射好的控件对象(除了当前的布局视图view对象之外,还包含ImageView和TextView控件对象),大大减少了反射执行的操作次数
# setTag作行李
setTag()
并不是专门针对ListView优化的方法,可以让当前对象携带任何其他类型的对象(Object)
- 将
findViewById
查找出来的控件捆绑到一起放到ViewHolder上,将ViewHolder作为一个 行李 让 ConvertView 来携带;因为convertView对象不进入到缓存池,当下一个item
复用时再将ViewHolder行李交给新的 item
去教室上课时要背书包,上完课后回来将书包放下
# ListView优化
Adapter Demo
/**
* @Author: iqqcode
* @Date: 2021-04-30 21:53
* @Description: ListView-Adapter的优化
*/
public class MyBaseAdapter extends BaseAdapter {
private List<AppInfo> list; // 乘方数据源的容器
// 反射器: 将item的XML布局文件反射成为View
private LayoutInflater inflater;
public MyBaseAdapter(Context context, List<AppInfo> list) {
// 初始化上下文
this.inflater = LayoutInflater.from(context);
this.list = list;
}
@Override
public int getCount() {
return list == null ? 0 : list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// [二级优化] ViewHolder
ViewHolder holder = null;
// [一级优化] convertView减少inflate的次数
if (convertView == null) {
Log.e("TAG", "getView() *** position = " + position + " convertView = " + convertView);
// 此处传递this是Adapter的对象
// 反射器: 将item的XML布局文件反射成为View
// 1. 拿到view视图对象,从item布局文件反射器获取而来(XML文件映射成为内存中实际存在的View对象)
// 父容器对象不需要传递,因为仅仅是展示
convertView = inflater.inflate(R.layout.item_view, null);
// 2. 获取控件
holder = new ViewHolder();
holder.logo = convertView.findViewById(R.id.item_image);
holder.app_name = convertView.findViewById(R.id.item_text_app_name);
holder.app_size = convertView.findViewById(R.id.item_text_app_size);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 每个item加载的内容是不同的,所以需要重复获取(无法优化)
AppInfo info = new AppInfo();
holder.logo.setImageResource(list.get(position).getAppIcon());
holder.app_name.setText(list.get(position).getAppName());
holder.app_size.setText(list.get(position).getAppSize());
// 返回实实在在的view供系统渲染item
return convertView;
}
}
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
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
编辑 (opens new window)
上次更新: 2022/03/27, 22:58:50