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 的区域,来实现加载大图布局。
- 几个要点:
- 在 onMeasure 中拿到测量后的大小,设置给 Rect
- 在 onTouchEvent() 方法中,计算滑动的距离,然后设置给 Rect
- 设置了新的显示区域以后,调用 invalidate() 方法, 请求绘制,这时候会调用 onDraw() 方法
- 在 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);
}
}
Android 加载大图的更多相关文章
- Android加载大图到内存如何避免内存溢出?
加载大图怎么避免溢出实际做法就是对图像进行压缩,也是比较老的话题了,在最初做android时是经常会遇到的问题,而如今对于图片加载这一块都已经有很成熟稳定的三方库来弄它了,所以图片加载过大内存溢出的比 ...
- android加载大图,防止oom
高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是 ...
- Android加载大图不OOM
首先,我们试着往sdcard里放一张400k的图片,但是分辨率是2560*1600 布局简单 <?xml version="1.0" encoding="utf-8 ...
- Android高效加载大图、多图解决方案,有效避免程序内存溢出现象
好久没有写博客了,今天就先写一个小的关于在Android中加载大图如何避免内存溢出的问题. 后面会写如何使用缓存技术的核心类,android.support.v4.util.LruCache来加载图片 ...
- Android中高效的显示图片之一 ——加载大图
在网上看了不少文章,发现还是官方文档介绍最详细,把重要的东西简单摘要出来.详细可看官方文档地址 ( http://www.bangchui.org/read.php?tid=9 ) . 在应用中显示图 ...
- 解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题
下面两种现象,用同一种方法解决 1.解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题 2.突然有一天首页访问图片很慢,至少隔20多秒所有图片才会出来.(解析:app使 ...
- android加载大量图片内存溢出的三种方法
android加载大量图片内存溢出的三种解决办法 方法一: 在从网络或本地加载图片的时候,只加载缩略图. /** * 按照路径加载图片 * @param path 图片资源的存放路径 * @para ...
- android加载gif图片
Android加载GIF图片的两种方式 方式一:使用第三开源框架直接在布局文件中加载gif 1.在工程的build.gradle中添加如下 buildscript { repositories { m ...
- Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】
Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)[系列1] Android在加载或者处理超大巨型图片 ...
随机推荐
- EF Code First导航属性一对一关系中注意点及配置方法
//学生 public class Student { [key] public int StId { get; set; } public int SocialSecurityNumber { ge ...
- Linux(centos)新建,删除,移动,重命名文件夹和文件的命令
1.新建文件夹 mkdir 文件名 新建一个名为test的文件夹在home下 view source1 mkdir /home/test 2.新建文本 在home下新建一个test.sh脚本 vi / ...
- LeetCode 225 Implement Stack using Queues(用队列来实现栈)(*)
翻译 用队列来实现栈的例如以下操作. push(x) -- 将元素x加入进栈 pop() -- 从栈顶移除元素 top() -- 返回栈顶元素 empty() -- 返回栈是否为空 注意: 你必须使用 ...
- 三种分布式对象主流技术——COM、Java和COBRA
既上一遍,看到还有一遍将关于 对象的, 分布式对象, 故摘抄入下: 目前国际上,分布式对象技术有三大流派——COBRA.COM/DCOM和Java.CORBA技术是最早出现的,1991年OMG颁布了C ...
- weblogic服务目录迁移记录
weblogic服务,由于前期的规划不好,导致后期有点问题!为了更加规范运行服务及执行相关操作,故进行服务迁移... 先决条件:weblogic都是单个aminserver运行的,单个服务 问题解决: ...
- js中setTimeout、setInterval、 clearInterval方法简介
setTimeout setTimeout(code, millisec) 用于在指定的毫秒数后调用函数或计算表达式. 说明: setTimeout()只执行一次code.如果要多次调用,要使用set ...
- Java成神之路[转]
阿里大牛珍藏架构资料,点击链接免费获取 针对本文,博主最近在写<成神之路系列文章> ,分章分节介绍所有知识点.欢迎关注. 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 ...
- 封装naive socket
周五去一个公司打了个酱油,面试官问我:你封装过socket没? 言下之意是问我实际写过底层代码没,我悻悻地说写过点. PS:说实话木有封装过,今天无聊就来封装下. 话说写了这么久C++,底层用c来写还 ...
- 线程同步之mutex和event区别
之前只是用过 关键段来对同进程不同线程进行互斥,防止对同一份资源或代码段的竞争: mutex可以理解为不同进程或者同一进程内防止对同一份资源的竞争: event更多的是同步,当然也是不同进程或者同一进 ...
- >/dev/null 2>&1的含义
转帖:http://www.cnblogs.com/dkblog/archive/2009/07/31/1980722.html os.system("/etc/init.d/winbind ...