做自己的软件的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 ...
随机推荐
- 安卓2.x的版本使用4.x的主题
现在,还有大部分安卓开发者在开发安卓APP时使用的是2.x的SDK版本,为了兼容2.x的手机这本倒无可厚非,但最令人头痛的就是2.x版本的主题是在太丑了,这是安卓刚推出时只考虑到了实用,并没考虑到美观 ...
- Dynamics CRM Trace Reader for Microsoft Dynamics CRM
CRM中抓取日志的视窗工作叫做Diagnastics Tools For Dyanmics CRM,这个工具我们只是作为一个开关来用就不做多介绍了,日志生成后是个文本文档可读性是很差的,那就需要个视窗 ...
- Linux/Unix--设备类型
在Linux以及所有的Unix系统中,设备被分为以下三种类型: 块设备 字符设备 网络设备 块设备通常写为 blkdev ,它是可以寻址的 ...
- mac OS X 从无法同步互联网时间想到的
最近在mac OS X 巨浪 :)上执行 ntpdate time.nist.gov 失败,提示 13 Jan 19:41:53 ntpdate[1374]: the NTP socket is in ...
- Java进阶(三十八)快速排序
Java进阶(三十八)快速排序 前言 有没有既不浪费空间又可以快一点的排序算法呢?那就是"快速排序"啦!光听这个名字是不是就觉得很高端呢. 假设我们现在对"6 1 2 7 ...
- 使用Swift开发一个MacOS的菜单状态栏App
猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/52054107 ...
- Java基础----Java---集合框架---泛型、泛型方法、静态方法泛型、泛型接口、泛型限定、泛型类
泛型:jdk1.5后的新特性,用于解决安全问题,是一个安全机制. 好处: 1.将运行时的异常出现问题classcastException.转移到了编译时期.方便程序员调试解决问题,让运行事情问题减少, ...
- 使用Broadcast实现android组件之间的通信
android组件之间的通信有多种实现方式,Broadcast就是其中一种.在activity和fragment之间的通信,broadcast用的更多本文以一个activity为例. 效果如图: 布局 ...
- 05 Activity生命周期
生命周期:一个Activity从创建到销毁经过的全部方法 1.onCreate() 创建一个Activity的时候执行的方法 2.onStart()Activity可以被看见到时候无法交互(没有焦点) ...
- 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型
关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...