简析ListView的优化

这是个老生长谈的问题了,今天总结一下。在这里还要强调一下,无论你采用什么方式去优化,最终目的都是减少adapter里getView方法里面主线程的耗时操作或子线程的耗CPU操作。

1,回收站重复利用+ViewHolder模式

这个方案几乎是人尽皆知,其目的是重复利用ListView回收站里的item构造新的item,然后定义一个静态内部类ViewHolder存储item的控件,然后让convertView携带这这个ViewHolder,以备下次使用。代码如下

@Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            HashMap<String,Object> d=(HashMap<String, Object>) getItem(position);
            
            ViewHolder vh=null;
            if(convertView==null){
                vh=new ViewHolder();
                convertView=inf.inflate(itemLayout, null);
                vh.cb=(CheckBox) convertView.findViewById(R.id.cb);
                vh.iv=(ImageView) convertView.findViewById(R.id.iv);
                convertView.setTag(vh);
            }
            else{
                vh=(ViewHolder) convertView.getTag();
            }
            vh.cb.setFocusable(false);
            vh.iv.setImageBitmap(xxxxxxxxxxxx)
    
            return convertView;
        }
class ViewHolder{
            ImageView iv;
            CheckBox cb;
        }

2,尽可能减少item布局的复杂性


这个根据你项目的情况适当调整吧,尽量使你的item的布局层次不太深,嵌套不要太多

3,尽可能减少notifyDataSetChanged()方法的使用


调用这个方法会造成getView方法重调,所以在你滑动过程中尽量不要做这个事情

4,当item里含有网络图片,声音等对象时,要注意几点

  一、加载方式

  目前来说,比较合理的方式是分页加载+异步加载,一般比较合理的项目设计列表显示这一部分时都会给你一个分页设计,取数据按照页码去取,避免等待时间过长。异步加载则是在你getView里面用AsyncTask或者Thread去下载图片,需要注意几点:

  1,Thread下载时最好配一下线程优先级。让你的下载线程变成后台线程。配置方法如下,优先级的种类大家可以上网搜索到。

//在项目配置文件里加入
<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY"/>

//在Thread或者Runnable继承类里的run方法里加入
Process.setThreadPriority(Process.myTid(),Process.THREAD_PRIORITY_BACKGROUND);

  2,Thread下载完图片后若要更新UI时,先判断一下ListView是否正在滑动当中。这一点好理解,因为是异步的,所以当图片下载完时你正好在滑动屏幕,你还要更新图片显示,这就不可避免的发生卡顿。判断方式如下:

//在你的adapter里声明一个表示滑动状态的标记属性,例如adapter.setIsScrolling(true);就是根据不同情况给这个标记赋值,然后activity里起一个滑动监听,配置给listView。当你滑动时标记值会改变,你的getView里要先判断标记值然后觉得是否更新ui,当然滑动完毕再更新时比较合理的

mListView.setOnScrollListener(mScrollListener);
OnScrollListener mScrollListener = new OnScrollListener() {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            switch (scrollState) {
            case OnScrollListener.SCROLL_STATE_FLING:
                adapter.setIsScrolling(true);
                break;
            case OnScrollListener.SCROLL_STATE_IDLE:
                adapter.setIsScrolling(false);
                break;
            case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
                adapter.setIsScrolling(false);
                break;
            default:
                break;
            }
            adapter.notifyDataSetChanged();
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {

        }
    };

  3,对于你下载好的图片,你要根据项目具体情况设定缓存策略。目的是避免下次再滚动到这个item时getView重复下载,直接缓存里读取。当然怎么缓存这个还是有讲究的,缓存方式我会单独写一篇文章来讨论。

5,自定义ListView。

很多时候,我们对ListView的优化往往更多关注的是getView方法, 但有时我们也要不按常理出牌。这里的自定义ListView不仅仅是指的去写ListView的继承类。在很久很久以前刚刚接触android的时候,对于数据量不是特别大的列表显示方式,我喜欢这么干,我相信很多人也这么干过,那就是ListView的替代方案:ScrollView+动态生成LinearLayout。实际上,单纯的以这种方式显示列表数据,优劣非常明显:优势是因为数据是加载完毕的所以非常顺滑,几乎无卡顿,但成于斯亦败于斯,这种方式一旦数据过大内存就会吃不消。而ListView针对大量数据的回收站机制很好的解决了这一点,它能使得当前内存的使用量维持在用户的可视范围那一块的item里。

所以,我们能否集合二者之长呢?答案是可以的,那就是自定义一个“列表控件”,使得其也拥有adapter的机制。具体的做法我会新写一篇文章来说明。

6.暂时想到这些,我觉得总结的这些远远不够,若有网友们看到这篇文章,希望大家能多多提出更好的优化方案

posted @ 2013-11-25 16:39  靈Lucifinil  阅读(320)  评论(0编辑  收藏  举报