Android照片墙完整版,完美结合LruCache和DiskLruCache

转载地址:http://blog.csdn.net/guolin_blog/article/details/34093441#comments

在上一篇文章当中,我们学习了DiskLruCache的概念和基本用法,但仅仅是掌握理论知识显然是不够的,那么本篇文章我们就来继续进阶一下,看一看在实战当中应该怎样合理使用DiskLruCache。还不熟悉DiskLruCache用法的朋友可以先去参考我的上一篇文章 Android DiskLruCache完全解析,硬盘缓存的最佳方案 。

其实,在真正的项目实战当中如果仅仅是使用硬盘缓存的话,程序是有明显短板的。而如果只使用内存缓存的话,程序当然也会有很大的缺陷。因此,一个优秀的程序必然会将内存缓存和硬盘缓存结合到一起使用,那么本篇文章我们就来看一看,如何才能将LruCache和DiskLruCache完美结合到一起。

在 Android照片墙应用实现,再多的图片也不怕崩溃 这篇文章当中,我编写了一个照片墙的应用程序,但当时只是单纯使用到了内存缓存而已,而今天我们就对这个例子进行扩展,制作一个完整版的照片墙。

那我们开始动手吧,新建一个Android项目,起名叫PhotoWallDemo,这里我使用的是Android 4.0的API。然后新建一个libcore.io包,并将DiskLruCache.java文件拷贝到这个包下,这样就把准备工作完成了。

接下来首先需要考虑的仍然是图片源的问题,简单起见,我仍然是吧所有图片都上传到了我的CSDN相册当中,然后新建一个Images类,将所有相册中图片的网址都配置进去,代码如下所示:

[java] view plaincopy

  1. public class Images {
  2. public final static String[] imageThumbUrls = new String[] {
  3. "http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg",
  4. "http://img.my.csdn.net/uploads/201407/26/1406383291_6518.jpg",
  5. "http://img.my.csdn.net/uploads/201407/26/1406383291_8239.jpg",
  6. "http://img.my.csdn.net/uploads/201407/26/1406383290_9329.jpg",
  7. "http://img.my.csdn.net/uploads/201407/26/1406383290_1042.jpg",
  8. "http://img.my.csdn.net/uploads/201407/26/1406383275_3977.jpg",
  9. "http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg",
  10. "http://img.my.csdn.net/uploads/201407/26/1406383264_3954.jpg",
  11. "http://img.my.csdn.net/uploads/201407/26/1406383264_4787.jpg",
  12. "http://img.my.csdn.net/uploads/201407/26/1406383264_8243.jpg",
  13. "http://img.my.csdn.net/uploads/201407/26/1406383248_3693.jpg",
  14. "http://img.my.csdn.net/uploads/201407/26/1406383243_5120.jpg",
  15. "http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg",
  16. "http://img.my.csdn.net/uploads/201407/26/1406383242_9576.jpg",
  17. "http://img.my.csdn.net/uploads/201407/26/1406383242_1721.jpg",
  18. "http://img.my.csdn.net/uploads/201407/26/1406383219_5806.jpg",
  19. "http://img.my.csdn.net/uploads/201407/26/1406383214_7794.jpg",
  20. "http://img.my.csdn.net/uploads/201407/26/1406383213_4418.jpg",
  21. "http://img.my.csdn.net/uploads/201407/26/1406383213_3557.jpg",
  22. "http://img.my.csdn.net/uploads/201407/26/1406383210_8779.jpg",
  23. "http://img.my.csdn.net/uploads/201407/26/1406383172_4577.jpg",
  24. "http://img.my.csdn.net/uploads/201407/26/1406383166_3407.jpg",
  25. "http://img.my.csdn.net/uploads/201407/26/1406383166_2224.jpg",
  26. "http://img.my.csdn.net/uploads/201407/26/1406383166_7301.jpg",
  27. "http://img.my.csdn.net/uploads/201407/26/1406383165_7197.jpg",
  28. "http://img.my.csdn.net/uploads/201407/26/1406383150_8410.jpg",
  29. "http://img.my.csdn.net/uploads/201407/26/1406383131_3736.jpg",
  30. "http://img.my.csdn.net/uploads/201407/26/1406383130_5094.jpg",
  31. "http://img.my.csdn.net/uploads/201407/26/1406383130_7393.jpg",
  32. "http://img.my.csdn.net/uploads/201407/26/1406383129_8813.jpg",
  33. "http://img.my.csdn.net/uploads/201407/26/1406383100_3554.jpg",
  34. "http://img.my.csdn.net/uploads/201407/26/1406383093_7894.jpg",
  35. "http://img.my.csdn.net/uploads/201407/26/1406383092_2432.jpg",
  36. "http://img.my.csdn.net/uploads/201407/26/1406383092_3071.jpg",
  37. "http://img.my.csdn.net/uploads/201407/26/1406383091_3119.jpg",
  38. "http://img.my.csdn.net/uploads/201407/26/1406383059_6589.jpg",
  39. "http://img.my.csdn.net/uploads/201407/26/1406383059_8814.jpg",
  40. "http://img.my.csdn.net/uploads/201407/26/1406383059_2237.jpg",
  41. "http://img.my.csdn.net/uploads/201407/26/1406383058_4330.jpg",
  42. "http://img.my.csdn.net/uploads/201407/26/1406383038_3602.jpg",
  43. "http://img.my.csdn.net/uploads/201407/26/1406382942_3079.jpg",
  44. "http://img.my.csdn.net/uploads/201407/26/1406382942_8125.jpg",
  45. "http://img.my.csdn.net/uploads/201407/26/1406382942_4881.jpg",
  46. "http://img.my.csdn.net/uploads/201407/26/1406382941_4559.jpg",
  47. "http://img.my.csdn.net/uploads/201407/26/1406382941_3845.jpg",
  48. "http://img.my.csdn.net/uploads/201407/26/1406382924_8955.jpg",
  49. "http://img.my.csdn.net/uploads/201407/26/1406382923_2141.jpg",
  50. "http://img.my.csdn.net/uploads/201407/26/1406382923_8437.jpg",
  51. "http://img.my.csdn.net/uploads/201407/26/1406382922_6166.jpg",
  52. "http://img.my.csdn.net/uploads/201407/26/1406382922_4843.jpg",
  53. "http://img.my.csdn.net/uploads/201407/26/1406382905_5804.jpg",
  54. "http://img.my.csdn.net/uploads/201407/26/1406382904_3362.jpg",
  55. "http://img.my.csdn.net/uploads/201407/26/1406382904_2312.jpg",
  56. "http://img.my.csdn.net/uploads/201407/26/1406382904_4960.jpg",
  57. "http://img.my.csdn.net/uploads/201407/26/1406382900_2418.jpg",
  58. "http://img.my.csdn.net/uploads/201407/26/1406382881_4490.jpg",
  59. "http://img.my.csdn.net/uploads/201407/26/1406382881_5935.jpg",
  60. "http://img.my.csdn.net/uploads/201407/26/1406382880_3865.jpg",
  61. "http://img.my.csdn.net/uploads/201407/26/1406382880_4662.jpg",
  62. "http://img.my.csdn.net/uploads/201407/26/1406382879_2553.jpg",
  63. "http://img.my.csdn.net/uploads/201407/26/1406382862_5375.jpg",
  64. "http://img.my.csdn.net/uploads/201407/26/1406382862_1748.jpg",
  65. "http://img.my.csdn.net/uploads/201407/26/1406382861_7618.jpg",
  66. "http://img.my.csdn.net/uploads/201407/26/1406382861_8606.jpg",
  67. "http://img.my.csdn.net/uploads/201407/26/1406382861_8949.jpg",
  68. "http://img.my.csdn.net/uploads/201407/26/1406382841_9821.jpg",
  69. "http://img.my.csdn.net/uploads/201407/26/1406382840_6603.jpg",
  70. "http://img.my.csdn.net/uploads/201407/26/1406382840_2405.jpg",
  71. "http://img.my.csdn.net/uploads/201407/26/1406382840_6354.jpg",
  72. "http://img.my.csdn.net/uploads/201407/26/1406382839_5779.jpg",
  73. "http://img.my.csdn.net/uploads/201407/26/1406382810_7578.jpg",
  74. "http://img.my.csdn.net/uploads/201407/26/1406382810_2436.jpg",
  75. "http://img.my.csdn.net/uploads/201407/26/1406382809_3883.jpg",
  76. "http://img.my.csdn.net/uploads/201407/26/1406382809_6269.jpg",
  77. "http://img.my.csdn.net/uploads/201407/26/1406382808_4179.jpg",
  78. "http://img.my.csdn.net/uploads/201407/26/1406382790_8326.jpg",
  79. "http://img.my.csdn.net/uploads/201407/26/1406382789_7174.jpg",
  80. "http://img.my.csdn.net/uploads/201407/26/1406382789_5170.jpg",
  81. "http://img.my.csdn.net/uploads/201407/26/1406382789_4118.jpg",
  82. "http://img.my.csdn.net/uploads/201407/26/1406382788_9532.jpg",
  83. "http://img.my.csdn.net/uploads/201407/26/1406382767_3184.jpg",
  84. "http://img.my.csdn.net/uploads/201407/26/1406382767_4772.jpg",
  85. "http://img.my.csdn.net/uploads/201407/26/1406382766_4924.jpg",
  86. "http://img.my.csdn.net/uploads/201407/26/1406382766_5762.jpg",
  87. "http://img.my.csdn.net/uploads/201407/26/1406382765_7341.jpg"
  88. };
  89. }

设置好了图片源之后,我们需要一个GridView来展示照片墙上的每一张图片。打开或修改activity_main.xml中的代码,如下所示:

[html] view plaincopy

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <GridView
  6. android:id="@+id/photo_wall"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:columnWidth="@dimen/image_thumbnail_size"
  10. android:gravity="center"
  11. android:horizontalSpacing="@dimen/image_thumbnail_spacing"
  12. android:numColumns="auto_fit"
  13. android:stretchMode="columnWidth"
  14. android:verticalSpacing="@dimen/image_thumbnail_spacing" >
  15. </GridView>
  16. </LinearLayout>

很简单,只是在LinearLayout中写了一个GridView而已。接着我们要定义GridView中每一个子View的布局,新建一个photo_layout.xml布局,加入如下代码:

[html] view plaincopy

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content" >
  5. <ImageView
  6. android:id="@+id/photo"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:layout_centerInParent="true"
  10. android:scaleType="fitXY"
  11. />
  12. </RelativeLayout>

仍然很简单,photo_layout.xml布局中只有一个ImageView控件,就是用它来显示图片的。这样我们就把所有的布局文件都写好了。

接下来新建PhotoWallAdapter做为GridView的适配器,代码如下所示:

[java] view plaincopy

  1. public class PhotoWallAdapter extends ArrayAdapter<String> {
  2. /**
  3. * 记录所有正在下载或等待下载的任务。
  4. */
  5. private Set<BitmapWorkerTask> taskCollection;
  6. /**
  7. * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
  8. */
  9. private LruCache<String, Bitmap> mMemoryCache;
  10. /**
  11. * 图片硬盘缓存核心类。
  12. */
  13. private DiskLruCache mDiskLruCache;
  14. /**
  15. * GridView的实例
  16. */
  17. private GridView mPhotoWall;
  18. /**
  19. * 记录每个子项的高度。
  20. */
  21. private int mItemHeight = 0;
  22. public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,
  23. GridView photoWall) {
  24. super(context, textViewResourceId, objects);
  25. mPhotoWall = photoWall;
  26. taskCollection = new HashSet<BitmapWorkerTask>();
  27. // 获取应用程序最大可用内存
  28. int maxMemory = (int) Runtime.getRuntime().maxMemory();
  29. int cacheSize = maxMemory / 8;
  30. // 设置图片缓存大小为程序最大可用内存的1/8
  31. mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
  32. @Override
  33. protected int sizeOf(String key, Bitmap bitmap) {
  34. return bitmap.getByteCount();
  35. }
  36. };
  37. try {
  38. // 获取图片缓存路径
  39. File cacheDir = getDiskCacheDir(context, "thumb");
  40. if (!cacheDir.exists()) {
  41. cacheDir.mkdirs();
  42. }
  43. // 创建DiskLruCache实例,初始化缓存数据
  44. mDiskLruCache = DiskLruCache
  45. .open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. @Override
  51. public View getView(int position, View convertView, ViewGroup parent) {
  52. final String url = getItem(position);
  53. View view;
  54. if (convertView == null) {
  55. view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);
  56. } else {
  57. view = convertView;
  58. }
  59. final ImageView imageView = (ImageView) view.findViewById(R.id.photo);
  60. if (imageView.getLayoutParams().height != mItemHeight) {
  61. imageView.getLayoutParams().height = mItemHeight;
  62. }
  63. // 给ImageView设置一个Tag,保证异步加载图片时不会乱序
  64. imageView.setTag(url);
  65. imageView.setImageResource(R.drawable.empty_photo);
  66. loadBitmaps(imageView, url);
  67. return view;
  68. }
  69. /**
  70. * 将一张图片存储到LruCache中。
  71. *
  72. * @param key
  73. *            LruCache的键,这里传入图片的URL地址。
  74. * @param bitmap
  75. *            LruCache的键,这里传入从网络上下载的Bitmap对象。
  76. */
  77. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  78. if (getBitmapFromMemoryCache(key) == null) {
  79. mMemoryCache.put(key, bitmap);
  80. }
  81. }
  82. /**
  83. * 从LruCache中获取一张图片,如果不存在就返回null。
  84. *
  85. * @param key
  86. *            LruCache的键,这里传入图片的URL地址。
  87. * @return 对应传入键的Bitmap对象,或者null。
  88. */
  89. public Bitmap getBitmapFromMemoryCache(String key) {
  90. return mMemoryCache.get(key);
  91. }
  92. /**
  93. * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
  94. * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
  95. */
  96. public void loadBitmaps(ImageView imageView, String imageUrl) {
  97. try {
  98. Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
  99. if (bitmap == null) {
  100. BitmapWorkerTask task = new BitmapWorkerTask();
  101. taskCollection.add(task);
  102. task.execute(imageUrl);
  103. } else {
  104. if (imageView != null && bitmap != null) {
  105. imageView.setImageBitmap(bitmap);
  106. }
  107. }
  108. } catch (Exception e) {
  109. e.printStackTrace();
  110. }
  111. }
  112. /**
  113. * 取消所有正在下载或等待下载的任务。
  114. */
  115. public void cancelAllTasks() {
  116. if (taskCollection != null) {
  117. for (BitmapWorkerTask task : taskCollection) {
  118. task.cancel(false);
  119. }
  120. }
  121. }
  122. /**
  123. * 根据传入的uniqueName获取硬盘缓存的路径地址。
  124. */
  125. public File getDiskCacheDir(Context context, String uniqueName) {
  126. String cachePath;
  127. if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
  128. || !Environment.isExternalStorageRemovable()) {
  129. cachePath = context.getExternalCacheDir().getPath();
  130. } else {
  131. cachePath = context.getCacheDir().getPath();
  132. }
  133. return new File(cachePath + File.separator + uniqueName);
  134. }
  135. /**
  136. * 获取当前应用程序的版本号。
  137. */
  138. public int getAppVersion(Context context) {
  139. try {
  140. PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(),
  141. 0);
  142. return info.versionCode;
  143. } catch (NameNotFoundException e) {
  144. e.printStackTrace();
  145. }
  146. return 1;
  147. }
  148. /**
  149. * 设置item子项的高度。
  150. */
  151. public void setItemHeight(int height) {
  152. if (height == mItemHeight) {
  153. return;
  154. }
  155. mItemHeight = height;
  156. notifyDataSetChanged();
  157. }
  158. /**
  159. * 使用MD5算法对传入的key进行加密并返回。
  160. */
  161. public String hashKeyForDisk(String key) {
  162. String cacheKey;
  163. try {
  164. final MessageDigest mDigest = MessageDigest.getInstance("MD5");
  165. mDigest.update(key.getBytes());
  166. cacheKey = bytesToHexString(mDigest.digest());
  167. } catch (NoSuchAlgorithmException e) {
  168. cacheKey = String.valueOf(key.hashCode());
  169. }
  170. return cacheKey;
  171. }
  172. /**
  173. * 将缓存记录同步到journal文件中。
  174. */
  175. public void fluchCache() {
  176. if (mDiskLruCache != null) {
  177. try {
  178. mDiskLruCache.flush();
  179. } catch (IOException e) {
  180. e.printStackTrace();
  181. }
  182. }
  183. }
  184. private String bytesToHexString(byte[] bytes) {
  185. StringBuilder sb = new StringBuilder();
  186. for (int i = 0; i < bytes.length; i++) {
  187. String hex = Integer.toHexString(0xFF & bytes[i]);
  188. if (hex.length() == 1) {
  189. sb.append(‘0‘);
  190. }
  191. sb.append(hex);
  192. }
  193. return sb.toString();
  194. }
  195. /**
  196. * 异步下载图片的任务。
  197. *
  198. * @author guolin
  199. */
  200. class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
  201. /**
  202. * 图片的URL地址
  203. */
  204. private String imageUrl;
  205. @Override
  206. protected Bitmap doInBackground(String... params) {
  207. imageUrl = params[0];
  208. FileDescriptor fileDescriptor = null;
  209. FileInputStream fileInputStream = null;
  210. Snapshot snapShot = null;
  211. try {
  212. // 生成图片URL对应的key
  213. final String key = hashKeyForDisk(imageUrl);
  214. // 查找key对应的缓存
  215. snapShot = mDiskLruCache.get(key);
  216. if (snapShot == null) {
  217. // 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
  218. DiskLruCache.Editor editor = mDiskLruCache.edit(key);
  219. if (editor != null) {
  220. OutputStream outputStream = editor.newOutputStream(0);
  221. if (downloadUrlToStream(imageUrl, outputStream)) {
  222. editor.commit();
  223. } else {
  224. editor.abort();
  225. }
  226. }
  227. // 缓存被写入后,再次查找key对应的缓存
  228. snapShot = mDiskLruCache.get(key);
  229. }
  230. if (snapShot != null) {
  231. fileInputStream = (FileInputStream) snapShot.getInputStream(0);
  232. fileDescriptor = fileInputStream.getFD();
  233. }
  234. // 将缓存数据解析成Bitmap对象
  235. Bitmap bitmap = null;
  236. if (fileDescriptor != null) {
  237. bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
  238. }
  239. if (bitmap != null) {
  240. // 将Bitmap对象添加到内存缓存当中
  241. addBitmapToMemoryCache(params[0], bitmap);
  242. }
  243. return bitmap;
  244. } catch (IOException e) {
  245. e.printStackTrace();
  246. } finally {
  247. if (fileDescriptor == null && fileInputStream != null) {
  248. try {
  249. fileInputStream.close();
  250. } catch (IOException e) {
  251. }
  252. }
  253. }
  254. return null;
  255. }
  256. @Override
  257. protected void onPostExecute(Bitmap bitmap) {
  258. super.onPostExecute(bitmap);
  259. // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
  260. ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
  261. if (imageView != null && bitmap != null) {
  262. imageView.setImageBitmap(bitmap);
  263. }
  264. taskCollection.remove(this);
  265. }
  266. /**
  267. * 建立HTTP请求,并获取Bitmap对象。
  268. *
  269. * @param imageUrl
  270. *            图片的URL地址
  271. * @return 解析后的Bitmap对象
  272. */
  273. private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
  274. HttpURLConnection urlConnection = null;
  275. BufferedOutputStream out = null;
  276. BufferedInputStream in = null;
  277. try {
  278. final URL url = new URL(urlString);
  279. urlConnection = (HttpURLConnection) url.openConnection();
  280. in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
  281. out = new BufferedOutputStream(outputStream, 8 * 1024);
  282. int b;
  283. while ((b = in.read()) != -1) {
  284. out.write(b);
  285. }
  286. return true;
  287. } catch (final IOException e) {
  288. e.printStackTrace();
  289. } finally {
  290. if (urlConnection != null) {
  291. urlConnection.disconnect();
  292. }
  293. try {
  294. if (out != null) {
  295. out.close();
  296. }
  297. if (in != null) {
  298. in.close();
  299. }
  300. } catch (final IOException e) {
  301. e.printStackTrace();
  302. }
  303. }
  304. return false;
  305. }
  306. }
  307. }

代码有点长,我们一点点进行分析。首先在PhotoWallAdapter的构造函数中,我们初始化了LruCache类,并设置了内存缓存容量为程序最大可用内存的1/8,紧接着调用了DiskLruCache的open()方法来创建实例,并设置了硬盘缓存容量为10M,这样我们就把LruCache和DiskLruCache的初始化工作完成了。

接着在getView()方法中,我们为每个ImageView设置了一个唯一的Tag,这个Tag的作用是为了后面能够准确地找回这个ImageView,不然异步加载图片会出现乱序的情况。然后在getView()方法的最后调用了loadBitmaps()方法,加载图片的具体逻辑也就是在这里执行的了。

进入到loadBitmaps()方法中可以看到,实现是调用了getBitmapFromMemoryCache()方法来从内存中获取缓存,如果获取到了则直接调用ImageView的setImageBitmap()方法将图片显示到界面上。如果内存中没有获取到,则开启一个BitmapWorkerTask任务来去异步加载图片。

那么在BitmapWorkerTask的doInBackground()方法中,我们就灵活运用了上篇文章中学习的DiskLruCache的各种用法。首先根据图片的URL生成对应的MD5 key,然后调用DiskLruCache的get()方法来获取硬盘缓存,如果没有获取到的话则从网络上请求图片并写入硬盘缓存,接着将Bitmap对象解析出来并添加到内存缓存当中,最后将这个Bitmap对象显示到界面上,这样一个完整的流程就执行完了。

那么我们再来分析一下上述流程,每次加载图片的时候都优先去内存缓存当中读取,当读取不到的时候则回去硬盘缓存中读取,而如果硬盘缓存仍然读取不到的话,就从网络上请求原始数据。不管是从硬盘缓存还是从网络获取,读取到了数据之后都应该添加到内存缓存当中,这样的话我们下次再去读取图片的时候就能迅速从内存当中读取到,而如果该图片从内存中被移除了的话,那就重复再执行一遍上述流程就可以了。

这样我们就把LruCache和DiskLruCache完美结合到一起了。接下来还需要编写MainActivity的代码,非常简单,如下所示:

[java] view plaincopy

  1. public class MainActivity extends Activity {
  2. /**
  3. * 用于展示照片墙的GridView
  4. */
  5. private GridView mPhotoWall;
  6. /**
  7. * GridView的适配器
  8. */
  9. private PhotoWallAdapter mAdapter;
  10. private int mImageThumbSize;
  11. private int mImageThumbSpacing;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. mImageThumbSize = getResources().getDimensionPixelSize(
  17. R.dimen.image_thumbnail_size);
  18. mImageThumbSpacing = getResources().getDimensionPixelSize(
  19. R.dimen.image_thumbnail_spacing);
  20. mPhotoWall = (GridView) findViewById(R.id.photo_wall);
  21. mAdapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls,
  22. mPhotoWall);
  23. mPhotoWall.setAdapter(mAdapter);
  24. mPhotoWall.getViewTreeObserver().addOnGlobalLayoutListener(
  25. new ViewTreeObserver.OnGlobalLayoutListener() {
  26. @Override
  27. public void onGlobalLayout() {
  28. final int numColumns = (int) Math.floor(mPhotoWall
  29. .getWidth()
  30. / (mImageThumbSize + mImageThumbSpacing));
  31. if (numColumns > 0) {
  32. int columnWidth = (mPhotoWall.getWidth() / numColumns)
  33. - mImageThumbSpacing;
  34. mAdapter.setItemHeight(columnWidth);
  35. mPhotoWall.getViewTreeObserver()
  36. .removeGlobalOnLayoutListener(this);
  37. }
  38. }
  39. });
  40. }
  41. @Override
  42. protected void onPause() {
  43. super.onPause();
  44. mAdapter.fluchCache();
  45. }
  46. @Override
  47. protected void onDestroy() {
  48. super.onDestroy();
  49. // 退出程序时结束所有的下载任务
  50. mAdapter.cancelAllTasks();
  51. }
  52. }

上述代码中,我们通过getViewTreeObserver()的方式监听View的布局事件,当布局完成以后,我们重新修改一下GridView中子View的高度,以保证子View的宽度和高度可以保持一致。

到这里还没有结束,最后还需要配置一下AndroidManifest.xml文件,并加入相应的权限,如下所示:

[html] view plaincopy

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.example.photoswalldemo"
  3. android:versionCode="1"
  4. android:versionName="1.0" >
  5. <uses-sdk
  6. android:minSdkVersion="14"
  7. android:targetSdkVersion="17" />
  8. <uses-permission android:name="android.permission.INTERNET" />
  9. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  10. <application
  11. android:allowBackup="true"
  12. android:icon="@drawable/ic_launcher"
  13. android:label="@string/app_name"
  14. android:theme="@style/AppTheme" >
  15. <activity
  16. android:name="com.example.photoswalldemo.MainActivity"
  17. android:label="@string/app_name" >
  18. <intent-filter>
  19. <action android:name="android.intent.action.MAIN" />
  20. <category android:name="android.intent.category.LAUNCHER" />
  21. </intent-filter>
  22. </activity>
  23. </application>
  24. </manifest>

好了,全部代码都在这儿了,让我们来运行一下吧,效果如下图所示:

第一次从网络上请求图片的时候有点慢,但之后加载图片就会非常快了,滑动起来也很流畅。

那么我们最后再检查一下这些图片是不是已经正确缓存在指定地址了,进入 /sdcard/Android/data/<application package>/cache/thumb 这个路径,如下图所示:

可以看到,每张图片的缓存以及journal文件都在这里了,说明我们的硬盘缓存已经成功了。

好了,今天的讲解就到这里,有疑问的朋友可以在下面留言。

源码下载,请点击这里

时间: 2024-10-05 04:55:17

Android照片墙完整版,完美结合LruCache和DiskLruCache的相关文章

Android照片墙完整版,的完美结合LruCache和DiskLruCache

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/34093441 在上一篇文章其中,我们学习了DiskLruCache的概念和基本使用方法.但仅仅是掌握理论知识显然是不够的,那么本篇文章我们就来继续进阶一下.看一看在实战其中应该如何合理使用DiskLruCache. 还不熟悉DiskLruCache使用方法的朋友能够先去參考我的上一篇文章 Android DiskLruCache全然解析,硬盘缓存的最佳方案 . 事实上,在真正的项

弱智儿童欢乐多游戏android源码完整版

1.弱智儿童欢乐多游戏源码完整版2  项目概要:弱智儿童欢乐多,这就是一款弱智的游戏……回归正题,这是一款仿IOS上TripTrap的关卡解锁模式的益智休闲游戏,代码中实际只有一关游戏,但是可以体验解锁,第一关和第二关都是一样的游戏场景.3  项目问题:虽然上传了文档,但不是参考,而是因为存在很多问题,欢迎大家指导:代码也很渣,欢迎吐槽:没有做适屏,所以打包的APK如果安装在大家的机子里maybe会混乱,我的机子是魅族MX3:微博分享功能是直接用的网上下载的例子,自己原理也没有弄清楚,所以这块有

Android掌中游斗地主游戏源码完整版

源码大放送-掌中游斗地主(完整版),集合了单机斗地主.网络斗地主.癞子斗地主等,有史以来最有参考价值的源码,虽然运行慢了一点但是功能正常,用的是纯java写的. 项目详细说明:http://android.662p.com <ignore_js_op> [/td][td] <ignore_js_op> [/td][td] <ignore_js_op> [/td][td] <ignore_js_op> [/td][td] <ignore_js_op>

【电子书下载】《Android应用程序开发与典型案例》完整版!!

图书简介: <android应用程序开发与典型案例>共23章,内容包含两大部分.第一部分是android程序设计基础,在介绍android环境搭建以及android系统基本控件和组件后,详细介绍了android系统应用编程中典型的技术,比如,android中的图形图像.多媒体编程.gps定位与地图编程等:第二部分是android程序ui设计,从手机软件的交互设计谈起,介绍了android用户界面设计原则和方法.android用户界面设计哲学等,并给出了具体建议. <android应用程序

转 Android Activity之间动画完整版详解

标签:Android Activity动画详解 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://mzh3344258.blog.51cto.com/1823534/807337 大家可能遇到过跟小马一样的问题,就是在项目中不给Activity添加动画感觉好烂,特此,小马找个时间学习下Activity跳转时添加动画,网上资料一堆,但有些讲的是错的,而且只有文字没有效果,不如自己来,顺带试下什么效果,这个工程里面小马收集了一些常用

完整版linux下android源码下载、编译、模拟器启动运行

一.环境说明: 1.VMware版本:8.0.3 build-703057 2.liunx系统:Ubuntu10.10 3.jdk:sun-java6-jdk 二.Ubuntu 10.10更新源列表 由于??Ubuntu 10.10版本的源已经过期了,所以,需要对其进行更新,Ubuntu 10.10 已经发布了,下面提供更新源方法与更新源列表地址: 1.sudo gedit /etc/apt/sources.list     编辑你的源列表,将原来的内容全部删除,添加下面的源,复制到你的列表中,

Android 获取运营商信息(完整版)-解决高通,MTK等双卡问题

由于国内的运营商问题,双卡手机获取IMSI号问题要根据厂商API 来实现. 下面我们就来做一套完整的分析运营商获取IMSI号逻辑. 1,首先我们要判断手机的平台. 1.1,判断手机是否MTK平台 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public static M

office2016 软件全集 官方下载免费完整版(含破解文件)不含垃圾软件 win10完美激活

office2016官方下载免费完整版是新一代办公软件,office2016官方下载免费完整版已经分享到下面,office2016官方下载免费完整版包括了Word.Excel.PowerPoint.OneNote.Outlook.Skype.Project.Visio以及Publisher等组件和服务.下面分享:office2016软件的下载.安装及激活. 本文来自互联网,按原教程安装结果中招了,因含有垃圾软件,所以决定改写,以免你懂的... 注册文件下载中含有垃圾软件,请按下面的步骤操作会跳过

微信表情的字符编号完整版【图文并茂哦 】

[图文并茂哦!] Emoji表情的分类(微信表情的字符编号完整版) NLP神经网络实现在伪原创方面的运用NLP伪原创技术早期并不是很受欢迎基于主动学习的伪原创句法识别研究小发猫-人工智能的伪原创工具小发猫与普通伪原创工具的区别自媒体如何快速伪原创(附:小发猫伪原创)NLP伪原创技术要知道的三个概念自媒体如何快速伪原创自媒体伪原创工具 小发猫基于NLP的自媒体原创爆文洗稿神器 Emoji表情有很多种版本,包括Unified.FreeEIM.DoCoMo.KDDI.Softbank和Google,而