android 加载大图 代理,Android 加载大图

本文详细介绍了Android开发中如何处理大图加载,以防止出现内存溢出(OOM)问题。通过BitmapFactory.Options的inJustDecodeBounds和inSampleSize属性,可以有效地减小图片内存占用。首先,通过inJustDecodeBounds获取图片尺寸,然后计算合适的inSampleSize,使图片按比例缩放。此外,还介绍了BitmapRegionDecoder用于局部加载大图,以及如何自定义View实现滑动显示巨图的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在 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);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值