美文网首页安卓资源收集
Bitmaps加载之综合应用

Bitmaps加载之综合应用

作者: keith666 | 来源:发表于2016-06-07 23:43 被阅读21次

前面分别从Bitmap的介绍,高效加载大图,异步加载,缓存内存管理这些方面来学习Bitmap的加载,本文将综合以上方法,来示范ViewPager和GridView中Bitmap的加载.

ViewPager中Bitmap的加载

关于页面的滑动切换,有一个很好的方式可以参考swipe view pattern.对于ViewPager的Adapter,可以使用的有三个PagerAdapter,FragmentPagerAdapter和FragmentStatePagerAdapter,其中后面两个是PagerAdapter的子类,都是用Fragment来管理每一个Page,但是不同的是FragmentStatePagerAdapter有销毁和保存状态机制,即使有很多的Page也可以将内存保持在较低的水平.

ImageDetailActivity中的ViewPager

public class ImageDetailActivity extends FragmentActivity {
    public static final String EXTRA_IMAGE = "extra_image";

    private ImagePagerAdapter mAdapter;
    private ViewPager mPager;

    // A static dataset to back the ViewPager adapter
    public final static Integer[] imageResIds = new Integer[] {
            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_detail_pager); // Contains just a ViewPager

        mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);
        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);
    }

    public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
        private final int mSize;

        public ImagePagerAdapter(FragmentManager fm, int size) {
            super(fm);
            mSize = size;
        }

        @Override
        public int getCount() {
            return mSize;
        }

        @Override
        public Fragment getItem(int position) {
            return ImageDetailFragment.newInstance(position);
        }
    }
}

带ImageView的Fragment的代码:

public class ImageDetailFragment extends Fragment {
    private static final String IMAGE_DATA_EXTRA = "resId";
    private int mImageNum;
    private ImageView mImageView;

    static ImageDetailFragment newInstance(int imageNum) {
        final ImageDetailFragment f = new ImageDetailFragment();
        final Bundle args = new Bundle();
        args.putInt(IMAGE_DATA_EXTRA, imageNum);
        f.setArguments(args);
        return f;
    }

    // Empty constructor, required as per Fragment docs
    public ImageDetailFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // image_detail_fragment.xml contains just an ImageView
        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
        mImageView = (ImageView) v.findViewById(R.id.imageView);
        return v;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        final int resId = ImageDetailActivity.imageResIds[mImageNum];
        mImageView.setImageResource(resId); // Load image into ImageView
    }
}

注意: mImageView.setImageResource(resId)这行代码是在主线程执行的,这会有潜在的ANR风险,下面利用之前的知识进行改进,使用AsyncTask来异步加载.部分代码如下:

public class ImageDetailActivity extends FragmentActivity {
    ...

    public void loadBitmap(int resId, ImageView imageView) {
        mImageView.setImageResource(R.drawable.image_placeholder);
        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
        task.execute(resId);
    }

    ... // include BitmapWorkerTask class
}

public class ImageDetailFragment extends Fragment {
    ...

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (ImageDetailActivity.class.isInstance(getActivity())) {
            final int resId = ImageDetailActivity.imageResIds[mImageNum];
            // Call out to ImageDetailActivity to load the bitmap in a background thread
            ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);
        }
    }
}

改进之后任何额外的操作(如缩放或网络请求)都可以使用之前的BitmapWorkerTask 来执行,不会影响到主线程的响应. 当然如果有需要还可以加入前面说的内存缓存,如下:

public class ImageDetailActivity extends FragmentActivity {
    ...
    private LruCache<String, Bitmap> mMemoryCache;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // initialize LruCache as per Use a Memory Cache section
    }

    public void loadBitmap(int resId, ImageView imageView) {
        final String imageKey = String.valueOf(resId);

        final Bitmap bitmap = mMemoryCache.get(imageKey);
        if (bitmap != null) {
            mImageView.setImageBitmap(bitmap);
        } else {
            mImageView.setImageResource(R.drawable.image_placeholder);
            BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
            task.execute(resId);
        }
    }

    ... // include updated BitmapWorkerTask from Use a Memory Cache section
}

这样就把所有的技巧串在一起用在ViewPager上,就可以最小化图片加载的等待时间和尽量少的后台处理.

GridView中Bitmap的加载

GridView在加载大量网络数据时,也是要保证UI的流畅性,内存的合理控制,和并发的正确处理.
下面看代码:

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
    private ImageAdapter mAdapter;

    // A static dataset to back the GridView adapter
    public final static Integer[] imageResIds = new Integer[] {
            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};

    // Empty constructor as per Fragment docs
    public ImageGridFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAdapter = new ImageAdapter(getActivity());
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
        mGridView.setAdapter(mAdapter);
        mGridView.setOnItemClickListener(this);
        return v;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
        startActivity(i);
    }

    private class ImageAdapter extends BaseAdapter {
        private final Context mContext;

        public ImageAdapter(Context context) {
            super();
            mContext = context;
        }

        @Override
        public int getCount() {
            return imageResIds.length;
        }

        @Override
        public Object getItem(int position) {
            return imageResIds[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            ImageView imageView;
            if (convertView == null) { // if it's not recycled, initialize some attributes
                imageView = new ImageView(mContext);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setLayoutParams(new GridView.LayoutParams(
                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            } else {
                imageView = (ImageView) convertView;
            }
            imageView.setImageResource(imageResIds[position]); // Load image into ImageView
            return imageView;
        }
    }
}

同样,上面也有一个问题,那就是imageView.setImageResource(imageResIds[position]);,这行代码在主线中执行,因此需要异步处理,但是跟ViewPager不同的是,GridView带有回收机制,这会导致乱序的出现,需要注意,改进的代码如下:

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
    ...

    private class ImageAdapter extends BaseAdapter {
        ...

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            ...
            loadBitmap(imageResIds[position], imageView)
            return imageView;
        }
    }

    public void loadBitmap(int resId, ImageView imageView) {
        if (cancelPotentialWork(resId, imageView)) {
            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            final AsyncDrawable asyncDrawable =
                    new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
            imageView.setImageDrawable(asyncDrawable);
            task.execute(resId);
        }
    }

    static class AsyncDrawable extends BitmapDrawable {
        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

        public AsyncDrawable(Resources res, Bitmap bitmap,
                BitmapWorkerTask bitmapWorkerTask) {
            super(res, bitmap);
            bitmapWorkerTaskReference =
                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
        }

        public BitmapWorkerTask getBitmapWorkerTask() {
            return bitmapWorkerTaskReference.get();
        }
    }

    public static boolean cancelPotentialWork(int data, ImageView imageView) {
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (bitmapWorkerTask != null) {
            final int bitmapData = bitmapWorkerTask.data;
            if (bitmapData != data) {
                // Cancel previous task
                bitmapWorkerTask.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;
    }

    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
       if (imageView != null) {
           final Drawable drawable = imageView.getDrawable();
           if (drawable instanceof AsyncDrawable) {
               final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
               return asyncDrawable.getBitmapWorkerTask();
           }
        }
        return null;
    }

    ... // include updated BitmapWorkerTask class

这样就可以愉快的和GridView玩耍了,同样使用于ListView. 具体代码看官方Demo.

Reference

  1. Displaying Bitmaps in Your UI
  2. 官方DisplayingBitmaps Demo

相关文章

  • Bitmaps加载之综合应用

    前面分别从Bitmap的介绍,高效加载大图,异步加载,缓存和内存管理这些方面来学习Bitmap的加载,本文将综合以...

  • Bitmaps加载之异步加载

    介绍 前文介绍了Bitmap大图的高效加载,如果需要加载的图片来源于网络或本次磁盘或其他非内存地方,这个加载的任务...

  • Bitmaps加载之介绍

    介绍 Android开发应用过程中加载bitmap是很tricky的,如果你稍微不注意,bitmaps就会快速消耗...

  • Bitmaps加载之缓存

    前文介绍了Bitmaps的异步加载,将单个Bitmap加载到视图控件是很简单直接的,但是要同时批量加载的话就会变得...

  • Android 性能优化之Loading Big Bitmaps

    高效加载Large Bitmaps 加载大Bitmaps到内存中,总是会有各种各样的问题,我们在开发过程中,经常会...

  • bitmaps.md

    Dirty Bitmaps and Incremental Backup Dirty Bitmaps are ob...

  • Bitmaps加载之内存管理

    除了前面说的Bitmap缓存之外,还有一些事情我们可以做来使用好GC和Bitmap的重用. 对于不同的Androi...

  • Bitmaps加载之高效加载大图

    前文 很多时候我们要加载的图片的尺寸要比放置的控件的来的大,而该控件只要求图片的像素和控件本身的尺寸一致即可达到最...

  • bitmaps

    Roaring bitmaps 说到Roaring bitmaps,就必须先从bitmap说起。Bitmap是一种...

  • Android之setContentView和LayoutInf

    Android应用setContentView与LayoutInflater加载解析机制源码分析 Android之...

网友评论

    本文标题:Bitmaps加载之综合应用

    本文链接:https://www.haomeiwen.com/subject/vkqjdttx.html