做自己的软件的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 ...
随机推荐
- SQLite 数据类型(http://www.w3cschool.cc/sqlite/sqlite-data-types.html)
SQLite 数据类型 SQLite 数据类型是一个用来指定任何对象的数据类型的属性.SQLite 中的每一列,每个变量和表达式都有相关的数据类型. 您可以在创建表的同时使用这些数据类型.SQLite ...
- BeanUtils制作自定义的转换器
一般来说,BeanUtils自带的Converter基本上可以满足我们在开发过程中的使用了,然而很多时候我们还是需要自定义一些转换器. MyBean.java package beanutils; i ...
- 剑指Offer——Trie树(字典树)
剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...
- FFmpeg源代码简单分析:avformat_find_stream_info()
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- shell入门之expr的使用
在expr中加减乘除的使用,脚本如下: #!/bin/sh #a test about expr v1=`expr 5 + 6` echo "$v1" echo `expr 3 + ...
- java编程小记
http://blog.csdn.net/pipisorry/article/details/51050189 很久没写java,什么都不会了,小记一下. 类型转换 字符串转int类型:Integer ...
- Android日历视图(CalendarView)讲解-android学习之旅(三十六)
CalendarView简介 CalendarView用于显示和选择日期,如果希望监听事件的改变可以用setOnDateChangeListener()方法. CalendarView属性介绍 代码示 ...
- 论文系统Step1:从日志记录中提取特定信息
论文系统Step1:从日志记录中提取特定信息 前言 论文数据需要,需要实现从服务器日志中提取出用户的特定交互行为信息.日志内容如下: 自己需要获取"请求数据包一行的信息"及&quo ...
- Android进阶(六)文件读操作
Android中文件的读写操作与Java中文件的读写操作是有区别的.在Java中,读文件操作如以下代码所示: public class FileRead { private static final ...
- Uva - 210 - Concurrency Simulator
自己写个双端队列,或者直接用deque,这个也比较好用 AC代码: #include <iostream> #include <cstdio> #include <cst ...