先上效果图:

如图,android默认也有Gallery,很多软件在调用时,都是使用自己的Gallery,一方面好维护,另外一方面可以做优化。要做成以上样式,图片加载类起至关重要,一不小心,就好OOM, 下面这个类就是做Gallery的核心。

  1. package com.example.gallery.utils;
  2.  
  3. import java.lang.reflect.Field;
  4. import java.util.LinkedList;
  5. import java.util.concurrent.ExecutorService;
  6. import java.util.concurrent.Executors;
  7. import java.util.concurrent.Semaphore;
  8.  
  9. import android.graphics.Bitmap;
  10. import android.graphics.BitmapFactory;
  11. import android.graphics.BitmapFactory.Options;
  12. import android.os.Handler;
  13. import android.os.Looper;
  14. import android.os.Message;
  15. import android.support.v4.util.LruCache;
  16. import android.util.DisplayMetrics;
  17. import android.view.ViewGroup.LayoutParams;
  18. import android.widget.ImageView;
  19.  
  20. public class ImageLoader {
  21.  
  22. public static ImageLoader mInstance;
  23. private LruCache<String, Bitmap> mLruCache;
  24. private ExecutorService mThreadPool;
  25. private static final int DEFAULT_THREAD_POOL_SIZE = 1;
  26. private Type mType = Type.LIFO; //队列的调度方式
  27. private LinkedList<Runnable> mTaskQueue; //任务队列,可以从头部和尾部取对象,链表不用连续的内存
  28. private Thread mPoolThread; //后台轮询线程
  29. private Handler mPoolThreadHandler;
  30. private Handler mUIHandler; //UI线程
  31. private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0); //信号量用来同步,默认申请0
  32. private Semaphore mSemaphoreThreadPool;
  33.  
  34. public enum Type {
  35. FIFO,LIFO;
  36. }
  37. private ImageLoader(int threadCount, Type type) {
  38. init(threadCount, type);
  39. }
  40.  
  41. private void init(int threadCount, Type type) {
  42. //后台轮询线程
  43. mPoolThread = new Thread() {
  44. @Override
  45. public void run() {
  46. Looper.prepare();
  47. mPoolThreadHandler = new Handler(){
  48. public void handleMessage(android.os.Message msg) {
  49. //线程池去取出一个任务进行执行
  50. mThreadPool.execute(getTask());
  51. try {
  52. mSemaphoreThreadPool.acquire(); //阻塞住
  53. } catch (InterruptedException e) {
  54. e.printStackTrace();
  55. }
  56. };
  57. };
  58. mSemaphorePoolThreadHandler.release();//释放信号量
  59. Looper.loop();
  60. };
  61. };
  62. mPoolThread.start();
  63. //获取我们应用的最大可用内存
  64. int maxMemory = (int) Runtime.getRuntime().maxMemory();
  65. int cacheMemory = maxMemory / 8;
  66. mLruCache = new LruCache<String, Bitmap>(cacheMemory){
  67. protected int sizeOf(String key, Bitmap value) {
  68.  
  69. return value.getRowBytes() * value.getHeight();
  70. };// 每行的字节数*高度
  71. };
  72. //创建线程池
  73. mThreadPool = Executors.newFixedThreadPool(threadCount);
  74. mTaskQueue = new LinkedList<Runnable>();
  75. mType = type;
  76. mSemaphoreThreadPool = new Semaphore(threadCount);
  77. }
  78.  
  79. public static ImageLoader getInstance(int size, Type type) {
  80. if(mInstance == null) {
  81. //效率的提升,如果多个线程进入时
  82. synchronized (ImageLoader.class) {
  83. if(mInstance == null) { // 每次都new ,会产生多个对象
  84. mInstance = new ImageLoader(DEFAULT_THREAD_POOL_SIZE, Type.LIFO);
  85. }
  86. }
  87. }
  88. return mInstance;
  89. }
  90.  
  91. public void loadImage(final String path, final ImageView image) {
  92. image.setTag(path);//防止多次复用
  93. if(mUIHandler == null) {
  94. mUIHandler = new Handler() {
  95. public void handleMessage(android.os.Message msg) {
  96. //获取得到图片,为image回调设置图片
  97. ImageBeanHolder holder = (ImageBeanHolder) msg.obj;
  98. Bitmap bm = holder.bitmap;
  99. ImageView imageview = holder.image;
  100. String path = holder.path;
  101. //将path与getTag存储路径进行比较
  102. if(imageview.getTag().toString().equals(path)) {
  103. imageview.setImageBitmap(bm);
  104. }
  105.  
  106. };
  107. };
  108. }
  109. Bitmap bm = getBitmapFromLruCache(path);
  110. if (bm != null) {
  111. refreshBitmap(path, image, bm);
  112. } else {
  113. addTask(new Runnable() {
  114.  
  115. @Override
  116. public void run() {
  117. //加载图片
  118. //图片的压缩
  119. //1.获得图片需要显示的大小
  120. ImageSize imageViewSize = getImageViewSize(image);
  121. //2.压缩图片
  122. Bitmap bm = decodeSampleBitmapFromPath(path, imageViewSize.width, imageViewSize.height);
  123. //3.把图片加入到缓存
  124. addBitmapToLruCache(path,bm);
  125. //4.进行回调
  126. refreshBitmap(path, image, bm);
  127. mSemaphoreThreadPool.release(); //任务完成,就施放信号量
  128. }
  129. });
  130. }
  131. }
  132.  
  133. //从任务队列中取任务
  134. private Runnable getTask() {
  135. if(mType == Type.FIFO) {
  136. return mTaskQueue.removeFirst();
  137. } else if(mType == Type.LIFO) {
  138. return mTaskQueue.removeLast();
  139. }
  140. return null;
  141. }
  142.  
  143. private void refreshBitmap(final String path, final ImageView image, Bitmap bm) {
  144. Message msg = Message.obtain();
  145. ImageBeanHolder holder = new ImageBeanHolder();
  146. holder.bitmap = bm;
  147. holder.image = image;
  148. holder.path = path;
  149. msg.obj = holder;
  150. mUIHandler.sendMessage(msg);
  151. }
  152.  
  153. //将图片加到LruCache
  154. private void addBitmapToLruCache(String path, Bitmap bm) {
  155. if(getBitmapFromLruCache(path) == null) {
  156. if(bm != null) {
  157. mLruCache.put(path, bm);
  158. }
  159. }
  160. }
  161.  
  162. private ImageSize getImageViewSize(ImageView image) {
  163. ImageSize imageSize = new ImageSize();
  164. DisplayMetrics displayMetrics = image.getContext().getResources().getDisplayMetrics();
  165. LayoutParams layoutParams = image.getLayoutParams();
  166. int width = image.getWidth(); //获取实际宽度
  167. // int width = getImageViewFiledValue(image, "mMaxWidth");
  168. //layoutParams.width == LayoutParams.WRAP_CONTENT ? 0 :
  169. if(width <= 0) { //wrap_content -1 fill_parent -2
  170. width = layoutParams.width; //获取image在layout中声明的宽度
  171. }
  172. if(width <= 0) {
  173. width = getImageViewFiledValue(image, "mMaxWidth"); //检查最大值
  174. }
  175. if(width <= 0) {
  176. width = displayMetrics.widthPixels;//为屏幕的宽度
  177. }
  178.  
  179. int height = image.getHeight(); //获取实际高度
  180. //layoutParams.width == LayoutParams.WRAP_CONTENT ? 0 :
  181. if(height <= 0) { //wrap_content -1 fill_parent -2
  182. height = layoutParams.height; //获取image在layout中声明的高度
  183. }
  184. if(height <= 0) {
  185. height = getImageViewFiledValue(image, "mMaxHeight"); //检查最大值
  186. }
  187. if(height <= 0) {
  188. height = displayMetrics.heightPixels;//为屏幕的高度
  189. }
  190. imageSize.height = height;
  191. imageSize.width = width;
  192. return imageSize;
  193. }
  194.  
  195. //为什么用反射,因为检查最大值是API 16才能用,兼容API 8 时,就用反射
  196. private static int getImageViewFiledValue(Object object,String fieldName) {
  197. int value = 0;
  198. try {
  199. Field field = ImageView.class.getDeclaredField(fieldName);
  200. field.setAccessible(true);
  201. int fieldValue = field.getInt(object);
  202. if(fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
  203. value = fieldValue;
  204. }
  205. } catch (NoSuchFieldException e) {
  206. e.printStackTrace();
  207. } catch (IllegalAccessException e) {
  208. e.printStackTrace();
  209. } catch (IllegalArgumentException e) {
  210. e.printStackTrace();
  211. }
  212. return value;
  213. }
  214.  
  215. //根据显示的宽和高对图片进行压缩
  216. private Bitmap decodeSampleBitmapFromPath(String path, int width, int height) {
  217.  
  218. //获取图片的宽和高,并不把图片加载到内存中
  219. BitmapFactory.Options options = new BitmapFactory.Options();
  220.  
  221. options.inJustDecodeBounds = true;
  222. BitmapFactory.decodeFile(path, options);
  223. options.inSampleSize = caculateInSampleSize(options, width, height);
  224.  
  225. options.inJustDecodeBounds = false; //把图片加载到内存中
  226. Bitmap bitmap = BitmapFactory.decodeFile(path, options);//已经进行压缩
  227. return bitmap;
  228. }
  229.  
  230. private int caculateInSampleSize(Options options, int reqWidth, int reqHeight) {
  231. int width = options.outWidth;
  232. int height = options.outHeight;
  233. int inSampleSize = 1;
  234. if(width > reqWidth || height > reqHeight) {
  235. int widthRadio = Math.round(1.0f * width / reqWidth);
  236. int heightRadio = Math.round(1.0f * width / reqHeight);
  237. inSampleSize = Math.max(widthRadio, heightRadio);
  238. }
  239. return inSampleSize;
  240. }
  241.  
  242. private synchronized void addTask(Runnable runnable) {
  243. mTaskQueue.add(runnable);
  244. // if(mPoolThreadHandler == null)
  245. // wait();
  246. try {
  247. if(mSemaphorePoolThreadHandler == null)
  248. mSemaphorePoolThreadHandler.acquire();
  249. } catch (InterruptedException e) {
  250. e.printStackTrace();
  251. }
  252. mPoolThreadHandler.sendEmptyMessage(0x110); //发送通知
  253. }
  254.  
  255. private Bitmap getBitmapFromLruCache(String key) {
  256. return mLruCache.get(key);
  257. }
  258.  
  259. private class ImageSize {
  260. int width;
  261. int height;
  262. }
  263.  
  264. private class ImageBeanHolder{ //防止错乱
  265. Bitmap bitmap;
  266. ImageView image;
  267. String path;
  268. }
  269. }

做自己的软件的Gallery(一)的更多相关文章

  1. Nginx是什么,有什么优点?为什么选择Nginx做web服务器软件?(经典经典)

    1.基础知识 代理服务器:    一般是指局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端.应用比如:GoAgent,FQ神器.    一个完整的代理请求过程为:客 ...

  2. 用RecyclerView做一个小清新的Gallery效果

    一.简介 RecyclerView现在已经是越来越强大,且不说已经被大家用到滚瓜烂熟的代替ListView的基础功能,现在RecyclerView还可以取代ViewPager实现Banner效果,当然 ...

  3. 用RecyclerView做一个小清新的Gallery效果 - Ryan Lee的博客

    一.简介 RecyclerView现在已经是越来越强大,且不说已经被大家用到滚瓜烂熟的代替ListView的基础功能,现在RecyclerView还可以取代ViewPager实现Banner效果,当然 ...

  4. PCAP文件格式分析(做抓包软件之必备)

    转载源:http://blog.csdn.net/anzijin/article/details/2008333 http://www.ebnd.cn/2009/09/07/file-format-a ...

  5. C#做上位机软件——绘图并传输给下位机

    拿到任务之后首先分成了几个部分: 1.绘图.学习了GDI+ 2.图片保存. 3.将图片转换成byte[].由于使用Socket通信,只能传输byte[]数据,所以这一步是向下位机传输的关键. 相应地, ...

  6. Surfer 软件做等值线图

    使用surfer软件做等值线图 Surfer软件美国Golden Software公司编制的一款以画三维图(等高线,image map,3d surface)的软件. Surfer具有的强大插值功能和 ...

  7. 为什么要做一款ERP软件——开源软件诞生7

    技术之外的探讨--第7篇 用日志记录“开源软件”的诞生 赤龙ERP开源地址: 点亮星标,感谢支持,与开发者交流 kzca2000 码云:https://gitee.com/redragon/redra ...

  8. 我的敏捷、需求分析、UML、软件设计电子书 - 下载(持续更新中)

    我将所有我的电子书汇总在一起,方便大家下载!(持续更新) 文档保存在我的网站——软件知识原创基地上(www.umlonline.org),请放心下载. 1)软件设计是怎样炼成的?(2014-4-1 发 ...

  9. ubuntu一些基本软件安装方法

    ubuntu一些基本软件安装方法 首先说明一下 ubuntu 的软件安装大概有几种方式:1. deb 包的安装方式deb 是 debian 系 Linux 的包管理方式, ubuntu 是属于 deb ...

随机推荐

  1. Html书写规范,基本标签使用

    一.html简介1.html是什么Html是用来描述网页的一种语言.(1)HTML 指的是超文本标记语言 (Hyper Text Markup Language)(2)HTML 不是一种编程语言,而是 ...

  2. 闪屏页面开发遇到的问题you need to use a theme.appcompat theme (or descendant)

    开始做一个新闻客户端的应用,在做到闪屏页面时之前发布应用的时候总是报错,原因是我在splash.java中把Activty写成ActionBarActivity,导包,然后就可以了.以前也遇到过这种情 ...

  3. Matlab:如何读取CSV文件以及如何读取带有字符串数据项的CSV文件

    CSV,逗号分开的文件,如果能快速的读取这些文件中的数据,无疑会帮助我们解决很多问题. 1. 只有数据的CSV文件,CSV file that includes only numbers. As an ...

  4. 关于activitygroup过时,用frament替换操作

    现在Fragment的应用真的是越来越广泛了,之前Android在3.0版本加入Fragment的时候,主要是为了解决Android Pad屏幕比较大,空间不能充分利用的问题,但现在即使只是在手机上, ...

  5. API创建员工Element

    DECLARE ln_element_link_id PAY_ELEMENT_LINKS_F.ELEMENT_LINK_ID%TYPE; ld_effective_start_date DATE; l ...

  6. 查全率(召回率)、精度(准确率)和F值

    文献中的recall rate(查全率或召回率) and precision(精度)是很重要的概念.可惜很多中文网站讲的我都稀里糊涂,只好用google查了个英文的,草翻如下:召回率和精度定义: 从一 ...

  7. Java spi机制浅谈

    最近看到公司的一些框架和之前看到的开源的一些框架的一些服务发现和接入都采用了java的spi机制. 所以简单的总结下java spi机制的思想. 我们系统里抽象的各个模块,往往有很多不同的实现方案,比 ...

  8. 【java虚拟机系列】java中类与对象的加载顺序

    首先了解一下Java虚拟机初始化的原理. JVM通过加装.连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用.类型的生命周期如下图所示: 装载和连接必须在初始化之前就要完成. 类 ...

  9. Java中httpClient中三种超时设置

    本文章给大家介绍一下关于Java中httpClient中的三种超时设置小结 在Apache的HttpClient包中,有三个设置超时的地方: /* 从连接池中取连接的超时时间*/ ConnManage ...

  10. 15个易遗忘的java知识点

    1.java中的基本数据类型以及所占内存大小 (1)整形  byte 1字节  short 2字节  int 4字节  long 8字节  (2)浮点型  float 4字节  double 8字节  ...