您的位置:首页 > 其它

高效的加载图片整理

2017-07-04 22:37 155 查看
高效的加载图片

图片的缩放

public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) {

// Raw height and width of image

final int height = options.outHeight;

有效地加载大尺寸位图(Loading Large Bitmaps Efficiently)

读取位图的尺寸与类型(Read Bitmap Dimensions and Type)

加载一个按比例缩小的版本到内存中(Load a Scaled Down

Version into Memory)

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;

}


AsyncTask 类提供了一个简单的方法来在后台线程执行一些操作,并且可以把后台的结果呈现到UI线程。下面是一个加载大

图的示例:

class BitmapWorkerTask extends AsyncTask {

private final WeakReference imageViewReference;

private int data = 0;

public BitmapWorkerTask(ImageView imageView) {

// Use a WeakReference to ensure the ImageView can be garbage collected

imageViewReference = new WeakReference(imageView);

}

// Decode image in background.

@Override

protected Bitmap doInBackground(Integer... params) {

data = params[0];

return decodeSampledBitmapFromResource(getResources(), data, 100, 100));

}

// Once complete, see if ImageView is still around and set bitmap.

@Override

protected void onPostExecute(Bitmap bitmap) {

if (imageViewReference != null && bitmap != null) {

final ImageView imageView = imageViewReference.get();

if (imageView != null) {

imageView.setImageBitmap(bitmap);

}

}

}

}


为ImageView使用WeakReference 确保了 AsyncTask 所引用的资源可以被GC(garbage collected)。因为当任务结束时不能

确保 ImageView 仍然存在,因此你必须在 onPostExecute() 里面去检查引用。这个ImageView 也许已经不存在了,例如,

在任务结束时用户已经不在那个Activity或者是设备已经发生配置改变(旋转屏幕等)。

开始异步加载位图,只需要创建一个新的任务并执行它即可:

public void loadBitmap(int resId, ImageView imageView) {

BitmapWorkerTask task = new BitmapWorkerTask(imageView);

task.execute(resId);

}


处理并发问题(Handle Concurrency)

Multithreading for Performance 这篇博文更进一步的讨论了如何处理并发并且提供了一种解决方法,当任务结束时

ImageView 保存一个最近常使用的AsyncTask引用。使用类似的方法, AsyncTask 可以扩展出一个类似的模型。创建一个专

用的 Drawable 子类来保存一个可以回到当前工作任务的引用。在这种情况下,BitmapDrawable 被用来作为占位图片,它可

以在任务结束时显示到ImageView中。

创建一个专用的Drawable的子类来储存返回工作任务的引用。在这种情况下,当任务完成时BitmapDrawable会被使用,

placeholder image才会在ImageView中被显示:

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();

}

}


在执行BitmapWorkerTask 之前,你需要创建一个 AsyncDrawable 并且绑定它到目标组件 ImageView 中:

public void loadBitmap(int resId, ImageView imageView) {

if (cancelPotentialWork(resId, imageView)) {

final BitmapWorkerTask task = new BitmapWorkerTask(imageView);

final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);

imageView.setImageDrawable(asyncDrawable);

task.execute(resId);

}

}


在上面的代码示例中, cancelPotentialWork 方法检查确保了另外一个在ImageView中运行的任务得以取消。如果是这样,它

通过执行 cancel() 方法来取消之前的一个任务. 在小部分情况下, New出来的任务有可能已经存在,这样就不需要执行这个任

务了。下面演示了如何实现一个 cancelPotentialWork 。

public static boolean cancelPotentialWork(int data, ImageView imageView) {

final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

if (bitmapWorkerTask != null) {

final int bitmapData = bitmapWorkerTask.data;

if (bitmapData == 0 || bitmapData != data) {

// Cancel previous task

bitmapWorkerTask.cancel(true);

} else {

// The same work is already in progress

return false;

}

}

// No task associated with the ImageView, or an existing task was cancelled

return true;

}


在上面有一个帮助方法, getBitmapWorkerTask() , 被用作检索任务是否已经被分配到指定的 ImageView:

private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {

if (imageView != null) {

final Drawable drawable = imageView.getDrawable();

if (drawable instanceof AsyncDrawable) {

final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;

return asyncDrawable.getBitmapWorkerTask();

}

}

return null;

}


最后一步是在BitmapWorkerTask 的 onPostExecute() 方法里面做更新操作:

class BitmapWorkerTask extends AsyncTask {

...

@Override

protected void onPostExecute(Bitmap bitmap) {

if (isCancelled()) {

bitmap = null;

}

if (imageViewReference != null && bitmap != null) {

final ImageView imageView = imageViewReference.get();

final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

if (this == bitmapWorkerTask && imageView != null) {

imageView.setImageBitmap(bitmap);

}

}

}

}


这个方法不仅仅适用于 ListView 与 GridView 组件,在那些需要循环利用子视图的组件中同样适用。只需要在设置图片到

ImageView的地方调用 loadBitmap 方法。例如,在GridView 中实现这个方法会是在 getView() 方法里面调用。

BitmapWorkerTask task = new BitmapWorkerTask(mImageView);

task.execute(resId);


完整的示例代码:

private class ImageAdapter extends BaseAdapter {

...

@Override

public View getView(int position, View convertView, ViewGroup container) {

...

loadBitmap(imageResIds[position], imageView)

return imageView;

}

public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); } }
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(); } }
public static boolean cancelPotentialWork(int data, ImageView imageView) {

final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

if (bitmapWorkerTask != null) {

final int bitmapData = bitmapWorkerTask.data;

if (bitmapData != data) {

// Cancel previous task

bitmapWorkerTask.cancel(true);

} else {

// The same work is already in progress

return false;

}

}

// No task associated with the ImageView, or an existing task was cancelled

return true;

}

private static BitmapWorkerTask getBitmapWorkerTask(ImageView
4000
imageView) {

if (imageView != null) {

final Drawable drawable = imageView.getDrawable();

if (drawable instanceof AsyncDrawable) {

final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;

return asyncDrawable.getBitmapWorkerTask();

}

}

return null;

}

}


声明:资源 摘自:Google Android官方培训教程中文版
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: