故事要从一场面试说起,如何加载一个大图而不会发生OOM?这里有两种途径,一、降采样大图;二、局部加载大图。
由于android内存的限制,andoid系统给每个应用分配的内存是有限的,当直接加载一个占用内存加大的图片时就会发生OOM。
一、降采样大图
主要是利用BitmapFactory.Options对象的inSampleSize参数,根据空间尺寸与大图尺寸的比率来缩小大图,从而显示大图的缩略图。
关键代码如下:
//大图压缩看缩略图
private void loadImageView() {
imageView.setVisibility(View.VISIBLE);
largeImageView.setVisibility(View.GONE);
try {
if (inputStream==null) {
inputStream = getAssets().open("beauty.jpg");
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //只读取图片,不加载到内存中
inputStream.mark(inputStream.available()); //标记起点
BitmapFactory.decodeStream(inputStream,null,options);
inputStream.reset(); //用完inputStream后就重置状态,否则下次(inputStream.available()为0,无法再次使用
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
options.inSampleSize = calculateSampleSize(options,imageView.getWidth()==0?dm.widthPixels:imageView.getWidth(),imageView.getHeight()==0?dm.heightPixels:imageView.getHeight());
options.inJustDecodeBounds = false; //读取降采样图片,加载到内存中
inputStream.mark(inputStream.available());
Bitmap bitmap=BitmapFactory.decodeStream(inputStream,null,options);
imageView.setImageBitmap(bitmap);
inputStream.reset();
isLarge=false;
} catch (Exception e) {
e.printStackTrace();
}
btn.setText("看大图");
}
//计算缩放比
private int calculateSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
int height=options.outHeight; //获取原始图片的高度
int width=options.outWidth; //获取原始图片的宽度
int inSampleSize=1; //采样率越大则图片缩小的越厉害
if (height>maxHeight || width>maxWidth) {
int widthRatio=Math.round(width*1.0f/maxWidth);
int heightRatio=Math.round(height*1.0f/maxHeight);
inSampleSize= Math.max(widthRatio,heightRatio);
}
return inSampleSize;
}
二、
局部加载大图
主要利用BitmapRegionDecoder类的decodeRegion方法来加载局部区域,通过重写View的onTouchEvent方法监听手指滑动事件,更新显示区域。
关键代码如下:
public void initStream(InputStream inputStream) {
try {
Timber.d("initStream,inputStream="+inputStream+",size="+inputStream.available());
BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
tmpOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, tmpOptions);
mImageHeight = tmpOptions.outHeight;
mImageWidth = tmpOptions.outWidth;
bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); //要放在BitmapFactory.decodeStream(inputStream, null, tmpOptions)后面,否则tmpOptions.outHeight与tmpOptions.outWidth为-1
requestLayout();
invalidate();
} catch (IOException e) {
Timber.e(e);
e.printStackTrace();
}
Timber.d("initStream,mImageHeight="+mImageHeight+",,mImageWidth="+mImageWidth);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Timber.d("onTouchEvent,event.getAction()="+event.getAction()+",mImageHeight="+mImageHeight+",,getHeight()="+getHeight()+",mImageWidth="+mImageWidth
+",,getWidth()="+getWidth());
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mStartX=event.getX();
mStartY=event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (mImageHeight>getHeight()) {
rect.offset(0, (int) (mStartY-event.getY()));
measureHeight();
invalidate();
}
if (mImageWidth>getWidth()) {
rect.offset((int) (mStartX-event.getX()), 0);
measureWidth();
invalidate();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmapRegionDecoder.decodeRegion(rect, options), 0, 0, null);
}
效果图如下:
工程源码