标签:
先上几张效果图:
在加载多图片时,我们采用后进先出策略(即滑动到哪里就先加载哪里的图片),节省了内存的使用,也有了更好的用户体验。接着我们就先定义自己的ImageLoader。
①首先我们先定义一些基本的变量
private static final int MSG_ADDTASK = 0x001;
private LruCache<String, Bitmap> mLruCache;// 图片缓存核心对象,LruCache是android提供的一个缓存工具类,其算法是最近最少使用算法
private ExecutorService mThreadPool;// 线程池
private LinkedList<Runnable> mTaskQueue;// 任务队列
private Thread mPollThread;// 后台轮询线程
private Handler mUIThread;// UI线程的Handler,将图片回显到UI界面上
private Handler mPollThreadHandler;// 后台线程的Handler,给后台线程发送消息
private Semaphore mSemphorePollThreadHandler = new Semaphore(0);// 信号量,用于同步addTask()中与mPollThreadHandler的同步问题
private Semaphore mSemphoreTreadPool;// 控制线程池空闲的时候才去取线程执行
private Type mType = Type.LIFO;// 队列的调度模式,默认为后进先出
/**
* 队列的调度方法,图片的缓冲模式:先进先出(First In First Out),后进先出(Last In First Out)
*/
private enum Type {
FIFO, LIFO
}
②关于ImageLoader的使用直接调用ImageLoader.getInstance()获取,我们采用单例模式
private static ImageLoader mInstance;
private ImageLoader(int threadCount, Type type) {
init(threadCount, type);
}
public static ImageLoader getInstance() {
if (mInstance == null) {
synchronized (ImageLoader.class) {
if (mInstance == null) {
mInstance = new ImageLoader(3, Type.LIFO);// 设置线程池个数为3个,图片的加载模式是后进先加载
}
}
}
return mInstance;
}
③实现第一个init()方法,初始化一些变量以及后台线程
private void init(int threadCount, Type type) {
mType = type;
mSemphoreTreadPool = new Semaphore(3);// 设置信号量为3,当有第四个线程进入时就会阻塞
// 后台轮询线程
mPollThread = new Thread() {
@Override
public void run() {
Looper.prepare();// 准备Looper
mPollThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ADDTASK:
mThreadPool.execute(getTask());// 去线程中取出一个任务进行执行
try {
mSemphoreTreadPool.acquire();// 此时有三个线程在执行,当进来第四个线程的时候就会被阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
mSemphorePollThreadHandler.release();// 释放一个信号量,mSemphorePollThreadHandler.acquire的阻塞就会被取消,这里的作用是防止mPollThreadHandler还没有创建完毕,就发送消息,见addTask
break;
}
}
};
Looper.loop();// 开始loop,遍历消息
}
};
mPollThread.start();// 开启线程
int maxMemory = (int) Runtime.getRuntime().maxMemory();// 获取最大使用内存
int cacheMemory = maxMemory / 8;// 一般默认使用均为1/8
mLruCache = new LruCache<String, Bitmap>(cacheMemory) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getHeight() * bitmap.getRowBytes();// 每张图片占用的内存大小
}
};
mThreadPool = Executors.newFixedThreadPool(threadCount);// 设置线程数
mTaskQueue = new LinkedList<Runnable>();// 设置线程队列,使用LinkedList,便于获取任意位置的task
}
/** 添加一个任务 */
private synchronized void addTask(Runnable task) {
mTaskQueue.add(task);
try {
if(mPollThreadHandler == null) {
mSemphorePollThreadHandler.acquire();// 当mPollThreadHandler还没有建立的时候,此时就会被阻塞,直到mSemphorePollThreadHandler.realase
}
} catch (InterruptedException e) {
e.printStackTrace();
}
mPollThreadHandler.sendEmptyMessage(MSG_ADDTASK);
}
/** 获取一个任务 */
private Runnable getTask() {
if(mType == Type.FIFO) {
return mTaskQueue.removeFirst();
} else if(mType == Type.LIFO) {
return mTaskQueue.removeLast();
}
return null;
}
④接下来进入我们的核心方法loadImage(imageView,path),根据图片路径和imageView控件,把图片设置到imageView控件上。
public void loadImage(final ImageView imageView, final String path) {
imageView.setTag(path);// imageView设置一个Tag,防止图片加载过程中错乱
mUIThread = new Handler() {
@Override
public void handleMessage(Message msg) {
// 获取得到的图片,给imageView设置图片
ImageHolder imageHolder = (ImageHolder) msg.obj;
ImageView imageView = imageHolder.imageView;
Bitmap bitmap = imageHolder.bitmap;
String path = imageHolder.path;
if(imageView.getTag().toString().equals(path)) {
imageView.setImageBitmap(bitmap);
}
}
};
// 根据path从缓存中获取bitmap
Bitmap bm = getBitmapFromLruCache(path);
if (bm != null) {
sendBitmapToUIHandler(imageView, bm, path);
} else {
addTask(new Runnable(){
@Override
public void run() {
// 加载图片,图片压缩,放入缓冲中
// 获取图片的要显示的大小
ImageSize imageSize = getImageViewSize(imageView);
// 压缩图片
Bitmap bitmap = decodeSempledBitmapFromPath(path, imageSize);
// 把图片放入LruCache中
addBitmapToLruCache(bitmap, path);
// 发送给UIhandlder,刷新图片
sendBitmapToUIHandler(imageView, bitmap, path);
mSemphoreTreadPool.release();// 释放线程所占用的信号量
}
});
}
}
/** 发送图片信息到主线程中,从而更新图片 */
private void sendBitmapToUIHandler(ImageView imageView, Bitmap bitmap, String path) {
ImageHolder imageHolder = new ImageHolder();
imageHolder.imageView = imageView;
imageHolder.bitmap = bitmap;
imageHolder.path = path;
Message msg = Message.obtain();
msg.obj = imageHolder;
mUIThread.sendMessage(msg);
}
/**
* 给imageView设置图片的实体类,便于handler的数据传输
*/
private class ImageHolder {
ImageView imageView;
Bitmap bitmap;
String path;
}
/** imageView的大小 */
private class ImageSize {
int width;
int height;
}
关于图片处理的一些方法
/** 获取图片要显示的大小 */
private ImageSize getImageViewSize(ImageView imageView) {
DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
int width = imageView.getWidth();
if(width < 0) {
width = layoutParams.width;
}
if(width < 0) {
width = getFieldValue(imageView, "mMaxWidth");
}
if(width < 0) {
width = displayMetrics.widthPixels;
}
int height = imageView.getHeight();
if(height < 0) {
height = layoutParams.height;
}
if(height < 0) {
height = getFieldValue(imageView, "mMaxHeight");
}
if(height < 0) {
height = displayMetrics.heightPixels;
}
ImageSize imageSize = new ImageSize();
imageSize.height = height;
imageSize.width = width;
return imageSize;
}
/** 反射获取字段值 */
private int getFieldValue(Object obj, String fieldName) {
int value = 0;
try {
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldInt = field.getInt(obj);
if(fieldInt > 0 && fieldInt < Integer.MAX_VALUE){
value = fieldInt;
}
}catch (Exception e) {
e.printStackTrace();
}
return value;
}
/** 压缩图片 */
private Bitmap decodeSempledBitmapFromPath(String path, ImageSize imageSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;// 获取图片大小,并不把图片大小加入到内存中
BitmapFactory.decodeFile(path, options);
options.inSampleSize = caculateInSampleSize(options, imageSize);// 设置图片的压缩率
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return bitmap;
}
/** 计算出图片的最佳压缩比例 */
private int caculateInSampleSize(BitmapFactory.Options options, ImageSize imageSize) {
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;// 设置取样率
if(width > imageSize.width || height > imageSize.height) {
int widthRadio = Math.round(width * 1.0f / imageSize.width);
int heightRadio = Math.round(height * 1.0f / imageSize.height);
inSampleSize = Math.max(widthRadio, heightRadio);
}
return inSampleSize;
}
/** 把图片放入到LruCache中 */
private void addBitmapToLruCache(Bitmap bitmap, String path) {
if(getBitmapFromLruCache(path) == null) {
if(bitmap != null) {
mLruCache.put(path, bitmap);
}
}
}
================至此我们的ImageLoader定义完============================
MainActivity中获取手机图片代码
private void initData() {
// 利用ContentProvider扫描手机的照片
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Toast.makeText(this, "当前储存卡不可用", Toast.LENGTH_SHORT).show();
return;
}
// 扫描手机中的照片
new Thread() {
@Override
public void run() {
Uri mUriImg = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = MainActivity.this.getContentResolver();
String selection = MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?";
String[] selectionArgs = new String[]{"image/jpeg", "image/png"};
String sortOrder = MediaStore.Images.Media.DATE_MODIFIED;
Cursor cursor = contentResolver.query(mUriImg, null, selection, selectionArgs, sortOrder);
Set<String> dirPaths = new HashSet<String>();// 储存遍历过的parentPath,防止重复遍历
FolderBean bean = null;
while (cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));// 拿到图片路径
File parentFile = new File(path).getParentFile();// 拿到父文件夹
if (parentFile == null) {
continue;
}
String parentPath = parentFile.getAbsolutePath();
if (dirPaths.contains(parentPath)) {
continue;
}
String[] imgNames = parentFile.list(mFilter);// 获取文件夹下的所有图片
if(imgNames == null) {
continue;
}
dirPaths.add(parentPath);
bean = new FolderBean();
bean.setDirPath(parentPath);
bean.setFirstImgPath(path);
Log.d(TAG, path + "------------------------------------");
bean.setDirName(parentFile.getName());
bean.setImgCount(imgNames.length);
mFolderBeans.add(bean);
}
cursor.close();
bean = new FolderBean();
bean.setDirName("所有图片");
bean.setFirstImgPath(mFolderBeans.get(0).getFirstImgPath());
int imgCount = 0;
String imgDirPath = "";
for(FolderBean folderBean : mFolderBeans) {
imgCount += folderBean.getImageCount();
imgDirPath += folderBean.getDirPath() + SEPERATOR;
}
bean.setImgCount(imgCount);
bean.setDirPath(imgDirPath.substring(0, imgDirPath.length() - 1));
mFolderBeans.add(0, bean);
initShowImgData(bean);
// 扫描完毕,发送消息给handler
Message msg = handler.obtainMessage();
msg.what = MSG_DATA_LOADED;
msg.obj = bean;
handler.sendMessage(msg);
}
}.start();
}
标签:
原文地址:http://blog.csdn.net/linglingchenchen/article/details/51988820