ListView终极优化方法,绝对流畅

而使用静态的ViewHoulder的目的则是节省了findViewById的时间。如果不使用ViewHolder,每次getView的时候都需要得到一次子布局,而这也是很耗时并且耗资源的;如果使用了ViewHolder作为子布局的缓存,使用View的setTag方法将缓存与每个item绑定,则也可以省去了findViewById的事件;而将ViewHolder设置为static的目的是指在初始化Adapter时初始化一次这个内部类,否则将会在每次创建Adapter时都要初始化一次,而这是没有必要的。

上述方法能够解决大部分listview消耗资源以及卡顿的问题,但对于不同的需求的listview来说还会存在其他让listview卡顿的原因,比如listview的item每次加载时都需要获得图片并设置到imageview中,item加载时需要进行大量的计算,item里的TextView需要设置指定字体;这些耗时的操作都会让listview滑动起来很卡,带来不好的体验;

这几天我一直在研究Android4.0+系统联系人的源码,因为我发现,系统联系人采用的也是listview布局,每一个item都有图片文字,而且使用了fastscroller模式,对联系人以首字母进行了分来,同时Adapter还实现了SectionIndexer接口,能够实现这种效果;但是却一点都不卡,而我自己做的一个类似的界面,却十分卡顿,很影响用户体验;于是我找来了Android体统联系人的源码,自己建立起来了一个可以运行的程序,然后去研究它是如何实现这么流畅的listview的;

经过研究我发现系统联系人的listview使用的是自定义的listview:PinnedHeaderListView它的定义是这样的:

/**

* A ListView that maintains a header pinned at the top of the list. The

* pinned header can be pushed up and dissolved as needed.

*/

它给listview加了一个header即显示在联系人界面最上方贴着屏幕顶部的那部分,同时它继承自AutoScrollListView而这个AutoScrollListView继承自listview,对listview进行了一些优化,让listview在互动到指定的item时更流畅;

看到这里,于是我使用了这个AutoScrollListView,但效果不是很明显,listview还是很卡;在系统联系人使用的listview中没有找到解决方法,我开始研究它的Adapter;研究Adapter发现,系统的Adapter进行了很多层的封装,完全淡化了geiView方法;使用了很多AsyncTask来加载不同的数据,然后使用了CursorLoader来将加载好的数据添加到Adapter里,同时还将图片的加载与数据的加载进行了分离,代码逻辑十分复杂;但通过这个我得出了一个结论:不要将任何的耗时操作放在listview的getView方法里。

在系统联系人的这些Adapter里我发现getView方法中没有任何的耗时操作,在设置图片时图片已经得到,对列表按照字母进行的分类也已经分类好了,存放在一个内部类里;在得出这个结论之前我尝试过很多系统联系人中的代码,但都没有得到明显的效果,经过大量的测试我得出了这个结论,并且在测试中得到了验证。

在我的项目中,listview的每一个item都有一个图片,和很多TextView,,而且所有的TextView都要设置非系统的字体;Adapter使用的是ViewHolder优化,在getView中的代码已经很少了,但是还是卡;我的listview中的数据是一个对象的List,在对象里只存放了item需要展示的图片的资源ID,或者是图片的路径,需要通过一些操作才能获得图片,而这些操作其实是很耗时的;于是我将原来的对象进行了更改,将图片对象直接存放在item对应的对象中,然后再Adapter初始化的时候将这些对象初始化,虽然listview展示所需的时间稍微长了一点,但是结果是listview滑动流畅了很多;接着我又将从assets中获得字体TypeFace的操作放在了Adapter初始化的方法中,并且将字体通过静态的变量都存起来,然后再getView中只需为TextView设置一下taptface即可,不需要在从asset中获取字体所花费的时间;通过上面两步操作之后,我的listview一点都不卡了,十分流畅(将图片放在List的每个数据项里虽然能解决问题,但不是一个很好的解决方案,后来我使用了内存缓存加上异步加载图片的方式也保证了列表的流畅)。

我这里提供我使用的内存缓存的一个实现:

public class MemoryCache {private static final String TAG = "MemoryCache";private Map<String, Bitmap> cache=Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU orderingprivate long size=0;//current allocated sizeprivate long limit=1000000;//max memory in bytespublic MemoryCache(){//use 25% of available heap sizesetLimit(Runtime.getRuntime().maxMemory()/4);}public void setLimit(long new_limit){limit=new_limit;Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");}public Bitmap get(String id){try{if(!cache.containsKey(id))return null;//NullPointerException sometimes happen here ?id=78return cache.get(id);}catch(NullPointerException ex){ex.printStackTrace();return null;}}public void put(String id, Bitmap bitmap){try{if(cache.containsKey(id))size-=getSizeInBytes(cache.get(id));cache.put(id, bitmap);size+=getSizeInBytes(bitmap);checkSize();}catch(Throwable th){th.printStackTrace();}}private void checkSize() {Log.i(TAG, "cache size="+size+" length="+cache.size());if(size>limit){Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iteratedwhile(iter.hasNext()){Entry<String, Bitmap> entry=iter.next();size-=getSizeInBytes(entry.getValue());iter.remove();if(size<=limit)break;}Log.i(TAG, "Clean cache. New size "+cache.size());}}public void clear() {try{//NullPointerException sometimes happen here ?id=78cache.clear();size=0;}catch(NullPointerException ex){ex.printStackTrace();}}long getSizeInBytes(Bitmap bitmap) {if(bitmap==null)return 0;return bitmap.getRowBytes() * bitmap.getHeight();}}

当世界给草籽重压时,它总会用自己的方法破土而出。

ListView终极优化方法,绝对流畅

相关文章:

你感兴趣的文章:

标签云: