做自己的软件的Gallery(一)
先上效果图:
如图,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;
}
}
做自己的软件的Gallery(一)的更多相关文章
- Nginx是什么,有什么优点?为什么选择Nginx做web服务器软件?(经典经典)
1.基础知识 代理服务器: 一般是指局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端.应用比如:GoAgent,FQ神器. 一个完整的代理请求过程为:客 ...
- 用RecyclerView做一个小清新的Gallery效果
一.简介 RecyclerView现在已经是越来越强大,且不说已经被大家用到滚瓜烂熟的代替ListView的基础功能,现在RecyclerView还可以取代ViewPager实现Banner效果,当然 ...
- 用RecyclerView做一个小清新的Gallery效果 - Ryan Lee的博客
一.简介 RecyclerView现在已经是越来越强大,且不说已经被大家用到滚瓜烂熟的代替ListView的基础功能,现在RecyclerView还可以取代ViewPager实现Banner效果,当然 ...
- PCAP文件格式分析(做抓包软件之必备)
转载源:http://blog.csdn.net/anzijin/article/details/2008333 http://www.ebnd.cn/2009/09/07/file-format-a ...
- C#做上位机软件——绘图并传输给下位机
拿到任务之后首先分成了几个部分: 1.绘图.学习了GDI+ 2.图片保存. 3.将图片转换成byte[].由于使用Socket通信,只能传输byte[]数据,所以这一步是向下位机传输的关键. 相应地, ...
- Surfer 软件做等值线图
使用surfer软件做等值线图 Surfer软件美国Golden Software公司编制的一款以画三维图(等高线,image map,3d surface)的软件. Surfer具有的强大插值功能和 ...
- 为什么要做一款ERP软件——开源软件诞生7
技术之外的探讨--第7篇 用日志记录“开源软件”的诞生 赤龙ERP开源地址: 点亮星标,感谢支持,与开发者交流 kzca2000 码云:https://gitee.com/redragon/redra ...
- 我的敏捷、需求分析、UML、软件设计电子书 - 下载(持续更新中)
我将所有我的电子书汇总在一起,方便大家下载!(持续更新) 文档保存在我的网站——软件知识原创基地上(www.umlonline.org),请放心下载. 1)软件设计是怎样炼成的?(2014-4-1 发 ...
- ubuntu一些基本软件安装方法
ubuntu一些基本软件安装方法 首先说明一下 ubuntu 的软件安装大概有几种方式:1. deb 包的安装方式deb 是 debian 系 Linux 的包管理方式, ubuntu 是属于 deb ...
随机推荐
- 在电脑上安装Linux操作系统
1硬件需求 A 一台电脑 B 一个优盘 2软件需求 A制作优盘启动盘的软件PowerISO BLinux操作系统的镜像文件 3安装PowerISO,并使用PowerISO A安装PowerISO B插 ...
- Android优化之ViewPager的懒加载
转载本博客请注明出处:点击打开链接 http://blog.csdn.net/qq_32059827/article/details/52487794 出于对用户消耗流量的考虑,有必要对view ...
- 你知道RxJava也可以实现AsyncTask吗?
使用RxJava实现异步操作(AsyncTask) 常见的异步操作我们可以联想到AsyncTask或者handler,其实google创造出的目的也就是为了让代码更加清晰明了,让代码更加简洁. 而Rx ...
- JAVA之旅(三十二)——JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用
JAVA之旅(三十二)--JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用 GUI写到一半电脑系统挂了,也就算了,最多GUI还有一个提示框和实例, ...
- 14 Fragment 碎片总结
Fragment 碎片 一, Fragment是什么? Android 3.0以后出现的 Api11 以上 Activity的组成部分 Fragment(小的Activity) Fragment可以显 ...
- Android多点触摸缩放图片-android学习之旅(四)
获取多触摸点 核心代码: 获取触摸点的个数和位置 public boolean onTouch(View v, MotionEvent event) { switch (event.getAction ...
- Error running app: Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled.
废了半天劲才解决... 就三步:菜单栏,Tools -> Adnroid -> enable ADB integration
- 新手学python(3):yield与序列化
1 Yield生成器 Yield是我在其他语言中没有见过的一个属性,算是python的一大特色,用好之后可以使代码更简洁.考虑一个简单的例子,文件的遍历.要遍历一个目录下的所有文件需要递归的操作.如果 ...
- Web Service进阶(七)浅谈SOAP Webservice和RESTful Webservice
浅谈SOAP Webservice和RESTful Webservice REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性.RE ...
- Android数据库框架——ORMLite轻量级的对象关系映射(ORM)Java包
Android数据库框架--ORMLite轻量级的对象关系映射(ORM)Java包 事实上,我想写数据库的念头已经很久了,在之前写了一个答题系统的小项目那只是初步的带了一下数据库,数据库是比较强大的, ...