Android中如何加载大图片和长图片
我们在做开发的时候总是会不可避免的遇到加载图片的情况,当图片的尺寸小于ImageView
的尺寸的时候,我们当然可以很happy
的去直接加载展示。但是如果我们要加载的图片远远大于ImageView
的大小,直接用ImageView
去展示的话,就会带来不好的视觉效果,也会占用太多的内存和性能开销。甚至这张图片足够大到导致程序oom
崩溃。这个时候我们就需要对图片进行特殊的处理了:
一、图片压缩
图片太大,那我就想办法把它压缩变小呗。老铁,这思路完全没毛病。BitmapFactory
这个类就提供了多个解析方法(decodeResource
、decodeStream
、decodeFile
等)用于创建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
,可以用来控制inSampleSize
,inPreferredConfig
等。下面是一个简单的例子,展示图片最前面屏幕大的部分:
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中如何加载大图片和长图片相关推荐
- Android中WebView加载sdcard中的html时提示:ERR_FILE_NOT_FOUND和ERR_ACCESS_DENIED
场景 Android中WebView加载sdcard中的html显示: Android中WebView加载sdcard中的html显示_BADAO_LIUMANG_QIZHI的博客-CSDN博客 在实 ...
- Android中WebView加载本地Html,与JavaScript与Android方法相互传值(续)...
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010046908/article/details/51809558 接着上篇Android中Web ...
- android 从相册读取多张图片大小,Android优化查询加载大数量的本地相册图片
一.概述 讲解优化查询相册图片之前,我们先来看下PM提出的需求,PM的需求很简单,就是要做一个类似微信的本地相册图片查询控件,主要包含两个两部分: 进入图片选择页面就要显示出手机中所有的照片,包括系统 ...
- android bitmap显示图片,Android_07 Android中Bitmap加载图片
一:计算机表示图形的几种方式 二:Android加载大图片 原理: [1]获取手机分辨率 [2]获取图片分辨率 创建位图工厂的配置参数 获取图片宽高 [3]计算缩放比例 [4]显示缩放后的图片 示例代 ...
- [转]Android有效解决加载大图片时内存溢出的问题
http://hi.baidu.com/%D6%C7%B4%EF%B8%DF%D4%B6lee/blog/item/7bd659af3f40dc1d4b36d68d.html 尽量不要使用setIma ...
- Android中如何加载显示大尺寸图片不发生OOM
前言:当加载高清大图时,系统给每个应用分配的内存是有限的,如果一个资源图片太大,加载到内存中后,占用的内存空间也会很大,这样就会造成OOM.那在Android开发中如何正确加载高清大图呢? 采用Bit ...
- android 动画 图片 内存溢出,Android有效解决加载大图片时内存溢出的问题
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过ja ...
- android webview内容变大,Android中WebView加载的网页被放大的解决办法
在某些大分辨率下,我们用WebView加载的网页可能会出现被放大的现象,这显然不是我们想要的结果,我开始看WebSettings这个是否可以设置不缩放,webSettings.setBuiltInZo ...
- Android中WebView加载sdcard中的html显示
场景 Android中使用WebView加载本地html并支持运行JS代码和支持缩放: Android中使用WebView加载本地html并支持运行JS代码和支持缩放_BADAO_LIUMANG_QI ...
最新文章
- javascript 垃圾回收机制--分代式垃圾回收机制
- 中南大学c语言程序设计2013年下学期期末考试,2013级计算机专业本科生C语言程序设计期末考试资料.doc...
- php文件显示不完整,github文件显示不全
- 什么是二叉树?以及二叉树如何遍历?
- 康宁玻璃ct值计算公式_【钦州】CT室铅板生产厂家
- MFC 教程【2_MFC和Win32 】
- 联合国应考虑建设第二总部
- DedeCms网站防挂马注意点
- 无法打开包括文件: “Eigen/Dense”【CMakeLists 解决方案】
- mysql查询有什么意义_mysql分页查询有什么作用
- 微机原理-80386(1)
- matlab 隐函数全微分,求隐函数的全微分
- 图像与视频的Alpha通道
- 笔记本计算机图标怎么设置出来,电脑桌面图标隐藏了怎么弄出来
- 思维导图软件与团队协作
- 房讯房屋租赁管理软件新版上线 专为房东朋友定制
- Beta Distribution Guided Aspect-aware Graph for Aspect Category Sentiment Analysis论文阅读笔记(EMNLP2021)
- 设计模式之模板模式和工厂模式
- fiddler抓取谷歌浏览器的包_fiddler抓不到chrome浏览器的请求
- js数据过滤算法搭建
热门文章
- 关闭Win10安装软件的时候的提示弹窗
- 《塞洛特傳說》NPC系统
- html打开手机相机和相册,上传图片
- 英灵神殿修改服务器名字吗,《Valheim英灵神殿》服务器彩色名称设置教程及常见服务器问题讲解...
- 是神话还是泡沫?“千元难求”片仔癀大股东首次减持,传递什么信号?
- 苹果起诉微软侵犯版权 | 历史上的今天
- 写学术论文相关的网站及资料方法收集
- 一个程序员的爱情宣言--程序员的情书
- 恒指期货赚钱的优势以及交易时间
- java http上传文件到_如何使用java将文件上传到http远程服务器?