ListView性能优化及加载图片出现图片错位,闪朔等问题的解决

来源:互联网 发布:sql server 2012下载 编辑:程序博客网 时间:2024/05/16 04:56

在开发中ListView是一个用的比较的频繁组件,使用ListView来展示数据,我们一般需要做以下几个步骤:在主布局文件中写ListView组件,设置一些属性,编写一个布局文件用来做为ListView的item,最后写一个适配器来连接ListView和数据。我们对ListView的优化基本上都是在适配器中实现的。
先来说下自定义适配器中复写baseAdapter的几个方法。

    //返回数据源中数据的个数,如果该方法的返回值为0,那么适配器就不用生成布局对象了,提高了程序性能    @Override    public int getCount() {        int count = 0;        if(datas!=null)            count = datas.size();        return count;    }    @Override    public Object getItem(int position) {        // TODO 根据位置返回数据项        return datas.get(position);    }    @Override    public long getItemId(int position) {        // TODO 返回数据项的位置        return position;    }    //返回用来显示每个数据项的布局对象    //参数parent接收的是容器视图对象(在本例中就是ListView对象)    //参数position接收的是当前显示的数据项的位置    @Override    public View getView(int position, View convertView, ViewGroup parent) {        return convertView;    }

其中的getView方法是最为重要的。
ListView的优化分为以下几个方面:

  • 内存空间上的优化(ConvertView)
  • 运行时间的优化(ViewHolder)
  • ListView的item多布局复用

    内存空间上的优化(ConvertView) :
    问题:ListView每加载一个item就会创建一个与之对应布局的实例,如果item达到一定的数量时就会出现应用消耗内存太大,使应用出现卡顿的现象。
    实例代码:

 @Override    public View getView(int position, View convertView, ViewGroup parent) {        //第一种方式:没有实现布局对象的复用        // TODO 生成显示数据项的布局对象        //根据布局的xml文件生成布局对象        //得到LayoutInflater对象         LayoutInflater inflater = ((Activity)context).getLayoutInflater();        //LayoutInflater对象可以根据布局的xml文件生成布局对象         //第二个参数必须是null,因为如果不是null,会去调用ListView的addView()方法,而ListView没有这个方法         View view = inflater.inflate(resourceId, null);         //System.out.println("====="+view);         //convertView接收的是可复用的布局对象,当没有可复用的布局对象时,接收的是null         //System.out.println("====="+convertView);         //把要显示的数据放到布局对象中的TextView上,得到TextView对象         //从整体的布局对象中获取TextView对象         TextView textView = (TextView) view.findViewById(R.id.textView);         //给textView的text属性设置为当前显示的数据项         textView.setText(datas.get(position).toString());        return view;    }

这样写代码,有多少个item就会创建多个对象。
解决:getview方法给我们提供了convertView参数,其实它就是一个可用的复用对象,如果加载第一屏的数据,没有可以复用的对象,那么这个convertView就是空的,如果它不是空的,那么我们就可以拿来复用,再也不需要创建那么多的布局实例。
实例代码:

//第二种方式:        //convertView接收的是可复用的布局对象,当没有可复用的布局对象时,接收的是null        //先判断是否有可复用的        if(convertView==null){            //convertView = LayoutInflater.from(context).inflate(resourceId, null);            convertView = LayoutInflater.from(context).inflate(resourceId, parent, false);        }        //得到布局对象中的TextView对象        //每次都要findViewById,影响程序性能        TextView tv = (TextView) convertView.findViewById(R.id.textView);        tv.setText(datas.get(position).toString());        return convertView;

运行空间的优化
问题:对于以上的写法,发现在不断的调用findviewbyid方法,这会是应用在运行时受到一定的影响,如果能把这个方法的调用减少,就能提高应用运行的速度。

解决:所以我们设计一个viewholder类来持有一个item布局中的控件,并将其实例化,作为convertview的tag值,用的时候直接在convertview中取就好了,省去了每次都要findviewbyid。

实例代码:

//第三种方式:        ViewHolder holder = null;        if(convertView ==null){            convertView = LayoutInflater.from(context).inflate(resourceId, parent, false);            holder = new ViewHolder();            //让holder中的textView成员指向布局对象中的TextView对象            holder.textView = (TextView) convertView.findViewById(R.id.textView);            convertView.setTag(holder);        }        else        {           holder = (ViewHolder) convertView.getTag();//当可复用时,直接获取holder        }        //操作holder中的textView就是操作布局对象中的TextView对象        holder.textView.setText(datas.get(position).toString());        return convertView;    }    //通过ViewHolder减少findViewById方法执行的次数    class ViewHolder    {        TextView textView;    }

ListView的item多布局复用:
这个已经在上面实现了。

二,加载图片出现图片错位,闪朔等问题的解决
在listview中有大量的图片时,如果处理不好就会出现图片的错位和闪朔等问题,
问题来源分析:
为了优化listview我们复用了convertview,其实问题就出现在这个地方。当item中有图片是我们会异步去加载图片,并展示在相应的位置,但是由于某些原因,图片的加载的速度就不一样的,比如,当你要加载一个item里面有图片,这时你复用了上面的convertview,正好上面的那个item也有一张图片加载,你刚显示这个item时,上一张图片就加载好了,而你的图片却还在加载中,就会先在布局上显示上一张图片,等你的图片加载好后,就立马显示你的图片。这就出先了图片的快速切换,在我们看来就是闪朔。有时还会出现图片位置的错乱。

解决方案:
所以只要解决了,图片可以显示在它指定的那个imageview上面,就ok了。所以我们给imageview设置一个tag可以是图片的url,等图片加载完后就根据tag来找到相应的imageview并显示图片。如果这个imageview已经被复用了,在listview中就找不到了,就不加载图片。这样就解决了问题。
实例代码:
异步任务:

public class DownImageAsyncTask extends AsyncTask<String, Void, Bitmap> {    private String path;    public DownImageAsyncTask(DownBack mDownBack) {        this.mDownBack = mDownBack;    }    @Override    protected Bitmap doInBackground(String... params) {        String url = null;        if (params != null) {            url = params[0];            path = url;            try {                byte[] bytes = HttpUtils.getByteFromPath(url);                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);                return bitmap;            } catch (Exception e) {                e.printStackTrace();            }        }        return null;    }    @Override    protected void onPostExecute(Bitmap bitmap) {        if (bitmap != null) {            if (mDownBack != null) {                mDownBack.response(path, bitmap);            }        }    }    public interface DownBack {        void response(String url, Bitmap bitmap);    }    private DownBack mDownBack;}

这里定义了个接口给适配器实现,在加载完图片后由异步任务来调用。
getview中的代码:

    String imageUrl = Urls.BASE_IMAGE_URL + news.getCover();                iv_image.setTag(imageUrl);                iv_image.setImageResource(R.mipmap.ic_launcher);                if (imageUrl.length() <= 0) {                    iv_image.setVisibility(View.GONE);                } else {                    iv_image.setVisibility(View.VISIBLE);                    SoftReference<Bitmap> soft = mBitmapCache.get(imageUrl);                    if (soft!=null){                        Bitmap   bitmap=soft.get();                        if (bitmap!=null){                            iv_image.setImageBitmap(bitmap);                        }else{                            downImage(imageUrl);                        }                    }else {                        downImage(imageUrl);                    }                }

在这个适配器中为了避免每次都发请求去下载图,我们就用了一个map来保存已经下载好的图片,map的key是图片的url,value是bitmap的一个软应用类型的实例。这样每次可以先判断map中有没有图片,如果有就直接显示,没有就去发请求下载图片。

0 0
原创粉丝点击