viewPager实现动态加载图片,并且附带大图片的处理。
1.实现动态加载图片数量
将图片放在assets下这样就可以动态读取图片的数量,当图片的内容和数量改变的时候程序不需要修改。
private void initImage(){
try {
pics = this.getResources().getAssets().list("guide");
LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
//初始化引导图片列表
for(int i=0; i<pics.length; i++) {
ImageView iv = new ImageView(this);
iv.setLayoutParams(mParams);
GetBitMap.instance().add(this, mHandler,"guide/"+pics[i], UPDATEDATA,iv);
views.add(iv);
}
} catch (IOException e) {
e.printStackTrace();
}
}
2.当图片过大的时候,如果放在UI线程去读取图片很容易造成ANR,下面介绍一种异步线程加载图片并进行图片处理的方式
下面是异步加载图片的处理方式,而且不会开启很多子线程。
public void add(Context context,Handler handler,String path, int mCode,ImageView imageView){
BitMapMsg msg = new BitMapMsg();
msg.mConext = context;
msg.mHandler = handler;
msg.path = path;
msg.mCode = mCode;
msg.mImgView = imageView;
mQueue.add(msg);
if(!running){
new Thread(this).start();
}
}
public void run() {
running = true;
while(mQueue.size() > 0 ){
BitMapMsg msg = null;
synchronized (mQueue) {
msg = mQueue.remove(0);
}
if(msg != null && !msg.path.equals("")){
Message message = new Message();
message.what = msg.mCode;
if(msg.mImgView != null){
BitmapData bitmapData = new BitmapData(getBitmap(msg,msg.path),msg.mImgView);
message.getData().putParcelable("bitmap", bitmapData);
msg.mHandler.sendMessage(message);
}
}
}
running = false;
}
3 当图片过大的时候,加载图片的时候相信大家都遇到过内存溢出的问题,下面介绍一下处理bitmap的方式
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。
如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而跟有效阻止抛out of Memory异常
另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各种分辨率来自动适应,所以请大家注意适配的问题。
下面介绍一下Config的参数
其实这都是色彩的存储方法:我们知道ARGB指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue,其实所有的可见色都是右红绿蓝组成的,所以红绿蓝又称为三原色,每个原色都存储着所表示颜色的信息值
Bitmap.Config ALPHA_8 图形参数应该由一个字节来表示,应该是一种8位的位图
Bitmap.Config ARGB_4444 图形的参数应该由两个字节来表示 分别用4个bit来记录每个像素的A、R、G、B数据,16色位图
Bitmap.Config ARGB_8888 图形的参数应该由四个字节来表示 分别用8个bit来记录每个像素的A、R、G、B数据,就是常说的32bit位图、256色位图(这个也可能是RGB888这种24bit位图)
Bitmap.Config RGB_565 图形的参数应该由两个字节来表示 分别用5个、6个和5个bit记录像素的R、G、B数据,其中G的6个bit中一个是无效保留的,32色位图
位图位数越高代表其可以存储的颜色信息越多,当然图像也就越逼真
下面介绍下BitmapFactory.Options
设置恰当的inSampleSize是解决该问题的关键之一。BitmapFactory.Options提供了另一个成员inJustDecodeBounds。
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。
有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。
查看Android源码,Android提供了一种动态计算的方法。
/**
* 动态计算inSampleSize
* @param options
* @param minSideLength
* @param maxNumOfPixels
* @return
*/
public static int computeSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength,
maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math
.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}