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

在开发中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中有没有图片,如果有就直接显示,没有就去发请求下载图片。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值