官方demo摘录:
计算inSampleSize的方法
/**
* 计算inSampleSize,例如, 一个分辨率为2048x1536的图片,如果设置 inSampleSize 为4,那么会产出一个大约512x384大小的Bitmap。
* @param options
* @param reqWidth 想要压缩到的宽度
* @param reqHeight 想要压缩到的高度
* @return
*/
private 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;
//计算最大inSampleSize值是2的幂,并保持高度和宽度大于所要求的高度和宽度。
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth
) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
获取Bitmap
/**
* 获取Bitmap, 是根据屏幕宽高来压缩的,可自行修改
* @param imageId 图片资源id
* @return
*/
private Bitmap decodeSampledBitmapFromResourse(int imageId) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//不加载图片,只获取图片的宽高和type
BitmapFactory.decodeResource(getResources(), imageId, options);
//获取屏幕宽高
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int width = metric.widthPixels; // 屏幕宽度(像素)
int height = metric.heightPixels; // 屏幕高度(像素)
options.inSampleSize = calculateInSampleSize(options, width ,height);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), imageId, options);
}
使用
Bitmap bitmap = decodeSampledBitmapFromResourse(R.drawable.zhizhuxia);
imageView.setImageBitmap(bitmap);
当然这些处理不应该放在ui线程中
是用那个AsyncTask异步处理耗时任务:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap>{
/**
* 为ImageView使用WeakReference确保了AsyncTask所引用的资源可以被垃圾回收器回收。
* 由于当任务结束时不能确保ImageView仍然存在,因此我们必须在onPostExecute()里面对引用进行检查。
* 该ImageView在有些情况下可能已经不存在了,例如,在任务结束之前用户使用了回退操作,或者是配置发生了改变(如旋转屏幕等)。
*/
private final WeakReference<ImageView> imageViewWeakReference;
private int imageId;
public BitmapWorkerTask(ImageView imageView){
imageViewWeakReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(Integer... params) {
imageId = params[0];
return decodeSampledBitmapFromResourse(imageId);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewWeakReference != null && bitmap != null){
final ImageView imageView = imageViewWeakReference.get();
if (imageView != null){
imageView.setImageBitmap(bitmap);
}
}
}
}
如下使用:
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(imageId);
为了适应多种并发操作,可做如下操作
/**
* 异步任务为 imageView加载大图, 适用 ListView 和 gridView ,所有方法已封装,在适当的位置调用该方法即可
* @param imageId
* @param imageView
*/
public void loadBitmap(int imageId, ImageView imageView){
if (cancelPotentialWork(imageId, imageView)){
final BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(bitmapWorkerTask);
imageView.setImageDrawable(asyncDrawable);
bitmapWorkerTask.execute(imageId);
}
}
/**
* 用来存储 BitmapWorkerTask 与之相应的 imageView 关联, 使用 {@link #getBitmapWorkerTask(ImageView)} 来获取任务
*/
static class AsyncDrawable extends BitmapDrawable{
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(BitmapWorkerTask bitmapWorkerTask) {
bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask(){
return bitmapWorkerTaskReference.get();
}
}
/**
* 判定当前与imageView绑定的任务
* @param imageId
* @param imageView
* @return
*/
public static boolean cancelPotentialWork(int imageId, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null){
final int bitmapId = bitmapWorkerTask.imageId;
if (bitmapId == 0 || bitmapId != imageId){
// 当前任务不是加载 imageId资源的异步任务,取消该任务
bitmapWorkerTask.cancel(true);
}else{
// 当前任务正在加载 imageId资源,没有其他任务占用
return false;
}
}
//没有任务与 imageView 绑定
return true;
}
/**
* 获取与ImageView相关联的 BitmapWorkerTask
* @param imageView
* @return
*/
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;
}
/**
* 异步任务,为ImageView加载大图
*/
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap>{
/**
* 为ImageView使用WeakReference确保了AsyncTask所引用的资源可以被垃圾回收器回收。
* 由于当任务结束时不能确保ImageView仍然存在,因此我们必须在onPostExecute()里面对引用进行检查。
* 该ImageView在有些情况下可能已经不存在了,例如,在任务结束之前用户使用了回退操作,或者是配置发生了改变(如旋转屏幕等)。
*/
private final WeakReference<ImageView> imageViewWeakReference;
private int imageId;
public BitmapWorkerTask(ImageView imageView){
imageViewWeakReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(Integer... params) {
imageId = params[0];
return decodeSampledBitmapFromResourse(imageId);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()){
bitmap = null;
}
if (imageViewWeakReference != null && bitmap != null){
final ImageView imageView = imageViewWeakReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null){
imageView.setImageBitmap(bitmap);
}
}
}
}
使用内存缓存存储已处理的图片(Api 12及以上 )
private LruCache<String, Bitmap> mMemoryCache;
//在onCreate中获取缓存空间
/** 内存缓存 **/
//获取最大可用的vm内存,超过这个值就会抛出内存溢出异常。
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//使用1/8 maxMemory来作为缓存空间
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
//最低api 12,以kb为单位
return value.getByteCount() / 1024;
}
};
从缓存添加和获取bitmap方法:
/**
* 添加 bitmap到缓存中
* @param key
* @param bitmap
*/
public void addBitmapToMemoryCache(String key, Bitmap bitmap){
if (getBitmapFromLruMemCache(key) == null){
mMemoryCache.put(key, bitmap);
}
}
/**
* 从缓存中获取 Bitmap
* @param key
* @return
*/
public Bitmap getBitmapFromLruMemCache(String key){
return mMemoryCache.get(key);
}
修改BitmapWorkerTask
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
@Override
protected Bitmap doInBackground(Integer... params) {
imageId = params[0];
final Bitmap bitmap = decodeSampledBitmapFromResourse(imageId);
//将处理后的bitmap添加到缓存中
addBitmapToMemoryCache(String.valueOf(imageId), bitmap);
return bitmap;
}
...
}
改进loadBitmap方法:
/**
* 异步任务为 imageView加载大图, 适用 ListView 和 gridView
* @param imageId
* @param imageView
*/
public void loadBitmap(int imageId, ImageView imageView){
//首先在缓存中查找
final String imgKey = String.valueOf(imageId);
final Bitmap bitmap = getBitmapFromLruMemCache(imgKey);
if (bitmap != null){
imageView.setImageBitmap(bitmap);
}else if (cancelPotentialWork(imageId, imageView)){
final BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(bitmapWorkerTask);
imageView.setImageDrawable(asyncDrawable);
bitmapWorkerTask.execute(imageId);
}
}