读取位图的尺寸与类型
BitmapFactory(Creates Bitmap objects from various sources, including files, streams, and byte-arrays.)提供了一些解码的方法,用来从不同的资源中创建一个Bitmap。
这些方法在构造位图的时候会尝试分配内存,因此会容易导致OutOfMemory的异常。所以为了避免这个异常,我们需要在真正解析图片之前检查它的尺寸。
每一种解码方法都可以通过BitmapFactory.Options设置一些附加的标记,以此来指定解码选项。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
设置 inJustDecodeBounds 属性为true可以在解码的时候避免内存的分配,它会返回一个null的Bitmap,但是可以获取到 outWidth, outHeight 与 outMimeType
因此我们可以按比例缩小图片
下面一段是根据目标图片大小来计算Sample图片大小:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
加载任意大小的图片,如下:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
我练习这个,用GridView来加载图片。
自定义的Adapter:
public class PictureAdapter extends BaseAdapter {
Context context;
int[] reseIds;
int layId;
public PictureAdapter(Context context,int[] reseIds,int layId) {
super();
this.context=context;
this.reseIds = reseIds;
this.layId=layId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(context).inflate(layId,parent,false);
ImageView imageView = (ImageView)view.findViewById(R.id.item_image);
//imageView.setImageResource(reseIds[position]);
Log.d("getView",String.valueOf(reseIds[position]));
loadBitmap(reseIds[position],imageView);
return view;
}
@Override
public int getCount() {
return reseIds.length;
}
@Override
public Object getItem(int position) {
return reseIds[position];
}
@Override
public long getItemId(int position) {
return reseIds[position];
}
public void loadBitmap(int resId, ImageView imageView){
if(cancelPotentialWork(resId,imageView)){
BitmapWorkerTask task = new BitmapWorkerTask(imageView,context.getResources());
Bitmap holder = BitmapFactory.decodeResource(context.getResources(),resId);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(context.getResources(),holder,task);
imageView.setImageDrawable(asyncDrawable);
Log.d("load",String.valueOf(resId));
task.execute(resId);
}
// BitmapWorkerTask task = new BitmapWorkerTask(imageView,context.getResources());
// task.execute(resId);
}
static class AsyncDrawable extends BitmapDrawable{
private final WeakReference reference;
public AsyncDrawable(Resources resources, Bitmap bitmap,BitmapWorkerTask bitmapWorkerTask){
super(resources,bitmap);
reference= new WeakReference(bitmapWorkerTask);
}
public BitmapWorkerTask getBitTask(){
return (BitmapWorkerTask)reference.get();
}
}
public static boolean cancelPotentialWork(int data,ImageView imageView){
final BitmapWorkerTask bitmapWorkerTask = getBitmapTask(imageView);
if(bitmapWorkerTask !=null){
final int bitmapData = bitmapWorkerTask.data;
if(bitmapData == 0 || bitmapData != data){
bitmapWorkerTask.cancel(true);
}else {
return false;
}
}
return true;
}
public static BitmapWorkerTask getBitmapTask(ImageView imageView){
if(imageView !=null){
final Drawable drawable = imageView.getDrawable();
if(drawable instanceof AsyncDrawable){
final AsyncDrawable asyncDrawable = (AsyncDrawable)drawable;
return asyncDrawable.getBitTask();
}
}
return null;
}
}
在getView中调用loadBitmap(),用canclePotentialWork方法检查是否有另一个正在执行的任务与该Image关联了起来,如果关联了,就取消另一个任务。为什么要判断呢,因为我是用的AsyncTask来加载每一个图片。因为子Item视图会在用户滑动屏幕时被循环使用,如果每一个子视图都触发了一个AsyncTask,就没有办法确保关联的视图在结束任务时,分配的视图已经进入循环队列中,给另一个子视图进行重用。而且无法保证所有的异步任务的完成顺序和他们本身的启动顺序保存一致。(ListView也是一样的)(简单来说,就是不知道同一个ImageView是否在同一个任务中)
因此我们又专门创建一个Drawable的子类来存储任务的引用:
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
new WeakReference(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
getBitmapTask()是用作检索AsyncTask是否已经被分配到指定的ImageView中。
public static BitmapWorkerTask getBitmapTask(ImageView imageView){
if(imageView !=null){
final Drawable drawable = imageView.getDrawable();
if(drawable instanceof AsyncDrawable){
final AsyncDrawable asyncDrawable = (AsyncDrawable)drawable;
return asyncDrawable.getBitTask();
}
}
return null;
}
AsyncTask的代码:
public class BitmapWorkerTask extends AsyncTask<Integer,Integer,Bitmap> {
private final WeakReference imageViewReference;
public int data = 0;
Resources resources;
public BitmapWorkerTask(ImageView imageView,Resources resources){
imageViewReference = new WeakReference(imageView);
this.resources = resources;
}
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
Log.d("back",String.valueOf(data));
return decodeSampleBitmapFromResource(resources,data,100,150);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if(imageViewReference !=null && bitmap !=null){
final ImageView imageView = (ImageView) imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = PictureAdapter.getBitmapTask(imageView);
Log.d("post0","aaaaa");
if(this == bitmapWorkerTask && imageView != null){
Log.d("post","aaaaa");
imageView.setImageBitmap(bitmap);
}
// if(imageView !=null){
// imageView.setImageBitmap(bitmap);
// }
}
}
public static Bitmap decodeSampleBitmapFromResource(Resources resources,int resId,int reqWidth,int reqHeight){
final BitmapFactory.Options options= new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(resources,resId,options);
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(resources,resId,options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if(height > reqHeight || width > reqWidth){
final int halfHeight = height/2;
final int halfWidth = width/2;
while((halfHeight /inSampleSize)>reqHeight &&(halfWidth / inSampleSize)>reqWidth){
inSampleSize *=2;
}
}
return inSampleSize;
}
}