@[View池、ViewHolder重用机制]
相信大家已经非常熟练的使用ViewPager这个控件了,ViewPager的常规用法在网上随便都能搜索到,这里就不再赘述,在常规用法之外大家有没有想过优化ViewPager性能的方法呢?例如面对使用ViewPager装在成百上千张图片这种场景该怎么做呢?
让我们先回顾一下ListView的常用优化技巧:
-
利用ConvertView重用:ListView中的每一个Item显示都需要Adapter调用一次getView()的方法,这个方法会传入一个convertView的参数,这个方法返回的View就是这个Item显示的View。如果当Item的数量足够大,再为每一个Item都创建一个View对象,必将占用很多内存空间,即创建View对象(mInflater.inflate(R.layout.lv_item, null);从xml中生成View,这是属于IO操作)是耗时操作,所以必将影响性能。Android提供了一个叫做Recycler(反复循环)的构件,就是当ListView的Item从滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,相应的会从生成一个Item,而此时调用的getView中的convertView参数就是滚出屏幕的缓存Item的View,所以说如果能重用这个convertView,就会大大改善性能。
如图,当这个convertView不存在时,即第一次使用它,我们就创建一个item布局的View对象并赋给convertView,以后使用convertView时,只需从convertView中getTag取出来就可以,不需要再次创建item的布局对象了,这样便提高了性能。
-
使用ViewHolder重用:我们都知道在getView()方法中的操作是这样的:先从xml中创建view对象(inflate操作,我们采用了重用convertView方法优化),然后在这个view去findViewById,找到每一个item的子View的控件对象,如:ImageView、TextView等。这里的findViewById操作是一个树查找过程,也是一个耗时的操作,所以这里也需要优化,就是使用ViewHolder,把每一个item的子View控件对象都放在Holder中,当第一次创建convertView对象时,便把这些item的子View控件对象findViewById实例化出来并保存到ViewHolder对象中。然后用convertView的setTag将viewHolder对象设置到Tag中, 当以后加载ListView的item时便可以直接从Tag中取出复用ViewHolder对象中的,不需要再findViewById找item的子控件对象了。这样便大大提高了性能。
既然ListView可以用重用机制优化性能,那么ViewPager能不能使用重用机制来优化性能?答案是可以的。
ListView的Adapter中的getView
方法有一个参数是ConvertView,我们利用它实现view重用,避免每次都inflate布局xml,但是ViewPager的pageAdapter既没有getView方法也没有convertView,那我们怎么实现View的复用呢?我们知道ViewPager有预加载机制,默认预加载当前页的前后两页,而当切换到下一页的时候,非当前页及当前页的左右两页的其他页面都会被destroy掉,因此复用的思路就是构建一个View池,在页面实例化的时候先判断池子中是否有可用的view,如果有我们取出一个使用,否则就inflate一个,在页面destory的时候我们把view放入池子中备用,这样就避免了view的反复inflate,然后我们就可以仿照listview使用viewholder优化性能了,具体方法跟Listview中的一致,这里就不在赘述了,具体看代码吧。
private Queue<View> viewPool = new LinkedList<>(); //View池
@Override public Object instantiateItem(ViewGroup container, int position) {
View view;
ViewHolder viewHolder;
//当池子中有存货就复用,否则才inflate
if(viewPool.size() > 0){
view = viewPool.poll();
viewHolder = (ViewHolder) view.getTag();
}else{
view = inflater.inflate(R.layout.gallery_item, container, false);
viewHolder = new ViewHolder();
viewHolder.textView = ((TextView) view.findViewById(R.id.title));
viewHolder.imageView = ((ImageView) view.findViewById(R.id.imageview));
view.setTag(viewHolder);
}
viewHolder.textView.setText("Pager " + position);
viewHolder.imageView.setImageResource(PIC_RES[position]);
container.addView(view);
return view;
}
@Override public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
//将当前View加入到池子中
viewPool.offer((View) object);
}
//定义一个ViewHolder
class ViewHolder{
TextView textView;
ImageView imageView;
}