项目中需要使用大图片展示用户帮助,本来以为是一个很简单的问题,直接用ImageView设置src属性,展示出来就行了。但是因为要展示app使用的多个步骤,所以图片特别的长而且需要上下滚动。在网上查询了如何加载大图片的实现方法,并且问过团队负责人,推荐用ListView展示这张用户帮助大图。
03-29 17:22:21.889 25554-25572/com.example.test W/OpenGLRenderer﹕ Bitmap too large to be uploaded into a texture (1080x4752, max=4096x4096)
这是自己在本地测试demo直接用BitmapFactory.decodeResource方法加载大图片的时候报告的错误信息。可以看到android对大图是有限制的,需要用多副小图来代替大图展示。实现原理很简单就是把大图片分割成多张连续的小图片,然后listView的每个条目展示一个小图片,这样就避免了大图片加载时OOM问题,同时保证了大图片能够上下滚动浏览。
最开始的时候打算让美工直接把这幅大图切成多副小图,然后直接以资源文件的形式放到图片目录里;但是考虑到以后可能项目的界面会经常调整,最终图片由服务器端返回。这样就需要客户端自己对整个图片做切割工作,查找了一下发现android有一个自带的工具类BitmapRegionDecoder能够分割大图片。
BitmapRegionDecoder类是2.3引入用来解决大图片加载异常问题,它能够从大图片中取出指定矩形大小的图片。可以使用InputStream输入流对象、FileDescriptor文件描述符和图像字节数组构造decoder对象,调用它的decodeRegion(rect,options),能够获得图像对应的矩形区域的子图像。
实现是用整个大图的高度除以每个小图片的高度获取展示的图片数量,从0开始遍历每个图片的矩形top与bottom,并且生成小图片。
try {
InputStream inputStream = getAssets().open("outdoor_help.png"); // 首先获得大图片的输入流
BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
int height = getPixels(100);
int originHeight = regionDecoder.getHeight(); // 获得大图片的高度
int count = originHeight % height == 0 ? originHeight / height : originHeight / height + 1; // 获得剪切后的小图片数量
List<Bitmap> bitmapList = new ArrayList<>();
Rect rect = new Rect();
rect.left = 0;
rect.right = regionDecoder.getWidth();
int lastHeight = height;
for (int i = 0; i < count; i++) { // 遍历生成小图片
rect.top = i * height; //确定小图片的top位置
lastHeight = (i + 1) * height;
rect.bottom = lastHeight > originHeight ? originHeight : lastHeight; // 确定小图片的底部位置
Bitmap bitmap = regionDecoder.decodeRegion(rect, null);
bitmapList.add(bitmap);
}
imageAdapter = new ImageAdapter(bitmapList, this);
listView.setAdapter(imageAdapter); // 使用listView展示多个小图片
regionDecoder.recycle(); // 注意回收对象,adapter里的bitmap在onDestroy后也要记得回收
} catch (IOException e) {
e.printStackTrace();
}