先上效果图:
如图,android默认也有Gallery,很多软件在调用时,都是使用自己的Gallery,一方面好维护,另外一方面可以做优化。要做成以上样式,图片加载类起至关重要,一不小心,就好OOM, 下面这个类就是做Gallery的核心。
package com.example.gallery.utils;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
public class ImageLoader {
public static ImageLoader mInstance;
private LruCache<String, Bitmap> mLruCache;
private ExecutorService mThreadPool;
private static final int DEFAULT_THREAD_POOL_SIZE = 1;
private Type mType = Type.LIFO; //队列的调度方式
private LinkedList<Runnable> mTaskQueue; //任务队列,可以从头部和尾部取对象,链表不用连续的内存
private Thread mPoolThread; //后台轮询线程
private Handler mPoolThreadHandler;
private Handler mUIHandler; //UI线程
private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0); //信号量用来同步,默认申请0
private Semaphore mSemaphoreThreadPool;
public enum Type {
FIFO,LIFO;
}
private ImageLoader(int threadCount, Type type) {
init(threadCount, type);
}
private void init(int threadCount, Type type) {
//后台轮询线程
mPoolThread = new Thread() {
@Override
public void run() {
Looper.prepare();
mPoolThreadHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
//线程池去取出一个任务进行执行
mThreadPool.execute(getTask());
try {
mSemaphoreThreadPool.acquire(); //阻塞住
} catch (InterruptedException e) {
e.printStackTrace();
}
};
};
mSemaphorePoolThreadHandler.release();//释放信号量
Looper.loop();
};
};
mPoolThread.start();
//获取我们应用的最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheMemory = maxMemory / 8;
mLruCache = new LruCache<String, Bitmap>(cacheMemory){
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
};// 每行的字节数*高度
};
//创建线程池
mThreadPool = Executors.newFixedThreadPool(threadCount);
mTaskQueue = new LinkedList<Runnable>();
mType = type;
mSemaphoreThreadPool = new Semaphore(threadCount);
}
public static ImageLoader getInstance(int size, Type type) {
if(mInstance == null) {
//效率的提升,如果多个线程进入时
synchronized (ImageLoader.class) {
if(mInstance == null) { // 每次都new ,会产生多个对象
mInstance = new ImageLoader(DEFAULT_THREAD_POOL_SIZE, Type.LIFO);
}
}
}
return mInstance;
}
public void loadImage(final String path, final ImageView image) {
image.setTag(path);//防止多次复用
if(mUIHandler == null) {
mUIHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
//获取得到图片,为image回调设置图片
ImageBeanHolder holder = (ImageBeanHolder) msg.obj;
Bitmap bm = holder.bitmap;
ImageView imageview = holder.image;
String path = holder.path;
//将path与getTag存储路径进行比较
if(imageview.getTag().toString().equals(path)) {
imageview.setImageBitmap(bm);
}
};
};
}
Bitmap bm = getBitmapFromLruCache(path);
if (bm != null) {
refreshBitmap(path, image, bm);
} else {
addTask(new Runnable() {
@Override
public void run() {
//加载图片
//图片的压缩
//1.获得图片需要显示的大小
ImageSize imageViewSize = getImageViewSize(image);
//2.压缩图片
Bitmap bm = decodeSampleBitmapFromPath(path, imageViewSize.width, imageViewSize.height);
//3.把图片加入到缓存
addBitmapToLruCache(path,bm);
//4.进行回调
refreshBitmap(path, image, bm);
mSemaphoreThreadPool.release(); //任务完成,就施放信号量
}
});
}
}
//从任务队列中取任务
private Runnable getTask() {
if(mType == Type.FIFO) {
return mTaskQueue.removeFirst();
} else if(mType == Type.LIFO) {
return mTaskQueue.removeLast();
}
return null;
}
private void refreshBitmap(final String path, final ImageView image, Bitmap bm) {
Message msg = Message.obtain();
ImageBeanHolder holder = new ImageBeanHolder();
holder.bitmap = bm;
holder.image = image;
holder.path = path;
msg.obj = holder;
mUIHandler.sendMessage(msg);
}
//将图片加到LruCache
private void addBitmapToLruCache(String path, Bitmap bm) {
if(getBitmapFromLruCache(path) == null) {
if(bm != null) {
mLruCache.put(path, bm);
}
}
}
private ImageSize getImageViewSize(ImageView image) {
ImageSize imageSize = new ImageSize();
DisplayMetrics displayMetrics = image.getContext().getResources().getDisplayMetrics();
LayoutParams layoutParams = image.getLayoutParams();
int width = image.getWidth(); //获取实际宽度
// int width = getImageViewFiledValue(image, "mMaxWidth");
//layoutParams.width == LayoutParams.WRAP_CONTENT ? 0 :
if(width <= 0) { //wrap_content -1 fill_parent -2
width = layoutParams.width; //获取image在layout中声明的宽度
}
if(width <= 0) {
width = getImageViewFiledValue(image, "mMaxWidth"); //检查最大值
}
if(width <= 0) {
width = displayMetrics.widthPixels;//为屏幕的宽度
}
int height = image.getHeight(); //获取实际高度
//layoutParams.width == LayoutParams.WRAP_CONTENT ? 0 :
if(height <= 0) { //wrap_content -1 fill_parent -2
height = layoutParams.height; //获取image在layout中声明的高度
}
if(height <= 0) {
height = getImageViewFiledValue(image, "mMaxHeight"); //检查最大值
}
if(height <= 0) {
height = displayMetrics.heightPixels;//为屏幕的高度
}
imageSize.height = height;
imageSize.width = width;
return imageSize;
}
//为什么用反射,因为检查最大值是API 16才能用,兼容API 8 时,就用反射
private static int getImageViewFiledValue(Object object,String fieldName) {
int value = 0;
try {
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = field.getInt(object);
if(fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
value = fieldValue;
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return value;
}
//根据显示的宽和高对图片进行压缩
private Bitmap decodeSampleBitmapFromPath(String path, int width, int height) {
//获取图片的宽和高,并不把图片加载到内存中
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = caculateInSampleSize(options, width, height);
options.inJustDecodeBounds = false; //把图片加载到内存中
Bitmap bitmap = BitmapFactory.decodeFile(path, options);//已经进行压缩
return bitmap;
}
private int caculateInSampleSize(Options options, int reqWidth, int reqHeight) {
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if(width > reqWidth || height > reqHeight) {
int widthRadio = Math.round(1.0f * width / reqWidth);
int heightRadio = Math.round(1.0f * width / reqHeight);
inSampleSize = Math.max(widthRadio, heightRadio);
}
return inSampleSize;
}
private synchronized void addTask(Runnable runnable) {
mTaskQueue.add(runnable);
// if(mPoolThreadHandler == null)
// wait();
try {
if(mSemaphorePoolThreadHandler == null)
mSemaphorePoolThreadHandler.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
mPoolThreadHandler.sendEmptyMessage(0x110); //发送通知
}
private Bitmap getBitmapFromLruCache(String key) {
return mLruCache.get(key);
}
private class ImageSize {
int width;
int height;
}
private class ImageBeanHolder{ //防止错乱
Bitmap bitmap;
ImageView image;
String path;
}
}