前言
在Android开发中,有时候会有加载巨图(长图)的需求,可以上下左右滑动,双击放大或缩小,手指缩放。如何加载一个大图而不产生OOM呢?使用体统提供的BitmapRegionDecoder,区域解码器,可以用来解码一个矩形区域(Rect)的图像,有了这个类我们就可以自定义一块矩形的区域,然后根据手势来移动矩形区域的位置就能慢慢看到整张图片了。
效果演示:
1.初始化变量
public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//第1步:设置bigImageView需要的成员变量
//加载显示的区域
mRect = new Rect();
mOptions = new BitmapFactory.Options();
//手勢识别
mGestureDetector = new GestureDetector(context, this);
//滚动类
mScroller = new Scroller(context);
//缩放手势识别
mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGesture() );
//设置触摸监听
setOnTouchListener(this);
}
BitmapFactory.Options我们很熟悉,用来配置Bitmap相关的参数,比如获取Bitmap的宽高,内存复用等参数。
GestureDetector用来识别双击事件,ScaleGestureDetector用来监听手指的缩放事件,都是系统提供的类,比较方便使用。
2.设置需要加载的图片
public void setImage(InputStream is) {
//获取图片的信息,不会将整张图片加载到内存
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, mOptions);
//获取图片的宽高信息
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
//开启内存复用
mOptions.inMutable = true;
//设置像素格式
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
//真正意义加载图片
mOptions.inJustDecodeBounds = false;
try {
//创建一个区域解码器
mDecoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
//刷新页面,与invalidate方法相反,只会触发onMeasure和onLayout方法,不会触发onDraw
requestLayout();
}
设置需要要加载的图片,无论图片放到哪里都可以拿到图片的一个输入流,所以参数使用输入流,通过BitmapFactory.Options拿到图片的真实宽高。
接下来是设置inMutable,开启内存复用。
关于像素格式设置,inPreferredConfig这个参数默认是Bitmap.Config.ARGB_8888,这里将它改成Bitmap.Config.RGB_565,去掉透明通道,可以减少一半的内存使用。
最后初始化区域解码器BitmapRegionDecoder。需要注意的是newInstance方法中参数false表示不共享输入流,可以理解为BitmapRegionDecoder拷贝了一份输入流,即获得的图片输入流close了,仍然可以进行解码操作。
requestLayout()刷新页面,需要注意与invalidate()的区别,后者请求重绘View树,即onDraw方法,不会触发onMeasure和onLayout方法,刚好相反。