针对包含多个元素的View,如ListView,GridView,ExpandableListview,的时候我们是给其设置一个Adapter。Adapter是父View给子View提供数据的桥梁,也是提供每个Item的视图桥梁。由于View滚动的时候涉及到资源和View的回收过程,这其中也有一些坑,处理不当容易造成OOM,这里列举了一些优化的办法。

设计模式

1.桥接模式

Adapter使用了桥接模式(Bridge Mode),将抽象部分与实现部分分离,使它们都可以独立的变化,即具有无数个子View的ViewGroup(ListView,GridView)以及各种Adapter(CursorAdapter,ArrayAdapter)都可独立的变化。Adapter是与View之间提供数据的桥梁,也是提供每个Item的视图桥梁。

2.适配器模式

适配器模式可以让两个没有任何关系的类在一起运行,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。


AbsListView内部的RecycleBin

  • fillActiveViews() 调用这个方法后就会根据传入的参数来将ListView中的指定元素存储到mActiveViews数组当中。
  • getActiveView() 获取ListView某一position处的View(获取之后即从mActiveViews中移除)
  • addScrapView() 当有某个View确定要废弃掉的时候(比如滚动出了屏幕)缓存这个View
    RecycleBin中有一个ArrayList来存储废弃的View,convertView被移出屏幕后进入到了废弃缓存中,item再次滑动到屏幕上时拿过来复用即可。
    可查看ListView工作原理完全解析

1.避免重复inflate view造成资源浪费

ListView等内部有Recycler的实现,无论数据中是多少个item,在显示上Recycler只存储其中可见的View在内存中。当向下滑动时,顶部不可见Item直接回移动到下方再次填充数据变为新增项,不用再inflate产生新的对象。

1
2
3
4
5
6
7
public View getView(int position, View convertView, ViewGroupparent){
if (convertView ==null) {
convertView =LayoutInflater.from(mContext).inflate(R.layout.item_view, null);
}
//do something
return converView;
}

2.使用一个静态类(ViewHolder)保存子View和xml的引用关系,避免多余的findViewById

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static class ViewHolder {
TextView tv;
ImageView iv;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_view, null);
holder = new ViewHolder();
holder.tv = (TextView) convertView.findViewById(R.id.text);
holder.iv = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tv.setText(DATA[pos].title);
holder.iv.setImageBitmap(DATA[pos].bitmap);
return convertView;
}

3.图片加载优化

  • 使用线程池进行图片请求并使用LRUCache做图片缓存处理
  • 图片根据边界,option进行缩放处理
  • 滑动时暂停请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {//list停止滚动时加载图片
loadImage(startPos, endPos);// 异步加载图片 ,只加载可以看到的图片
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//设置当前屏幕显示的起始pos和结束pos
startPos = firstVisibleItem;
endPos = firstVisibleItem + visibleItemCount;
if (endPos >= totalItemCount) {
endPos = totalItemCount - 1;
}
}
});

4.设置点击事件

设置点击事件的通常写法是在getView方法中一个个设置,每次调用getView时都设置了一个新的onClick事件,可以直接在ViewHolder中设置一个position,然后实现OnClickListenr接口

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
class ViewHolder implements OnClickListener{
int position;
TextView name;
public void setPosition(int position){
this.position = position;
}
@Override
public void onClick(View v) {
switch (v.getId()){
//do something
}
}
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView==null){
convertView = inflater.inflate(R.layout.list_item, parent, false);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.name.setOnClickListener(this);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
//设置holder
holder.name.setText(list.get(position).partname);
//设置position
holder.setPosition(position);
return convertView;
}

5.getView中减少重复逻辑,避免耗时操作,避免创建大量对象

6.RecyclerView的优化主要在代码量上