先上效果图:
如图,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; } }