我们在做开发的时候总是会不可避免的遇到加载图片的情况,当图片的尺寸小于ImageView的尺寸的时候,我们当然可以很happy的去直接加载展示。但是如果我们要加载的图片远远大于ImageView的大小,直接用ImageView去展示的话,就会带来不好的视觉效果,也会占用太多的内存和性能开销。甚至这张图片足够大到导致程序oom崩溃。这个时候我们就需要对图片进行特殊的处理了:

一、图片压缩

图片太大,那我就想办法把它压缩变小呗。老铁,这思路完全没毛病。BitmapFactory这个类就提供了多个解析方法(decodeResourcedecodeStreamdecodeFile等)用于创建Bitmap。我们可以根据图片的来源来选择解析方法。比如如果图片来源于网络,就可以使用decodeStream方法;如果是sd卡里面的图片,就可以选择decodeFile方法;如果是资源文件里面的图片,就可以使用decodeResource方法等。这些方法会为创建的Bitmap分配内存,如果图片过大的话就会导致 oom。

BitmapFactory为这些方法都提供了一个可选的参数BitmapFactory.Options,用来辅助我们解析图片。这个参数有一个属性inSampleSize,这个属性可以帮助我们来进行图片的压缩。为了解释inSampleSize的效果,我们可以举个栗子。比如我们有一张2048*1536的图片,设置inSampleSize的值为4,就可以把这张图片压缩为512*384,长短各缩小了4倍,所占内存就缩小了16倍。这就明了了,inSampleSize的作用就是可以把图片的长短缩小inSampleSize倍,所占内存缩小inSampleSize的平方。官方文档对于inSampleSize的值也做了一些要求,那就是inSampleSize的值必须大于等于1,如果给定的值小于1,那就默认为1。而且inSampleSize的值需要是2的倍数,如果不是的话,就会自动变为离这个值向下最近的2的倍数的值,比如给定的值是3,那么最终 inSampleSize的值会是2。

当然了,这个inSampleSize的值我们也不可能随便就给,最好使我们能获取到照片的原始大小,再根据需要进行压缩。别急,谷歌都帮我们想好了!BitmapFactory.Options有一个属性inJustDecodeBounds,这个属性当为true的时候,表明我们当前只是为了获取当前图片的边界的大小,此时BitmapFactory的解析图片方法的返回值为 null,该方法是一个十分轻量级的方法。这样我们就可以很愉快的拿到图片大小了,代码如下:


BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 当前只为获取图片的边界大小
BitmapFactory.decodeResource(getResources(), R.drawable.bigpic, options);
int outHeight = options.outHeight;
int outWidth = options.outWidth;
String outMimeType = options.outMimeType;

拿到了图片的大小,我们就可以根据需要计算出所需要压缩的大小了:


private int caculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {int sampleSize = 1;int picWidth = options.outWidth;int picHeight = options.outHeight;if (picWidth > reqWidth || picHeight > reqHeight) {int halfPicWidth = picWidth / 2;int halfPicHeight = picHeight / 2;while (halfPicWidth / sampleSize > reqWidth || halfPicHeight / sampleSize > reqHeight) {sampleSize *= 2;}}return sampleSize;
}

下面就是完整的代码:


        mIvBigPic = findViewById(R.id.iv_big_pic);BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true; // 当前只为获取图片的边界大小BitmapFactory.decodeResource(getResources(), R.drawable.bigpic, options);int outHeight = options.outHeight;int outWidth = options.outWidth;String outMimeType = options.outMimeType;System.out.println("outHeight = " + outHeight + " outWidth = " + outWidth + " outMimeType = " + outMimeType);options.inJustDecodeBounds = false;options.inSampleSize = caculateSampleSize(options, getScreenWidth(), getScreenHeight());Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bigpic, options);mIvBigPic.setImageBitmap(bitmap);

这样图片压缩到这里就差不多结束了。

二、局部展示

有时候我们通过压缩可以取得很好的效果,但有时候效果就不那么美好了,例如长图像清明上河图,像这类的长图,如果我们直接压缩展示的话,这张图完全看不清,很影响体验。这时我们就可以采用局部展示,然后滑动查看的方式去展示图片。

Android里面是利用BitmapRegionDecoder来局部展示图片的,展示的是一块矩形区域。为了完成这个功能那么就需要一个方法设置图片,另一个方法设置展示的区域。

  • 初始化

BitmapRegionDecoder提供了一系列的newInstance来进行初始化,支持传入文件路径,文件描述符和文件流InputStream

例如:

mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);

上面这个方法解决了传入图片,接下来就要去设置展示区域了。

Bitmap bitmap = mRegionDecoder.decodeRegion(mRect, sOptions);

参数一是一个Rect,参数二是BitmapFactory.Options,可以用来控制inSampleSizeinPreferredConfig等。下面是一个简单的例子,展示图片最前面屏幕大的部分:


    try {BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);BitmapFactory.Options options1 = new BitmapFactory.Options();options1.inPreferredConfig = Bitmap.Config.ARGB_8888;Bitmap bitmap = regionDecoder.decodeRegion(new Rect(0, 0, getScreenWidth(), getScreenHeight()), options1);mIvBigPic.setImageBitmap(bitmap);} catch (IOException e) {e.printStackTrace();}

当然了,这只是最简单的用法,对于我们想要完全展示图片并没什么用!客官,稍安勿躁,前途已经明了!既然我们可以实现区域展示,那我们可不可以自定义一个View,可以随着我们的手指滑动展示图片的不同区域。yes! of course。那么我们就继续吧!

根据上面的分析,我们自定义控件的思路就很明白了:

  • 提供一个设置图片的路口;
  • 重写onTouchEvent,根据用户移动的手势,修改图片显示的区域;
  • 每次更新区域参数后,调用invalidate,onDraw里面去regionDecoder.decodeRegion拿到bitmap,去draw

废话不多说,直接上代码:


public class BigImageView extends View {private static final String TAG = "BigImageView";private BitmapRegionDecoder mRegionDecoder;private int mImageWidth, mImageHeight;private Rect mRect = new Rect();private static BitmapFactory.Options sOptions = new BitmapFactory.Options();{sOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;}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);}public void setInputStream(InputStream inputStream) {try {mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = false;BitmapFactory.decodeStream(inputStream, null, options);mImageHeight = options.outHeight;mImageWidth = options.outWidth;requestLayout();invalidate();} catch (IOException e) {e.printStackTrace();}}int downX = 0;int downY = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:downX = (int) event.getX();downY = (int) event.getY();break;case MotionEvent.ACTION_MOVE:int curX = (int) event.getX();int curY = (int) event.getY();int moveX = curX - downX;int moveY = curY - downY;onMove(moveX, moveY);System.out.println(TAG + " moveX = " + moveX + " curX = " + curX + " downX = " + downX);downX = curX;downY = curY;break;case MotionEvent.ACTION_UP:break;}return true;}private void onMove(int moveX, int moveY) {if (mImageWidth > getWidth()) {mRect.offset(-moveX, 0);checkWidth();invalidate();}if (mImageHeight > getHeight()) {mRect.offset(0, -moveY);checkHeight();invalidate();}}private void checkWidth() {Rect rect = mRect;if (rect.right > mImageWidth) {rect.right = mImageWidth;rect.left = mImageWidth - getWidth();}if (rect.left < 0) {rect.left = 0;rect.right = getWidth();}}private void checkHeight() {Rect rect = mRect;if (rect.bottom > mImageHeight) {rect.bottom = mImageHeight;rect.top = mImageHeight - getHeight();}if (rect.top < 0) {rect.top = 0;rect.bottom = getWidth();}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = getMeasuredWidth();int height = getMeasuredHeight();mRect.left = 0;mRect.top = 0;mRect.right = width;mRect.bottom = height;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Bitmap bitmap = mRegionDecoder.decodeRegion(mRect, sOptions);canvas.drawBitmap(bitmap, 0, 0, null);}
}

根据上述源码:

  • setInputStream方法里面初始BitmapRegionDecoder,获取图片的实际宽高;
  • onMeasure方法里面给Rect赋初始化值,控制开始显示的图片区域;
  • onTouchEvent监听用户手势,修改Rect参数来修改图片展示区域,并且进行边界检测,最后invalidate;
  • onDraw里面根据Rect获取Bitmap并且绘制。

转载链接:https://www.jianshu.com/p/4640764bfbc6

Android中如何加载大图片和长图片相关推荐

  1. Android中WebView加载sdcard中的html时提示:ERR_FILE_NOT_FOUND和ERR_ACCESS_DENIED

    场景 Android中WebView加载sdcard中的html显示: Android中WebView加载sdcard中的html显示_BADAO_LIUMANG_QIZHI的博客-CSDN博客 在实 ...

  2. Android中WebView加载本地Html,与JavaScript与Android方法相互传值(续)...

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010046908/article/details/51809558 接着上篇Android中Web ...

  3. android 从相册读取多张图片大小,Android优化查询加载大数量的本地相册图片

    一.概述 讲解优化查询相册图片之前,我们先来看下PM提出的需求,PM的需求很简单,就是要做一个类似微信的本地相册图片查询控件,主要包含两个两部分: 进入图片选择页面就要显示出手机中所有的照片,包括系统 ...

  4. android bitmap显示图片,Android_07 Android中Bitmap加载图片

    一:计算机表示图形的几种方式 二:Android加载大图片 原理: [1]获取手机分辨率 [2]获取图片分辨率 创建位图工厂的配置参数 获取图片宽高 [3]计算缩放比例 [4]显示缩放后的图片 示例代 ...

  5. [转]Android有效解决加载大图片时内存溢出的问题

    http://hi.baidu.com/%D6%C7%B4%EF%B8%DF%D4%B6lee/blog/item/7bd659af3f40dc1d4b36d68d.html 尽量不要使用setIma ...

  6. Android中如何加载显示大尺寸图片不发生OOM

    前言:当加载高清大图时,系统给每个应用分配的内存是有限的,如果一个资源图片太大,加载到内存中后,占用的内存空间也会很大,这样就会造成OOM.那在Android开发中如何正确加载高清大图呢? 采用Bit ...

  7. android 动画 图片 内存溢出,Android有效解决加载大图片时内存溢出的问题

    尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过ja ...

  8. android webview内容变大,Android中WebView加载的网页被放大的解决办法

    在某些大分辨率下,我们用WebView加载的网页可能会出现被放大的现象,这显然不是我们想要的结果,我开始看WebSettings这个是否可以设置不缩放,webSettings.setBuiltInZo ...

  9. Android中WebView加载sdcard中的html显示

    场景 Android中使用WebView加载本地html并支持运行JS代码和支持缩放: Android中使用WebView加载本地html并支持运行JS代码和支持缩放_BADAO_LIUMANG_QI ...

最新文章

  1. javascript 垃圾回收机制--分代式垃圾回收机制
  2. 中南大学c语言程序设计2013年下学期期末考试,2013级计算机专业本科生C语言程序设计期末考试资料.doc...
  3. php文件显示不完整,github文件显示不全
  4. 什么是二叉树?以及二叉树如何遍历?
  5. 康宁玻璃ct值计算公式_【钦州】CT室铅板生产厂家
  6. MFC 教程【2_MFC和Win32 】
  7. 联合国应考虑建设第二总部
  8. DedeCms网站防挂马注意点
  9. 无法打开包括文件: “Eigen/Dense”【CMakeLists 解决方案】
  10. mysql查询有什么意义_mysql分页查询有什么作用
  11. 微机原理-80386(1)
  12. matlab 隐函数全微分,求隐函数的全微分
  13. 图像与视频的Alpha通道
  14. 笔记本计算机图标怎么设置出来,电脑桌面图标隐藏了怎么弄出来
  15. 思维导图软件与团队协作
  16. 房讯房屋租赁管理软件新版上线 专为房东朋友定制
  17. Beta Distribution Guided Aspect-aware Graph for Aspect Category Sentiment Analysis论文阅读笔记(EMNLP2021)
  18. 设计模式之模板模式和工厂模式
  19. fiddler抓取谷歌浏览器的包_fiddler抓不到chrome浏览器的请求
  20. js数据过滤算法搭建

热门文章

  1. 关闭Win10安装软件的时候的提示弹窗
  2. 《塞洛特傳說》NPC系统
  3. html打开手机相机和相册,上传图片
  4. 英灵神殿修改服务器名字吗,《Valheim英灵神殿》服务器彩色名称设置教程及常见服务器问题讲解...
  5. 是神话还是泡沫?“千元难求”片仔癀大股东首次减持,传递什么信号?
  6. 苹果起诉微软侵犯版权 | 历史上的今天
  7. 写学术论文相关的网站及资料方法收集
  8. 一个程序员的爱情宣言--程序员的情书
  9. 恒指期货赚钱的优势以及交易时间
  10. java http上传文件到_如何使用java将文件上传到http远程服务器?