ListView 的优化方式

  之前探讨了下高清图片加载的优化,高清图片的加载确实有很大几率会给用户带来卡顿的感觉。然而我们最容易感觉卡顿的是什么呢,含有图片的listview,一方面图片本身容易造成卡顿,而没有优化好的listview更是一场噩梦,我们不仅感觉listview显示的时候卡顿,而且滑动listview的时候更是卡成翔,那么优化listview都有哪些手段呢?我想就这个来探讨一下。

  要知道为啥listview会卡,我们得从listview的生成过程来了解一下哪些process比较耗时。先来看一段listview最依赖的adapter代码:

 1     public View getView(int position, View convertView, ViewGroup parent) {
 2         // TODO Auto-generated method stub
 3         LayoutInflater inflater = LayoutInflater.from(this.context);
 4         View inflaterView = inflater.inflate(R.layout.item, null);
 5         TextView tv = (TextView) inflaterView.findViewById(R.id.tv);
 6         ImageView img = (ImageView) inflaterView.findViewById(R.id.img);
 7         tv.setText(FEATURE_NAMES[position]);
 8         img.setImageResource(ICONS[position]);
 9         return inflaterView;
10     }

  Adapter 类作为listview显示不可缺少的适配器,listview的显示耗时几乎都在其中,而Adapter类的几大必须实现的方法中getView方法占有绝大部分的耗时操作。

  我们来看看getView这个方法中要完成多少操作:

1、拿到LayoutInflater类的对象(inflater)并把一个布局(R.layout.item)扩展成一个View对象(inflaterView);

2、用生成的View对象(inflat而View)通过findViewById的方法取得子控件;

3、分别给子控件设置资源;

4、返回View对象完成getView的操作。

这些操作对于一个完整的getView流程是必须要实现的,耗时操作有:

1、对应的扩展方法inflate();

2、对应的findViewById();

3、对应的资源set方法;

4、所有的new对象方法。

一次完整getView流程必须要实现,如何优化呢?核心思想就是 减少重复、引入缓存

  1、我们可以尽可能的减少方法的重复调用。因为listview是一个列表的形式存在,对于列表里面的内存,每显示一行,都要调用一次getView方法,里面的方法都要重新执行一遍,我们应该尽可能的不重复调用一个重复的方法。我们可以很清楚的发现有的操作其实只需要一遍就行了,譬如:

   LayoutInflater inflater = LayoutInflater.from(this.context);拿扩展对象,没有必要重复new一个LayoutInflater的对象。

  View inflaterView = inflater.inflate(R.layout.item, null);每次都是从同一个布局中扩展成view,没有必要重复。

  TextView tv = (TextView) inflaterView.findViewById(R.id.tv);
  ImageView img = (ImageView) inflaterView.findViewById(R.id.img);
  findViewById是一个非常耗时的操作,因为findViewById方法是一个蠢萌的树形遍历方法,当你应用中id多了的时候这个就非常耗时。而且每次findViewById同一个id时都是重复操作。

  所以这些操作都可以拿出来避免重复,初步优化后的代码如下:

 

 1     private LayoutInflater inflater;
 2     private View inflaterView;
 3     private TextView tv;
 4     private ImageView img;
 5 
 6     public View getView(int position, View convertView, ViewGroup parent) {
 7         // TODO Auto-generated method stub
 8         if (inflater == null) {
 9             inflater = LayoutInflater.from(this.context);
10         }
11         if (inflaterView == null) {
12             inflaterView = inflater.inflate(R.layout.item, null);
13         }
14         if (tv == null) {
15             tv = (TextView) inflaterView.findViewById(R.id.tv);
16         }
17         if (img == null) {
18             img = (ImageView) inflaterView.findViewById(R.id.img);
19         }
20 
21         tv.setText(FEATURE_NAMES[position]);
22         img.setImageResource(ICONS[position]);
23 
24         return inflaterView;
25     }

 

  这样优化后的getView能保证没有了那些重复调用的方法,减少了重复的耗时操作,如何你的listview没有超过一页,那么几乎是最快的方法。

  2、引入缓存。上面的优化对于listview较短没有滑动的listview来说足够,但是如果listview超过一页显示,当我们上下滑动的时候,会发现按照上面的方法,即使我们之前已经getView过了的list,还是会重新调用set方法,然而我们完全可以不再重新刷新里面的数据的,所以这个地方就可以引入缓存,把我们已经get过的list给缓存起来,等要用的时候再直接返回就行了,进一步优化后的代码如下:

 1     private LayoutInflater inflater;
 2     private static View inflaterView;
 3     private static TextView tv;
 4     private static ImageView img;
 5     private static View[] viewCache;
 6 
 7     public View getView(int position, View convertView, ViewGroup parent) {
 8         // TODO Auto-generated method stub
 9         if (viewCache[position] == null) {
10             if (inflater == null) {
11                 inflater = LayoutInflater.from(this.context);
12             }
13             if (inflaterView == null) {
14                 inflaterView = inflater.inflate(R.layout.item, null);
15             }
16             if (tv == null) {
17                 tv = (TextView) inflaterView.findViewById(R.id.tv);
18             }
19             if (img == null) {
20                 img = (ImageView) inflaterView.findViewById(R.id.img);
21             }
22 
23             tv.setText(FEATURE_NAMES[position]);
24             img.setImageResource(ICONS[position]);
25 
26             viewCache[position] = inflaterView;
27         }
28 
29         return viewCache[position];
30     }

  在这个例子中,static变量里面缓存了listview中已经get过的view,这样就可以在上滑操作时不必重复给view设置资源数据了,省了很多时间,这样所有的view都只需要get一遍,这几乎是最快的方法了,但是缓存也有缺点,listview缓存增加了内存消耗,不过在现在手机内存情况下一般是足够的。

posted @ 2015-04-16 15:34  Simba.Chen  阅读(857)  评论(0编辑  收藏  举报