自定义Adapter
# 1. 自定义Adapter
名称 | 说明 |
---|---|
ArrayAdapter | 适用于简单的文字列表 |
SimpleAdapter | 简单的图文列表,不适用复杂的业务 |
自定义Adapter | 自定义的灵活的适配器 |
实现自定义Adapter的步骤:
- 继承
BaseAdapter
- 实现
getView()
- 关联ListView
# 自定义Adapter工作原理
需要使用 Adapter 将 集合数据 和每一个 item所对应的布局 动态适配至ListView中显示
item布局文件需要通过LayoutInflater将XML布局文件转换为 View
对象,从而设置控件的内容
Adapter作为组装者,将 Data 和 item的layout 在**getView
** 中进行组合,每一个position
就是当前 getView方法组装并且绘制出的内容,显示在ListView(ViewGroup)上
# LayoutInflater
反射器: 将item的XML布局文件 反射 成为View对象到内存中
LayoutInflater反射出行布局对象,并填充上数据内容;拿到view视图对象,从item布局文件反射器获取而来(XML文件映射成为内存中实际存在的View对象)
LayoutInflater inflater;
// 父容器对象不需要传递,因为仅仅是展示
View view = inflater.inflate(R.layout.item_view, null);
2
3
4
# 2. 存在的问题
在每次调用getView方法中都要执行2个耗时操作(反射为耗时操作)
inflate
方法(获取布局对象)findViewById
方法(获取控件对象)
# 解决
在getView方法中使用 convertView 和 ViewHolder
- inflate方法耗时:convertView解决 (一级优化)
- findViewByld方法耗时:ViewHolder解决 (二级优化)
其实就是在优化反射操作,尽可能的做到复用,避免低效的重复加载工作
# 3. item复用
item复用:ConvertVie 减少inflate的次数
在getView()
中判断是否已存在视图对象
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// [一级优化] convertView减少inflate的次数
if (convertView == null) {
Log.e("TAG", "getView() *** position = " + position + " convertView = " + convertView);
// 此处传递this是Adapter的对象
convertView = inflater.inflate(R.layout.item_view, null);
}
// 反射器: 将item的XML布局文件反射成为View
// 1. 拿到view视图对象,从item布局文件反射器获取而来(XML文件映射成为内存中实际存在的View对象)
// 父容器对象不需要传递,因为仅仅是展示
//View view = inflater.inflate(R.layout.item_view, null);
// 2. 获取控件
ImageView logo = convertView.findViewById(R.id.logo);
// 返回实实在在的view供系统渲染item
return convertView;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当视图第一次啊加载时:前4个item都是通过inflate放射而来,加载XML中每个item的视图
当item1 和 item2已经滑出屏幕时:
原理上item1 和 item2 对象应该被GC;但是为了循环复用提高效率,将item1 和 item2 对象放入到 Recycle回收池 中,然后给将要加载的 item7
和item8
来复用
item6
需要通过inflate来反射,但是item7
复用item1
,item1
对象的地址赋给item7
;后面的 item
均是循环复用
Recycle缓存屏幕显示的 n + 1 个 item
,1
是处理半个 item
的情况
# 小结
Android渲染ListView遵循以下原则:
- 无论总量多少,Android只渲染当前屏的item项
- 被渲染过的item项,它的视图对象会被保存到Recycler中
- 新滑入屏幕的item项将从Recycler中直接获取缓存的视图,而不是再通过inflate方法反射获取
结论:
- 无论总量多少,Recylcler中只需要缓存N+1个视图对象,即可保证整个ListView的显示(N为一屏可显示的item数)
- 无论总量多少,inflate方法最多调用N+1次
# 4. 事件的消化
名称 | 说明 |
---|---|
true | 表示消化事件,事件不会继续传递 |
false | 表示不消化事件,事件会继续传递下去 |
如果希望长按事件后,不再触发点击事件,则应将返回值置位true
表示在点击了长按或者点击时,长按响应了之后就消化了该点击事件,
onItemClick
不再响应回调
// 点击事件
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(this, "点击item" + position, Toast.LENGTH_SHORT).show();
}
// 长按点击
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(this, "长按item" + position, Toast.LENGTH_SHORT).show();
return true; // false
}
2
3
4
5
6
7
8
9
10
11
12