如何用ListView异常流畅的加载大量高清无码大图。

要加载大量图片并在listView那样的控件中流畅的滑动和展示:

原则是让内存占用和内存垃圾尽可能的少,从而避免oom和频繁GC导致的丢帧。另外加载图片要尽可能的快,需要进行异步加载。
所以,要做到流畅,从以下几点入手

  1. listView中convertView和Viewholder的复用。
    这个是个码农都清楚,就不细说了。直接上代码:
 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null || convertView.getTag() == null) {
            convertView = View.inflate(mContext, R.layout.item_pic, null);
            holder = new ViewHolder(convertView);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        System.out.println(":::convertView:" + convertView.toString());
        System.out.println(":::holder:" + holder.toString());
        ImageLoader.getImageLoader(mContext).loadImageFromRes((Integer) getItem(position), holder.picView);
        return convertView;
    }
    class ViewHolder {
        private TextView tv_1;
        private TextView tv_2;
        private ImageView picView;

        public ViewHolder(View v){
            picView = (ImageView) v.findViewById(R.id.iv_pic);
            tv_1 = (TextView) v.findViewById(R.id.tv_1);
            tv_2 = (TextView) v.findViewById(R.id.tv_2);
        }
    }

我们将每次的convertView和ViewHolder的地址打印出来,会发现确实是复用了。
这里写图片描述

这里写图片描述
另外,如果不复用的话,在不停的滑动过程中。在观察到内存占用会不停的增大。

  1. Bitmap的缓存和复用
    Bitmap可谓内耗大户,尤其是大图的bitmap,当用户不断滑动的时候,如果不对bitmap进行缓存,内存必然吃不消。但是缓存也是要开辟程序内存的,而一般手机系统分配给单个程序的内存只有一百多M,如果在清单文件的application节点配置了 android:largeHeap=”true” 会增加一倍。所以缓存大小的设置要考虑到图片大小和数量,可用内存等方面。一般采用LRUCache缓存策略,再加上本地缓存。
    private static LruCache<String, Bitmap> mMemoryCache;

    private ImageLoader(Context context) {
        defaultBitmap = BitmapFactory.decodeResource(context.getResources(),R.mipmap.ic_launcher);
        mContext = context.getApplicationContext();
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory;
        // 创建缓存,cachesize为缓存大小。
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }
        };
    }
    // 添加bitmap到缓存
     public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }
    // 从缓存取bitmap
    public Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }
  1. bitmap的瘦身。
    图片如果特别大,可能一个bitmap对象就要几十M,这个就不得不从以下几个方面来缩小它了。
    opts.inPreferredConfig : 图片解码的色彩精度 Bitmap.Config.ARGB_8888要比Bitmap.Config.ARGB_8888小一倍。
    opts.inDensity opts.inTargetDensity : 设置图片解码的分辨率,如果屏幕分辨率很高,解码出来的图片分辨率也会很高。所以针对高分辨率的手机需适当缩小分辨率。解码的分辨率为inTargetDensity,如果设置inscaled为true(默认值),则宽高会按inDensity/inTargetDensity比例缩放。
    opts.inSampleSize: 宽高值会乘以1/inSampleSize。
  opts = new BitmapFactory.Options();
  opts.inJustDecodeBounds = true;
  BitmapFactory.decodeResource(mContext.getResources(), resId, opts);
  int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
  if (densityDpi > 240) {
      opts.inDensity = (int) (densityDpi * 2f);
      opts.inTargetDensity = (int) (densityDpi * 0.5f);
  }
  opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
  opts.inScaled = false;

  int outWidth = opts.outWidth;
  int outHeight = opts.outHeight;
  int scale = 1;
  int ws = 1;
  int hs = 1;
  if (outWidth > 300 || outHeight > 300) {
      ws = outWidth / 300;
      hs = outHeight / 300;
      scale = ws > hs ? ws : hs;
  }
  opts.inJustDecodeBounds = false;
  if (scale % 2 != 0) {
      scale--;
  }
  scale = scale < 1 ? 1 : scale;
  System.out.println("----scale==" + scale);
  opts.inSampleSize = scale;
  1. 异步任务对象的复用。
    老婆突然吼道:老公睡觉了。 无奈,只能被迫停下,赶紧把代码贴上,擦干眼泪陪她睡。
public void loadImageFromRes(int resId, ImageView imageView) {

        if (cancelPotentialWork(resId,imageView)){
                DeBitmapTask deBitmapTask = new DeBitmapTask(imageView);
                AsyncDrawable asyncDrawable = new AsyncDrawable(defaultBitmap, deBitmapTask);
                imageView.setImageDrawable(asyncDrawable);
                deBitmapTask.execute(resId);
            }
    }


    public boolean cancelPotentialWork(int data, ImageView imageView) {
        DeBitmapTask weakrefTask = getTask(imageView);
        if (weakrefTask != null) {
            int resId1 = weakrefTask.resId;
            if (data != resId1 || resId1 == 0){
                weakrefTask.cancel(true);
            } else {
                // The same work is already in progress
                return false;
            }
        }
        // No task associated with the ImageView, or an existing task was cancelled
        return true;
    }


    DeBitmapTask getTask(ImageView imageView) {
        Drawable imageViewDrawable = imageView.getDrawable();
        if (imageViewDrawable instanceof AsyncDrawable) {
            AsyncDrawable asyncDrawable1 = (AsyncDrawable) imageViewDrawable;
            return asyncDrawable1.getWeakrefTask();
        }
        return null;
    }
class DeBitmapTask extends AsyncTask {
        Bitmap bitmap = null;

        private WeakReference<ImageView> imageViewWeakReference;
        int resId;

        private DeBitmapTask(ImageView imageView) {
            imageViewWeakReference = new WeakReference<ImageView>(imageView);
        }


        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Object doInBackground(Object[] params) {
            resId = (Integer) params[0];
            bitmap = getBitmapFromMemCache(String.valueOf(resId));
            if (bitmap == null) {
                getBitmapOpts(resId);
                bitmap = BitmapFactory.decodeResource(mContext.getResources(), resId, opts);
                addBitmapToMemoryCache(String.valueOf(resId), bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Object o) {
            imageViewWeakReference.get().setImageBitmap((Bitmap) o);
        }

    }


    // 由于asyntask没被回收利用,并且每次显示都开启一个一步任务,容易产生错位。所以给每个imageView设置一个AsyncDrawable,且每个AsyncDrawable保存对应的asyntask。

    static class  AsyncDrawable extends BitmapDrawable {

        private WeakReference<DeBitmapTask> weakrefTask = null;

        public AsyncDrawable(Bitmap bitmap, DeBitmapTask task) {
            super(mContext.getResources(), bitmap);
            weakrefTask = new WeakReference<DeBitmapTask>(task);
        }


        public DeBitmapTask getWeakrefTask() {
            return weakrefTask.get();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值