Android 加载大图

  • 在 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
  • 代码
        BitmapFactory.Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
        int outHeight = options.outHeight;
        int outWidth = options.outWidth;

inSimpleSize

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

加载大图

    private void loadImage(ImageView view) {
        BitmapFactory.Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
        int outHeight = options.outHeight;
        int outWidth = options.outWidth;
        int inSampleSize = 1;
        int height = view.getMeasuredHeight();
        int width = view.getMeasuredWidth();
        if (outHeight > height || outWidth > width) {
            int halfHeight = outHeight / 2;
            int halfWidth = outWidth / 2;
            while ((halfHeight / inSampleSize) >= height || (halfWidth / inSampleSize) >= width) {
                inSampleSize *= 2;
            }
        }
        options.inSampleSize = inSampleSize;
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bigpicture, options);
        view.setImageBitmap(bitmap);
    }

展示巨图局部

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

    滑动显示巨图

  • 上面功能实现了展示巨图的局部,但是想要通过滑动显示巨图的其他区域,就需要自定义 View , 重写 onTouchEvent() 方法,根据手指滑动的距离,重新计算 Rect 的区域,来实现加载大图布局。
  • 几个要点:
    1. 在 onMeasure 中拿到测量后的大小,设置给 Rect
    2. 在 onTouchEvent() 方法中,计算滑动的距离,然后设置给 Rect
    3. 设置了新的显示区域以后,调用 invalidate() 方法, 请求绘制,这时候会调用 onDraw() 方法
    4. 在 onDraw() 方法中根据 Rect 拿到需要显示的局部 Bitmap, 通过 Canvas 绘制回来。
    public class BigImageView extends View {
    
      private int slideX = 0;
      private int slideY = 0;
      private BitmapRegionDecoder bitmapRegionDecoder;
      private Paint paint;
      private int mImageWidth;
      private int mImageHeight;
      private Options options;
      private Rect mRect;
    
      public BigImageView(Context context) {
          this(context, null);
      }
    
      public BigImageView(Context context, @Nullable AttributeSet attrs) {
          this(context, attrs, 0);
      }
    
      public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
          init();
      }
    
      private void init() {
          mRect = new Rect();
          paint = new Paint();
          try {
              InputStream inputStream = getResources().getAssets().open("bigpicture.jpg");
    
              options = new Options();
              options.inJustDecodeBounds = true;
              BitmapFactory.decodeStream(inputStream, null, options);
              mImageWidth = options.outWidth;
              mImageHeight = options.outHeight;
              options.inJustDecodeBounds = false;
              options.inPreferredConfig = Config.RGB_565;
              bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
          } catch (IOException e) {
              e.printStackTrace();
          }
    
      }
      float downX = 0;
      float downY = 0;
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
                  downX = event.getRawX();
                  downY = event.getRawY();
                  break;
              case MotionEvent.ACTION_MOVE:
                  float moveX = event.getRawX();
                  float moveY = event.getRawY();
                  slideX = (int) (moveX - downX);
                  slideY = (int) (moveY - downY);
                  if (mImageWidth > getWidth()) {
                      mRect.offset(-slideX, 0);
                      if (mRect.right > mImageWidth) {
                          mRect.right = mImageWidth;
                          mRect.left = mRect.right - getWidth();
                      }
                      if (mRect.left < 0) {
                          mRect.left = 0;
                          mRect.right = getWidth();
                      }
                      invalidate();
                  }
                  if (mImageHeight > getHeight()) {
                      mRect.offset(0, -slideY);
                      if (mRect.bottom > mImageHeight) {
                          mRect.bottom = mImageHeight;
                          mRect.top = mRect.bottom - getHeight();
                      }
                      if (mRect.top < 0) {
                          mRect.top = 0;
                          mRect.bottom = getHeight();
                      }
                      invalidate();
                  }
                  downX = moveX;
                  downY = moveY;
                  break;
              default:
                  break;
          }
    
          return true;
      }
    
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          mRect.left = 0;
          mRect.right = getMeasuredWidth() + mRect.left;
          mRect.top = 0;
          mRect.bottom = getMeasuredHeight() + mRect.top;
      }
    
      @Override
      protected void onDraw(Canvas canvas) {
          super.onDraw(canvas);
          Bitmap bitmap = bitmapRegionDecoder.decodeRegion(mRect, options);
          canvas.drawBitmap(bitmap, 0, 0, paint);
      }
    }

原文地址:https://www.cnblogs.com/liyiran/p/9272880.html

时间: 2024-10-03 21:53:34

Android 加载大图的相关文章

Android加载大图

第一步:读取图片的尺寸和和类型 根据图片数据源的不同选择BitmapFactory的相应的静态方法,通过下面的方法就能够常见图片解码生成Bitmap对象.如果是图片数据源是二进制流,选择BitmapFactory.decodeByteArray()方法, 如果图片在某个文件路径下面,选择BitmapFactory.decodeFile()方法, 如果图片是在APP的内部的res文件夹下面BitmapFactory.decodeResource()方法,注意:通常调用上面三种方法签名中带有Bitm

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

在网上看了不少文章,发现还是官方文档介绍最详细,把重要的东西简单摘要出来.详细可看官方文档地址 ( http://www.bangchui.org/read.php?tid=9 ) . 在应用中显示图片,如果不多加小心,很容易就会使应用因为异常“java.lang.OutofMemoryError:bitmap size exceeds VM budget”而导致crash.在android中加载图片需要一定的技巧性,主要是因为: 1.通常设备资源有限,安卓设备给每个应用只分配16M的空间.当然

Android 高效加载大图,多图解决方案,有效避免程序OOM

高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多.大家应该知道,我们编写的应用程序都是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常.我们可以通过下面的代码看出每个应用程序最高可用内存是多少. [java] view plaincopy in

有效解决Android加载大图片时内存溢出的问题

首先解析一下基本的知识: 位图模式,bitmap颜色位数是1位 灰度模式,bitmap颜色位数是8位,和256色一样 RGB模式,bitmap颜色位数是24位 在RGB模式下,一个像素对应的是红.绿.蓝三个字节 CMYK模式,bitmap颜色位数是32位  在CMYK模式下,一个像素对应的是青.品.黄.黑四个字节 图像文件的字节数(Byte) = 图像分辨率*颜色深度/8(bit/8) 例如:一幅640*480图像分辨率.RGB色一般为24位真彩色,图像未经压缩的数据容量为:640X480X24

Android加载网络GIF完整解决方案

前言: 加载并显示gif是App常见的一个功能,像加载普通图片一样,大体应该包含以下几项功能: 1.自动下载GIF到本地文件作为缓存,第二次加载同一个url的图片不需要下载第二遍 2.由于GIF往往较大,要显示圆形的进度条提示下载进度 3.在GIF完全下载完之前,先显示GIF的第一帧图像进行占位,完全下载完毕之后自动播放动画. 4.两个不同的页面加载同一张GIF,两个页面的加载进度应该一致 5.支持ViewPager同时加载多个GIF动图 效果演示:            实现思路: 1.关于下

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

解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题 iOS开发 · 2015-01-22 11:31 MWPhotoBrowser是一个非常不错的照片浏览器,在github的star接近3000个,地址:https://github.com/mwaterfall/MWPhotoBrowser.git MWPhotoBrowser来加载小图1M以下的都应该不会有内存警告的问题.如果遇到大图,3M.4M.5M的大图,很有可能导致内存警告.最近我就遇到这个问题,很是头疼

记:Ubuntu14.04 Android加载项目失败

Android 加载项目失败: sdk/build-tools/android-4.4.2/aapt: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory 安装: sudo apt-get install lib32z1 记:Ubuntu14.04 Android加载项目失败,布布扣,bubuko.com

Android加载大图片到内存

加载大图片到内存 步骤: 步骤一: 获取品屏幕的宽高数据;[HD1] 步骤二: 获取图片的宽高;[HD2] 步骤三: 计算屏幕与图片的宽高比例,按照差距最大的进行缩放处理;[HD3] 步骤四: 图片的缩放处理;[HD4] [HD1]// 获取手机的屏幕的窗体大小 WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); Display display = windowManager.getDef

Android -- 加载大图片的方法

在android中要加载一张大图片到内存中如果通过如下方式进行: Bitmap bitmap= BitmapFactory.decodeFile("/sdcard/a.jpg"); iv.setImageBitmap(bitmap); 则会抛出内存溢出异常Caused by: java.lang.OutOfMemoryError 正确的做法应该是这样的: public class MainActivity extends Activity { private ImageView iv;