原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/display-bitmap.html

这节课会将前面的知识点整合到一起,展示如何使用后台线程、位图缓存来加载多张图片到ViewPager或者GridView中,并会涉及并发处理及配置更改的相关知识。

加载位图到ViewPager

swipe view pattern是画廊应用详情页之间来回跳转的最佳演示。你可以使用ViewPager来实现这种模式。无论如何,FragmentStatePagerAdapter是ViewPager支撑适配器的最佳选择,它可以随着Fragment在屏幕上的消失自动销毁与保存Fragment的状态,降低内存的使用。

Note: 如果你有少量的图片并且可以保证它们一起被加入内存之后不会超出内存的限制,那么使用正规的PagerAdapter或者FragmentPagerAdapter可能更合适些。

下面是带有ImageView的ViewPager实现。MainActivity持有了ViewPager及Adapter的引用:

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 adapterpublic 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};@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.image_detail_pager); // Contains just a ViewPagermAdapter = 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;}@Overridepublic int getCount() {return mSize;}@Overridepublic Fragment getItem(int position) {return ImageDetailFragment.newInstance(position);}}
}

下面是详情Fragment的实现,它持有了ImageView的引用。这可能看起来是一种非常合理的方式,但是你可以看出这个实现的缺陷吗?如何改进它?

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 docspublic ImageDetailFragment() {}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// image_detail_fragment.xml contains just an ImageViewfinal View v = inflater.inflate(R.layout.image_detail_fragment, container, false);mImageView = (ImageView) v.findViewById(R.id.imageView);return v;}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);final int resId = ImageDetailActivity.imageResIds[mImageNum];mImageView.setImageResource(resId); // Load image into ImageView}
}

希望你会注意到这个问题:读取图片资源的过程是在UI线程中执行的,这会导致程序的卡顿及被强制关闭。使用在Processing Bitmaps Off the UI Thread课程中所描述的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 {...@Overridepublic 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;@Overridepublic 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

grid list building block对展示图片数据集很有帮助,它主要通过GridView来实现,它可以将很多图片在同一时间显示出来,如果用户要来回滑动,那可能会有更多的图片需要准备展示出来。当实现这种控制类型时,你必须确保UI依旧流畅,内存的使用依旧在控制范围之内,并发的处理依旧正确(因为GridView会回收它的子View)。

首先,这里有一个标准的GridView实现。再者,这可能看起来非常完美,但是它还有可以改进的地方吗?

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {private ImageAdapter mAdapter;// A static dataset to back the GridView adapterpublic 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 docspublic ImageGridFragment() {}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mAdapter = new ImageAdapter(getActivity());}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic int getCount() {return imageResIds.length;}@Overridepublic Object getItem(int position) {return imageResIds[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup container) {ImageView imageView;if (convertView == null) { // if it's not recycled, initialize some attributesimageView = 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 ImageViewreturn imageView;}}
}

再提示一次,这个实现的问题图像的设置过程位于UI线程。虽然这可能只是应用小图像、简单图像,但如果有任何的额外工作需要处理,那么UI线程就会戛然而止。

前一节中的一步处理及缓存图像可以实现到这里。无论如何,你还需要对由GridView所引起的并发问题提高警惕。如果要处理这个问题,可以使用在Processing Bitmaps Off the UI Thread课程中所提到的技巧。下面是更改后的版本:

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {...private class ImageAdapter extends BaseAdapter {...@Overridepublic 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 taskbitmapWorkerTask.cancel(true);} else {// The same work is already in progressreturn false;}}// No task associated with the ImageView, or an existing task was cancelledreturn 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

Note: 上面的代码也可以应用于ListView。

这个实现对如何处理及加载图像是很灵活的,并不会妨碍UI的流畅性。后台任务可以从网络加载图像并且可以调整数码相片的尺寸,并会在任务结束的时候提供处理后的图像。

对于本示例的全部代码以及这节课中讨论到的其它概念,可以查看包含这部分功能的示例应用。

Android官方开发文档Training系列课程中文版:高效显示位图之在UI中展示位图相关推荐

  1. Android官方开发文档Training系列课程中文版:目录

    原文地址 : http://android.xsoftlab.net/training/index.html 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻 ...

  2. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  3. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制

    原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方 ...

  4. Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之Fragment创建

    原文地址:http://android.xsoftlab.net/training/basics/fragments/index.html 导言 为了在Android中创建动态的多面板用户界面,你需要 ...

  5. Android官方开发文档Training系列课程中文版:高效显示位图之加载大位图

    原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/index.html 引言 学习如何使用一种常规的手段来处理及加载Bitmap ...

  6. Android官方开发文档Training系列课程中文版:高效显示位图之位图缓存

    原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/cache-bitmap.html 往UI界面中加载单张图片的过程是很简单的, ...

  7. Android官方开发文档Training系列课程中文版:打印内容之HTML文档打印

    原文地址:http://android.xsoftlab.net/training/printing/html-docs.html 在Android中打印内容要比打印照片要复杂一些,它要求将文本与图像 ...

  8. Android官方开发文档Training系列课程中文版:布局性能优化之布局层级优化

    原文地址:http://android.xsoftlab.net/training/improving-layouts/index.html 引言 布局是直接影响用户体验的关键部分.如果实现的不好,那 ...

  9. Android官方开发文档Training系列课程中文版:线程执行操作之创建多线程管理器

    原文地址:http://android.xsoftlab.net/training/multiple-threads/create-threadpool.html 上节课我们学习了如何定义一个任务.如 ...

  10. Android官方开发文档Training系列课程中文版:电池续航时间优化之监测电池电量及充电状态

    原文地址:http://android.xsoftlab.net/training/monitoring-device-state/index.html 引言 作为一款优秀的APP应用,应该总是想方设 ...

最新文章

  1. iOS-c语言小练习01
  2. linux下安装sbt_如何在Linux上安装SBT
  3. C - Heavy Transportation POJ - 1797
  4. iOS逆向之hook框架frida的安装和使用
  5. 面经——Linux相关
  6. C语言数据类型 / 变量类型 - C语言零基础入门教程
  7. centos清楚缓存
  8. 大数据时代:如何节省存储成本
  9. C++小游戏——24点
  10. 物联网(IOT)介绍与发展背景
  11. android维持登录状态
  12. 一个emoji表情包处理工具类
  13. 搭建通过路由器连接到光猫的服务器
  14. 互联网医院源码|互联网医院软件体现智慧医疗的优势
  15. 【C++/数据结构】先序遍历+中序遍历构建二叉树
  16. python用牛顿迭代法求平方根_利用牛顿迭代法求平方根 - 业精于勤,荒于嬉;行成于思,毁于随! - OSCHINA - 中文开源技术交流社区...
  17. Flutter 一行代码快速实现你的进度条
  18. 蚂蚁金服新一代数据可视化引擎 G2
  19. 32位古董级CPU-N270群晖系统安装手册
  20. BMP编程实践1:C语言实现bmp位图分析与创建

热门文章

  1. 基于 esp32 + lvgl8.0 的小电视
  2. 随便聊聊,Linux 中的环境变量
  3. 200个模块,怎么用有线的方式进行组网通信
  4. linux spinlock/rwlock/seqlock原理剖析(基于ARM64)
  5. linux驱动层获取当前的系统时间
  6. ik分词和jieba分词哪个好_Python 中文 文本分析 实战:jieba分词+自定义词典补充+停用词词库补充+词频统计...
  7. mysql中定时任务_mysql中定时任务的用法
  8. java 调用python_Java平台如何调用Python平台?
  9. Flink-Java版单词计数(批处理流处理)
  10. LeetCode 393. UTF-8 编码验证(位运算)