• 在 Android 开发中, Bitmap 是个吃内存大户,稍微操作不当就会 OOM 。虽然现在第三方的图片加载库已经很多,很完善,但是作为一个 Androider 还得知道如何自己进行操作来加载大图。
  • 为什么加载图片会很容易造成 OOM 呢,主要是从图片加载到内存说起,假如一个图片的分辨率是 1000*20000,那么这张图片加载的内存中的大致大小为 1000*20000*4 = 80000000 字节,那么就是占用内存为 77 M 左右,这样的话,很容易造成 OOM 。
  • 为了不 OOM ,Android 提供了 BitmapFactory.Options 的 inJustDecodeBounds 和 inSimpleSize ,合理使用这些变量可以轻松的加载图片

inJustDecodeBounds

  • 通过把该变量设置为 true ,可以在不加载图片的情况下,拿到图片的分辨率。这时,decodeResource 方法返回的 Bitmap 是 null
  • 代码
  1. BitmapFactory.Options options = new Options();
  2. options.inJustDecodeBounds = true;
  3. BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
  4. int outHeight = options.outHeight;
  5. int outWidth = options.outWidth;

inSimpleSize

  • 通过 inJustDecodeBounds 拿到了图片的分辨率,那么通过 ImageView 的宽和高与图片的宽高进行比较,只有当图片的宽和高任意一个都比图片的宽和高都小的时候,计算结束。inSimpleSize 的大小,默认值为 1 ,然后按照 2 的倍数增加,假如 inSimpleSize 为 2,那么图片的宽和高就按照1/2缩放,这样加载到内存就缩小了4倍。
  • 代码
  1. BitmapFactory.Options options = new Options();
  2. options.inJustDecodeBounds = true;
  3. BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
  4. int outHeight = options.outHeight;
  5. int outWidth = options.outWidth;
  6. int inSampleSize = 1;
  7. int height = view.getMeasuredHeight();
  8. int width = view.getMeasuredWidth();
  9. if (outHeight > height || outWidth > width) {
  10. int halfHeight = outHeight / 2;
  11. int halfWidth = outWidth / 2;
  12. while ((halfHeight / inSampleSize) >= height || (halfWidth / inSampleSize) >= width) {
  13. inSampleSize *= 2;
  14. }
  15. }

加载大图

  1. private void loadImage(ImageView view) {
  2. BitmapFactory.Options options = new Options();
  3. options.inJustDecodeBounds = true;
  4. BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
  5. int outHeight = options.outHeight;
  6. int outWidth = options.outWidth;
  7. int inSampleSize = 1;
  8. int height = view.getMeasuredHeight();
  9. int width = view.getMeasuredWidth();
  10. if (outHeight > height || outWidth > width) {
  11. int halfHeight = outHeight / 2;
  12. int halfWidth = outWidth / 2;
  13. while ((halfHeight / inSampleSize) >= height || (halfWidth / inSampleSize) >= width) {
  14. inSampleSize *= 2;
  15. }
  16. }
  17. options.inSampleSize = inSampleSize;
  18. options.inJustDecodeBounds = false;
  19. Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
  20. view.setImageBitmap(bitmap);
  21. }

展示巨图局部

  • 加载巨图,针对上面方式的话,就无法看清楚了。比如清明上河图,这时候就需要使用到另外的一个类 BitmapRegionDecoder ,通过该类可以展示圈定的矩形区域,这样就可以更加清晰的看到局部了。
  • 代码
  1. InputStream inputStream = getResources().getAssets().open("bigpicture.jpg");
  2. BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
  3. int measuredHeight = view.getMeasuredHeight();
  4. int measuredWidth = view.getMeasuredWidth();
  5. Rect rect = new Rect(0, 0, measuredWidth, measuredHeight);
  6. Bitmap bitmap = regionDecoder.decodeRegion(rect, new Options());
  7. view.setImageBitmap(bitmap);
  • 以上就可以在 ImageView 中展示图片的局部。

滑动显示巨图

  • 上面功能实现了展示巨图的局部,但是想要通过滑动显示巨图的其他区域,就需要自定义 View , 重写 onTouchEvent() 方法,根据手指滑动的距离,重新计算 Rect 的区域,来实现加载大图布局。
  • 几个要点:
    1. 在 onMeasure 中拿到测量后的大小,设置给 Rect
    2. 在 onTouchEvent() 方法中,计算滑动的距离,然后设置给 Rect
    3. 设置了新的显示区域以后,调用 invalidate() 方法, 请求绘制,这时候会调用 onDraw() 方法
    4. 在 onDraw() 方法中根据 Rect 拿到需要显示的局部 Bitmap, 通过 Canvas 绘制回来。
  1. public class BigImageView extends View {
  2. private int slideX = 0;
  3. private int slideY = 0;
  4. private BitmapRegionDecoder bitmapRegionDecoder;
  5. private Paint paint;
  6. private int mImageWidth;
  7. private int mImageHeight;
  8. private Options options;
  9. private Rect mRect;
  10. public BigImageView(Context context) {
  11. this(context, null);
  12. }
  13. public BigImageView(Context context, @Nullable AttributeSet attrs) {
  14. this(context, attrs, 0);
  15. }
  16. public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  17. super(context, attrs, defStyleAttr);
  18. init();
  19. }
  20. private void init() {
  21. mRect = new Rect();
  22. paint = new Paint();
  23. try {
  24. InputStream inputStream = getResources().getAssets().open("bigpicture.jpg");
  25. options = new Options();
  26. options.inJustDecodeBounds = true;
  27. BitmapFactory.decodeStream(inputStream, null, options);
  28. mImageWidth = options.outWidth;
  29. mImageHeight = options.outHeight;
  30. options.inJustDecodeBounds = false;
  31. options.inPreferredConfig = Config.RGB_565;
  32. bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. float downX = 0;
  38. float downY = 0;
  39. @Override
  40. public boolean onTouchEvent(MotionEvent event) {
  41. switch (event.getAction()) {
  42. case MotionEvent.ACTION_DOWN:
  43. downX = event.getRawX();
  44. downY = event.getRawY();
  45. break;
  46. case MotionEvent.ACTION_MOVE:
  47. float moveX = event.getRawX();
  48. float moveY = event.getRawY();
  49. slideX = (int) (moveX - downX);
  50. slideY = (int) (moveY - downY);
  51. if (mImageWidth > getWidth()) {
  52. mRect.offset(-slideX, 0);
  53. if (mRect.right > mImageWidth) {
  54. mRect.right = mImageWidth;
  55. mRect.left = mRect.right - getWidth();
  56. }
  57. if (mRect.left < 0) {
  58. mRect.left = 0;
  59. mRect.right = getWidth();
  60. }
  61. invalidate();
  62. }
  63. if (mImageHeight > getHeight()) {
  64. mRect.offset(0, -slideY);
  65. if (mRect.bottom > mImageHeight) {
  66. mRect.bottom = mImageHeight;
  67. mRect.top = mRect.bottom - getHeight();
  68. }
  69. if (mRect.top < 0) {
  70. mRect.top = 0;
  71. mRect.bottom = getHeight();
  72. }
  73. invalidate();
  74. }
  75. downX = moveX;
  76. downY = moveY;
  77. break;
  78. default:
  79. break;
  80. }
  81. return true;
  82. }
  83. @Override
  84. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  85. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  86. mRect.left = 0;
  87. mRect.right = getMeasuredWidth() + mRect.left;
  88. mRect.top = 0;
  89. mRect.bottom = getMeasuredHeight() + mRect.top;
  90. }
  91. @Override
  92. protected void onDraw(Canvas canvas) {
  93. super.onDraw(canvas);
  94. Bitmap bitmap = bitmapRegionDecoder.decodeRegion(mRect, options);
  95. canvas.drawBitmap(bitmap, 0, 0, paint);
  96. }
  97. }

Android 加载大图的更多相关文章

  1. Android加载大图到内存如何避免内存溢出?

    加载大图怎么避免溢出实际做法就是对图像进行压缩,也是比较老的话题了,在最初做android时是经常会遇到的问题,而如今对于图片加载这一块都已经有很成熟稳定的三方库来弄它了,所以图片加载过大内存溢出的比 ...

  2. android加载大图,防止oom

    高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是 ...

  3. Android加载大图不OOM

    首先,我们试着往sdcard里放一张400k的图片,但是分辨率是2560*1600 布局简单 <?xml version="1.0" encoding="utf-8 ...

  4. Android高效加载大图、多图解决方案,有效避免程序内存溢出现象

    好久没有写博客了,今天就先写一个小的关于在Android中加载大图如何避免内存溢出的问题. 后面会写如何使用缓存技术的核心类,android.support.v4.util.LruCache来加载图片 ...

  5. Android中高效的显示图片之一 ——加载大图

    在网上看了不少文章,发现还是官方文档介绍最详细,把重要的东西简单摘要出来.详细可看官方文档地址 ( http://www.bangchui.org/read.php?tid=9 ) . 在应用中显示图 ...

  6. 解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题

    下面两种现象,用同一种方法解决 1.解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题 2.突然有一天首页访问图片很慢,至少隔20多秒所有图片才会出来.(解析:app使 ...

  7. android加载大量图片内存溢出的三种方法

    android加载大量图片内存溢出的三种解决办法 方法一:  在从网络或本地加载图片的时候,只加载缩略图. /** * 按照路径加载图片 * @param path 图片资源的存放路径 * @para ...

  8. android加载gif图片

    Android加载GIF图片的两种方式 方式一:使用第三开源框架直接在布局文件中加载gif 1.在工程的build.gradle中添加如下 buildscript { repositories { m ...

  9. Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】

    Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)[系列1] Android在加载或者处理超大巨型图片 ...

随机推荐

  1. golang之log rotate

    操作系统: CentOS 6.9_x64 go语言版本: 1.8.3 问题描述 golang的log模块提供的有写日志功能,示例代码如下: /* golang log example E-Mail : ...

  2. std::thread中获取当前线程的系统id

    std::thread不提供获取当前线程的系统id的方法,仅可以获取当前的线程id,但是我们可以通过建立索引表的方式来实现 std::mutex m; std::map<std::thread: ...

  3. 整合ssm框架之配置文件

    原文:https://blog.csdn.net/zwyanqing/article/details/53039591 ssm整合 一.applicationContext.xml 1.配置数据源 & ...

  4. Pinpoint - 应用性能管理(APM)平台实践之部署篇

    0.0 前言 国内的APM行业这两年刚刚起步,但是在国外却比较成熟了,并且由于这两年人力成本的快速提高,国内外涌现了几家非常不错的APM企业,例如APPdynamic,Dynamic,NewRelic ...

  5. MYSQL多行合并成一行多列

    ), VALUE )) INSERT INTO # VALUES (,,'), (,,'), (,,'), (,,'), (,,'), (,,'), (,,') SELECT code,MIN(nam ...

  6. keras embeding设置初始值的两种方式

    随机初始化Embedding from keras.models import Sequential from keras.layers import Embedding import numpy a ...

  7. HTTP协议详解(转)

    HTTP协议详解 转自: http://blog.csdn.net/gueter/article/details/1524447     Author :Jeffrey   My Blog:http: ...

  8. kubelet源码分析(version: git tag 1.7.6)

    一.概述 kubelet源码入口:cmd/kubelet/kubelet.go main() cmd/kubelet/app 包中的Run函数: 查看先参数,kubelet.KubeletDeps t ...

  9. openstack neutron 深入

    一.概述 环境说明:

  10. ASP.NET Core Linux环境安装并运行项目

    原文地址:https://blog.csdn.net/u014368040/article/details/79192622 一 安装环境 1.  从微软官网下载 Linux版本的.NetCoreSd ...