ListView简析
ListView定义
专门用于处理那种内容元素很多,手机屏幕无法展示出所有的内容的情况。可以使用列表的形式来展示内容,超出屏幕部分的内容只需要通过手指滑动就可以移到屏幕内了。
ListView相比RecyclerView,有一些优点:
addHeaderVew()
,addFooterView()
添加头尾视图android:divider
设置自定义分割线setOnItemClickListener
设置点击事件
上述功能没有直接在RecyclerView直接提供,要自己实现。如果只是简单的展示内容,使用ListView相对更简单。
ListView用法
创建Adapter:在ListView和数据源之间起到了一个桥梁的作用,ListView借用Adapter和数据去打交道。
常见的Adapter有以下几类:
BaseAdapter
:基础数据适配器。SimpleAdapter
:简单适配器,系统自定义了一些方法。ArrayAdapter
:数据和UI一对一。SimpleCursorAdapter
:用于游标类型的数据源适配。
一般都会去继承
BaseAdapter
自定义实现功能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
46public class ListViewAdapter extends BaseAdapter {
Context context;
List<String> data;
LayoutInflater mInflater;
public ListViewAdapter(Context _context, List<String> _data) {
this.context = _context;
this.data = _data;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return data == null ? 0 : data.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
//在其中完善 Item的创建以及数据绑定流程
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_demo, null);
viewHolder = new ViewHolder();
viewHolder.title = convertView.findViewById(R.id.title);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.title.setText(data.get(position));
return convertView;
}
private class ViewHolder {
TextView title;
}
}ListView绑定Adapter
1
2
3
4
5
6
7
8
9
10listView.addHeaderView(headerView);
listView.addFooterView(footerView);
listView.setAdapter(new ListViewAdapter(Activity.this,datas));
//设置ListView 的 item点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
});
ListView源码解析
缓存机制
ListView是以
View
作为单位进行回收。RecycleBin
是ListView缓存机制的实现类。
RecycleBin实现的是二级缓存。
View[] mActiveViews
:缓存屏幕上的View,在该缓存中的View不需要调用getView()
ArrayList<View>[] mScrapViews
:对应了每个ItemType
,缓存由于滚动而消失的View,此处的View如果被复用,会以参数的形式传给getView()
。
ListView通过调用layoutChildren()
对子Item进行布局,一般发生在滑动时刻。
1 |
|
layoutChildren()
调用fillXX()
在不同位置填充item。其中的主要实现方法是makeAndAddView()
实现填充View逻辑。
1 |
|
如果从mActiveViews
中获取到了对应的View,就直接取出来,然后调用setUpChild()
把子View重新attach到ListView上。
如果没有找到合适的View,就需要调用到obtainView()
,重新执行getView()
流程生成对应布局,影响加载效率。
1 |
|
接下来介绍getScrapView(position)
的实现,该方法通过position
得到ItemType
,然后根据ItemType
从mScrapViews
获取可复用的View,如果获取不到,则返回null,
1 |
|
得到需要显示的View后,再调用setUpChild()
显示在界面上
1 |
|
观察上述源码可知,缓存机制简单分为以下几步:
- ListView滑动时,会调用到
layoutChildren()
对子View进行重新布局,如果数据源没有发生改变,需要把当前屏幕上存在的View缓存至mActiveViews
中;发生了改变的话,就都缓存至mScrapViews
中。 - 清除掉ListView的所有子View
- 开始进行数据填充,主要实现逻辑是
makeAndAddView()
。这里分为两部分:如果可以从mActiveViews
获取到View,就直接插入该View;没有获取到合适的View,需要调用obtainView()
从mScrapViews
获取可复用的View,然后重新走加载布局(getView()
)的流程。
ListView优化
ConverView重用机制
:在getView()
中使用convertView
,就不需要每次都去inflate一个View出来,减少内存损耗。ViewHolder
:使用ViewHolder,避免在getView()
频繁调用使用findViewById()
,节省内存滑动时不载入图片
:给ListView设置setOnScrollListener()
,在其中onScrollStateChanged()
判断是否为滑动状态,是的话就停止加载图片。getView()不执行耗时操作
:getView()
是执行在主线程的,需要减少耗时操作。设置scrollingCache和animateCache为false
:默认都是开启的,会消耗大量内存。降低Item的层级
ListView拓展
ListView局部刷新
平常用到ListView的时候,如果需要对单个Item进行刷新,我们就会调用到
notifyDataSetChanged()
去进行全量刷新,效率很低。
ListView局部刷新有3种方案可以实现:
更新对应View内容
通过
listView.getChildAt(pos)
拿到需要更新的item布局,然后通过findViewById()
去找到对应的控件进行设置1
2
3
4
5
6
7
8
9
10private void updateItemView(ListView listView,int pos,Data data/*需要更新的内容*/){
int firstVisiblePosition = listView.getFirstVisiblePosition();
int lastVisiblePosition = listView.getLastVisiblePosition();
if(pos>=firstVisiblePosition && pos<= lastVisiblePosition){
View view = listView.getChildAt(pos-firstVisiblePosition);
TextView textView= view.findViewById(R.id.textView);
textView.setText(data.getXX());
}
}通过ViewHolder去设置
通过Item找到对应ViewHolder,通过ViewHolder设置数据
1
2
3
4
5
6
7
8
9
10
11private void updateItemView(ListView listView,int pos,Data data/*需要更新的内容*/){
int firstVisiblePosition = listView.getFirstVisiblePosition();
int lastVisiblePosition = listView.getLastVisiblePosition();
if(pos>=firstVisiblePosition && pos<= lastVisiblePosition){
View view = listView.getChildAt(pos-firstVisiblePosition);
ViewHolder viewHolder = (ViewHolder)view.getTag();
TextView textView= iewHolder.textView;
textView.setText(data.getXX());
}
}再调用一次
getView()
调用Adapter的
getView()
,对内部的View进行刷新。Google官方推荐做法。1
2
3
4
5
6
7
8
9
10//外部对数据源进行变化,内部自动去更新
private void updateItemView(ListView listView,int pos,Data data/*需要更新的内容*/){
int firstVisiblePosition = listView.getFirstVisiblePosition();
int lastVisiblePosition = listView.getLastVisiblePosition();
if(pos>=firstVisiblePosition && pos<= lastVisiblePosition){
View view = listView.getChildAt(pos-firstVisiblePosition);
listViewAdapter.getView(pos,view,listView)
}
}
内容引用
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!