ListView优化

ListView优化

通过上一篇文章《ListView原理分析》我们对ListView有了进一步的了解,知道了ListView如何进行View复用,以达到展示数万条数据而不发生OOM的目的。但是,在我们在使用ListView时仍然有一些需要注意的地方,并且,通过优化,我们可以使ListView具有更好的性能。

首先引用一段Google的官方建议:

使ListView流畅滑动的关键是让主线程远离耗时任务,你要确保你的磁盘访问、网络访问以及SQL查询等操作在子线程中进行。你可以通过启用StrictMode来测试你的程序。

此外,Google还给了两个例子,一个是使用子线程,一个是使用ViewHolder。

// 使用子线程
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
    private ViewHolder v;

    @Override
    protected Bitmap doInBackground(ViewHolder... params) {
        v = params[0];
        return mFakeImageLoader.getImage();
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        super.onPostExecute(result);
        if (v.position == position) {
            // If this item hasn't been recycled already, hide the
            // progress and set and show the image
            v.progress.setVisibility(View.GONE);
            v.icon.setVisibility(View.VISIBLE);
            v.icon.setImageBitmap(result);
        }
    }
}.execute(holder);


//使用ViewHolder
static class ViewHolder {
  TextView text;
  TextView timestamp;
  ImageView icon;
  ProgressBar progress;
  int position;
}

ViewHolder holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
convertView.setTag(holder);

ConvertView

严格来说,使用ConvertView并不能算是对ListView的优化,因为你必须要使用ConvertView来完成View的复用。《ListView原理分析》这篇文章里已经对ListView的原理进行了分析,因此此处不在赘述使用ConvertView的必要性,这里给出一个使用ConvertView的示例:

    public View getView(int position, View convertView, ViewGroup parent){
        View v;
        if (convertView == null) {
            v = inflater.inflate(resource, parent, false);
        } else {
            v = convertView;
        }

        bindView(position, v);

        return v;
    }

ViewHolder

使用ViewHolder是Google官方的建议,它也几乎是所有ListView优化中必做的操作。通过使用ViewHolder,我们可以避免每次复用View时通过调用findViewById来查找每个子View,而是可以直接获取到需要的子View,然后为其绑定数据。文章开篇给出了Google提供的示例,这个给出一个完整的ViewHolder使用示例。

    static class ViewHolder {
      TextView text;
      ……//
      int position;
    }

    public View getView(int position, View convertView, ViewGroup parent){
        View v;
        ViewHolder holder;
        if (convertView == null) {
            v = inflater.inflate(resource, parent, false);
            holder = new ViewHolder();
            holder.text = (TextView) v.findViewById(R.id.listitem_text);
            ……//
            holder.position = position;
            v.setTag(holder);
        } else {
            v = convertView;
            holder = (ViewHolder)v.getTag();
        }

        bindView(position, holder);

        return v;
    }

    public void bindView(int position, ViewHolder holder){
      if(holder.position == position)
            return;
      holder.position = position;
      holder.text.setText(……);
      ……//
    }

数据加载

如果我们的ListView只有少量的数据来显示,那么直接加载所有数据或许是可行的。但是,如果我们用ListView展示成千上万的数据,并且这些数据来自磁盘、网络等慢速存储设备,那么我们获取数据的操作就会是一个耗时操作,并且有限且宝贵的内存资源使得加载全部数据到内存变的不可行。

考虑如下两种情况:

  1. 由于在ListView滑动的过程中会频繁的调用getView,因此,如果在每次调用getView时去执行耗时操作加载数据,那么滑动过程势必会由于不断执行耗时任务而引起卡顿;
  2. 再考虑这样一种情况,用户在一个带宽很小的网络环境下,我们按顺序加载数据,那么,当用户滑动到第1000条数据时,我们可能才加载到第100条数据,因此用户不得不面对空白的ListView等待,而且我们加载的其它数据用户可能根本没有机会看到,这无疑浪费了用户的流量和宝贵的 内存。

因此,为了使ListView能够流畅地响应滑动,我们需要做以下优化:

  1. 通过异步的方式来处理耗时的数据加载任务,就像在文章开篇介绍的那样,将耗时的数据加载过程放到异步任务里;
  2. 此外,在有大量数据需要加载的情况下,我们只有异步任务是不够的,我们还需要按需执行异步加载。我们可以给ListView设置OnScrollListener,然后在onScrollStateChanged方法中判断滑动状态,如果列表正在滑动,那么就停止数据加载,直到列表静止,开始加载列表目前所在position的数据。

其它

局部刷新:通常,我们数据集改变之后会通过Adapter的notifyDataSetChanged方法来通知ListView,而ListView的会去调用requestLayout请求重新布局,显然会做很多无用功。很多时候,我们只是想更新某一条数据,那么显然调用notifyDataSetChanged会造成比较大的开销。因此,我们可以继承BaseAdapter来实现局部刷新功能,主要思路是,当某一个position处的数据改变时,我们直接操作数据集,更改此处的数据,然后,判断该position是否正显示在屏幕上(通过ListView的getFirstPosition和getChildCount),如果正在显示,则通过setXXX来重新设置该View的显示的数据。

监听器:如果我们的ListView中显示的组件有使用OnXxxListener的需求,那么我们也可以将Listener绑定到ViewHolder来减少Listener的创建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值