一.LruCache的简单介绍
- Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。
- * 当cache已满的时候加入新的item时,在队列尾部的item会被回收。
- * 如果你cache的某个值需要明确释放,重写entryRemoved()
- * 如果key相对应的item丢掉啦,重写create().这简化了调用代码,即使丢失了也总会返回。
- * is limited to 4MiB of bitmaps: 默认cache大小是测量的item的数量,重写sizeof计算不同item的
- * 大小。
二.使用Lrucache来异步加载图片,防止阻塞(缓存图片)
下面是我的一个小程序,使用得到了Lrucache,总共有三个类,代码注释也是很详细的,很适合初学者学习。主要的功能就是形成一个像瀑布的图片墙。
在第三个类中有这个项目的设计思想。大家可以看看,共同进步,谢谢!!!
第一个类
1 package com.pangzaifei.falls; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 6 /** 7 * 瀑布流 8 * 9 */ 10 public class Demo extends Activity { 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.activity_demo); 15 16 } 17 }
第二个类
1 package com.pangzaifei.falls; 2 3 import android.graphics.Bitmap; 4 import android.graphics.BitmapFactory; 5 import android.graphics.BitmapFactory.Options; 6 import android.support.v4.util.LruCache; 7 8 /** 9 * 图片资源 数据源类 10 * 11 */ 12 public class Images { 13 private static Images images; 14 // Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。 15 // 当cache已满的时候加入新的item时,在队列尾部的item会被回收。 16 // 如果你cache的某个值需要明确释放,重写entryRemoved() 17 // 如果key相对应的item丢掉啦,重写create().这简化了调用代码,即使丢失了也总会返回。 18 // 默认cache大小是测量的item的数量,重写sizeof计算不同item的大小。 19 private LruCache<String, Bitmap> mMemoryCache; 20 21 public Images() { 22 initLrucache();// 初始化lrucache 23 } 24 25 public static Images getInstance() { 26 if (images == null) { 27 images = new Images(); 28 } 29 return images; 30 } 31 32 /** 33 * 初始化lrucache 34 */ 35 private void initLrucache() { 36 // 获取应用程序最大内存 37 long maxSize = Runtime.getRuntime().maxMemory(); 38 // 设置图片缓存大小为程序最大可用内存的1/8. 39 int cacheSize = (int) (maxSize / 8); 40 mMemoryCache = new LruCache<String, Bitmap>(cacheSize); 41 } 42 43 /** 44 * 从LruCache中获取一张图片,如果不存在就返回null 从lrucache中获得key key LruCache的键,传入图片的url地址 45 * return 对应传入的bitmap对象,或者null 46 */ 47 public Bitmap getMemoryCache(String key) { 48 if (mMemoryCache != null) { 49 Bitmap bitmap = mMemoryCache.get(key); 50 if (bitmap != null) { 51 return bitmap; 52 } 53 } 54 return null; 55 } 56 57 /** 58 * 将图片添加到lrucache中 59 * 60 * @param key 61 * LruCache的键,这里传入图片的URL地址 62 * @param bitmap 63 * 这里指的是从网络上下载的bitmap对象。 64 */ 65 public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 66 if (getMemoryCache(key) == null) { 67 mMemoryCache.put(key, bitmap); 68 } 69 } 70 71 public static final String[] imageThumbs = new String[] { 72 "//img-my.csdn.net/uploads/201309/01/1378037235_3453.jpg", 73 "//img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg", 74 "//img-my.csdn.net/uploads/201309/01/1378037235_9280.jpg", 75 "//img-my.csdn.net/uploads/201309/01/1378037234_3539.jpg", 76 "//img-my.csdn.net/uploads/201309/01/1378037234_6318.jpg", 77 "//img-my.csdn.net/uploads/201309/01/1378037194_2965.jpg", 78 "//img-my.csdn.net/uploads/201309/01/1378037193_1687.jpg", 79 "//img-my.csdn.net/uploads/201309/01/1378037193_1286.jpg", 80 "//img-my.csdn.net/uploads/201309/01/1378037192_8379.jpg", 81 "//img-my.csdn.net/uploads/201309/01/1378037178_9374.jpg", 82 "//img-my.csdn.net/uploads/201309/01/1378037177_1254.jpg", 83 "//img-my.csdn.net/uploads/201309/01/1378037177_6203.jpg", 84 "//img-my.csdn.net/uploads/201309/01/1378037152_6352.jpg", 85 "//img-my.csdn.net/uploads/201309/01/1378037151_9565.jpg", 86 "//img-my.csdn.net/uploads/201309/01/1378037151_7904.jpg", 87 "//img-my.csdn.net/uploads/201309/01/1378037148_7104.jpg", 88 "//img-my.csdn.net/uploads/201309/01/1378037129_8825.jpg", 89 "//img-my.csdn.net/uploads/201309/01/1378037128_5291.jpg", 90 "//img-my.csdn.net/uploads/201309/01/1378037128_3531.jpg", 91 "//img-my.csdn.net/uploads/201309/01/1378037127_1085.jpg", 92 "//img-my.csdn.net/uploads/201309/01/1378037095_7515.jpg", 93 "//img-my.csdn.net/uploads/201309/01/1378037094_8001.jpg", 94 "//img-my.csdn.net/uploads/201309/01/1378037093_7168.jpg", 95 "//img-my.csdn.net/uploads/201309/01/1378037091_4950.jpg", 96 "//img-my.csdn.net/uploads/201308/31/1377949643_6410.jpg", 97 "//img-my.csdn.net/uploads/201308/31/1377949642_6939.jpg", 98 "//img-my.csdn.net/uploads/201308/31/1377949630_4505.jpg", 99 "//img-my.csdn.net/uploads/201308/31/1377949630_4593.jpg", 100 "//img-my.csdn.net/uploads/201308/31/1377949629_7309.jpg", 101 "//img-my.csdn.net/uploads/201308/31/1377949629_8247.jpg", 102 "//img-my.csdn.net/uploads/201308/31/1377949615_1986.jpg", 103 "//img-my.csdn.net/uploads/201308/31/1377949614_8482.jpg", 104 "//img-my.csdn.net/uploads/201308/31/1377949614_3743.jpg", 105 "//img-my.csdn.net/uploads/201308/31/1377949614_4199.jpg", 106 "//img-my.csdn.net/uploads/201308/31/1377949599_3416.jpg", 107 "//img-my.csdn.net/uploads/201308/31/1377949599_5269.jpg", 108 "//img-my.csdn.net/uploads/201308/31/1377949598_7858.jpg", 109 "//img-my.csdn.net/uploads/201308/31/1377949598_9982.jpg", 110 "//img-my.csdn.net/uploads/201308/31/1377949578_2770.jpg", 111 "//img-my.csdn.net/uploads/201308/31/1377949578_8744.jpg", 112 "//img-my.csdn.net/uploads/201308/31/1377949577_5210.jpg", 113 "//img-my.csdn.net/uploads/201308/31/1377949577_1998.jpg", 114 "//img-my.csdn.net/uploads/201308/31/1377949482_8813.jpg", 115 "//img-my.csdn.net/uploads/201308/31/1377949481_6577.jpg", 116 "//img-my.csdn.net/uploads/201308/31/1377949480_4490.jpg", 117 "//img-my.csdn.net/uploads/201308/31/1377949455_6792.jpg", 118 "//img-my.csdn.net/uploads/201308/31/1377949455_6345.jpg", 119 "//img-my.csdn.net/uploads/201308/31/1377949442_4553.jpg", 120 "//img-my.csdn.net/uploads/201308/31/1377949441_8987.jpg", 121 "//img-my.csdn.net/uploads/201308/31/1377949441_5454.jpg", 122 "//img-my.csdn.net/uploads/201308/31/1377949454_6367.jpg", 123 "//img-my.csdn.net/uploads/201308/31/1377949442_4562.jpg", 124 "//img-my.csdn.net/uploads/201309/01/1378037235_3453.jpg", 125 "//img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg", 126 "//img-my.csdn.net/uploads/201309/01/1378037235_9280.jpg", 127 "//img-my.csdn.net/uploads/201309/01/1378037234_3539.jpg", 128 "//img-my.csdn.net/uploads/201309/01/1378037234_6318.jpg", 129 "//img-my.csdn.net/uploads/201309/01/1378037194_2965.jpg", 130 "//img-my.csdn.net/uploads/201309/01/1378037193_1687.jpg", 131 "//img-my.csdn.net/uploads/201309/01/1378037193_1286.jpg", 132 "//img-my.csdn.net/uploads/201309/01/1378037192_8379.jpg", 133 "//img-my.csdn.net/uploads/201309/01/1378037178_9374.jpg", 134 "//img-my.csdn.net/uploads/201309/01/1378037177_1254.jpg", 135 "//img-my.csdn.net/uploads/201309/01/1378037177_6203.jpg", 136 "//img-my.csdn.net/uploads/201309/01/1378037152_6352.jpg", 137 "//img-my.csdn.net/uploads/201309/01/1378037151_9565.jpg", 138 "//img-my.csdn.net/uploads/201309/01/1378037151_7904.jpg", 139 "//img-my.csdn.net/uploads/201309/01/1378037148_7104.jpg", 140 "//img-my.csdn.net/uploads/201309/01/1378037129_8825.jpg", 141 "//img-my.csdn.net/uploads/201309/01/1378037128_5291.jpg", 142 "//img-my.csdn.net/uploads/201309/01/1378037128_3531.jpg", 143 "//img-my.csdn.net/uploads/201309/01/1378037127_1085.jpg", 144 "//img-my.csdn.net/uploads/201309/01/1378037095_7515.jpg", 145 "//img-my.csdn.net/uploads/201309/01/1378037094_8001.jpg", 146 "//img-my.csdn.net/uploads/201309/01/1378037093_7168.jpg", 147 "//img-my.csdn.net/uploads/201309/01/1378037091_4950.jpg", 148 "//img-my.csdn.net/uploads/201308/31/1377949643_6410.jpg", 149 "//img-my.csdn.net/uploads/201308/31/1377949642_6939.jpg", 150 "//img-my.csdn.net/uploads/201308/31/1377949630_4505.jpg", 151 "//img-my.csdn.net/uploads/201308/31/1377949630_4593.jpg", 152 "//img-my.csdn.net/uploads/201308/31/1377949629_7309.jpg", 153 "//img-my.csdn.net/uploads/201308/31/1377949629_8247.jpg", 154 "//img-my.csdn.net/uploads/201308/31/1377949615_1986.jpg", 155 "//img-my.csdn.net/uploads/201308/31/1377949614_8482.jpg", 156 "//img-my.csdn.net/uploads/201308/31/1377949614_3743.jpg", 157 "//img-my.csdn.net/uploads/201308/31/1377949614_4199.jpg", 158 "//img-my.csdn.net/uploads/201308/31/1377949599_3416.jpg", 159 "//img-my.csdn.net/uploads/201308/31/1377949599_5269.jpg", 160 "//img-my.csdn.net/uploads/201308/31/1377949598_7858.jpg", 161 "//img-my.csdn.net/uploads/201308/31/1377949598_9982.jpg", 162 "//img-my.csdn.net/uploads/201308/31/1377949578_2770.jpg", 163 "//img-my.csdn.net/uploads/201308/31/1377949578_8744.jpg", 164 "//img-my.csdn.net/uploads/201308/31/1377949577_5210.jpg", 165 "//img-my.csdn.net/uploads/201308/31/1377949577_1998.jpg", 166 "//img-my.csdn.net/uploads/201308/31/1377949482_8813.jpg", 167 "//img-my.csdn.net/uploads/201308/31/1377949481_6577.jpg", 168 "//img-my.csdn.net/uploads/201308/31/1377949480_4490.jpg", 169 "//img-my.csdn.net/uploads/201308/31/1377949455_6792.jpg", 170 "//img-my.csdn.net/uploads/201308/31/1377949455_6345.jpg", 171 "//img-my.csdn.net/uploads/201308/31/1377949442_4553.jpg", 172 "//img-my.csdn.net/uploads/201308/31/1377949441_8987.jpg", 173 "//img-my.csdn.net/uploads/201308/31/1377949441_5454.jpg", 174 "//img-my.csdn.net/uploads/201308/31/1377949454_6367.jpg", 175 "//img-my.csdn.net/uploads/201309/01/1378037235_3453.jpg", 176 "//img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg", 177 "//img-my.csdn.net/uploads/201309/01/1378037235_9280.jpg", 178 "//img-my.csdn.net/uploads/201309/01/1378037234_3539.jpg", 179 "//img-my.csdn.net/uploads/201309/01/1378037234_6318.jpg", 180 "//img-my.csdn.net/uploads/201309/01/1378037194_2965.jpg", 181 "//img-my.csdn.net/uploads/201309/01/1378037193_1687.jpg", 182 "//img-my.csdn.net/uploads/201309/01/1378037193_1286.jpg", 183 "//img-my.csdn.net/uploads/201309/01/1378037192_8379.jpg", 184 "//img-my.csdn.net/uploads/201309/01/1378037178_9374.jpg", 185 "//img-my.csdn.net/uploads/201309/01/1378037177_1254.jpg", 186 "//img-my.csdn.net/uploads/201309/01/1378037177_6203.jpg", 187 "//img-my.csdn.net/uploads/201309/01/1378037152_6352.jpg", 188 "//img-my.csdn.net/uploads/201309/01/1378037151_9565.jpg", 189 "//img-my.csdn.net/uploads/201309/01/1378037151_7904.jpg", 190 "//img-my.csdn.net/uploads/201309/01/1378037148_7104.jpg", 191 "//img-my.csdn.net/uploads/201309/01/1378037129_8825.jpg", 192 "//img-my.csdn.net/uploads/201309/01/1378037128_5291.jpg", 193 "//img-my.csdn.net/uploads/201309/01/1378037128_3531.jpg", 194 "//img-my.csdn.net/uploads/201309/01/1378037127_1085.jpg", 195 "//img-my.csdn.net/uploads/201309/01/1378037095_7515.jpg", 196 "//img-my.csdn.net/uploads/201309/01/1378037094_8001.jpg", 197 "//img-my.csdn.net/uploads/201309/01/1378037093_7168.jpg", 198 "//img-my.csdn.net/uploads/201309/01/1378037091_4950.jpg", 199 "//img-my.csdn.net/uploads/201308/31/1377949643_6410.jpg", 200 "//img-my.csdn.net/uploads/201308/31/1377949642_6939.jpg", 201 "//img-my.csdn.net/uploads/201308/31/1377949630_4505.jpg", 202 "//img-my.csdn.net/uploads/201308/31/1377949630_4593.jpg", 203 "//img-my.csdn.net/uploads/201308/31/1377949629_7309.jpg", 204 "//img-my.csdn.net/uploads/201308/31/1377949629_8247.jpg", 205 "//img-my.csdn.net/uploads/201308/31/1377949615_1986.jpg", 206 "//img-my.csdn.net/uploads/201308/31/1377949614_8482.jpg", 207 "//img-my.csdn.net/uploads/201308/31/1377949614_3743.jpg", 208 "//img-my.csdn.net/uploads/201308/31/1377949614_4199.jpg", 209 "//img-my.csdn.net/uploads/201308/31/1377949599_3416.jpg", 210 "//img-my.csdn.net/uploads/201308/31/1377949599_5269.jpg", 211 "//img-my.csdn.net/uploads/201308/31/1377949598_7858.jpg", 212 "//img-my.csdn.net/uploads/201308/31/1377949598_9982.jpg", 213 "//img-my.csdn.net/uploads/201308/31/1377949578_2770.jpg", 214 "//img-my.csdn.net/uploads/201308/31/1377949578_8744.jpg", 215 "//img-my.csdn.net/uploads/201308/31/1377949577_5210.jpg", 216 "//img-my.csdn.net/uploads/201308/31/1377949577_1998.jpg", 217 "//img-my.csdn.net/uploads/201308/31/1377949482_8813.jpg", 218 "//img-my.csdn.net/uploads/201308/31/1377949481_6577.jpg", 219 "//img-my.csdn.net/uploads/201308/31/1377949480_4490.jpg", 220 "//img-my.csdn.net/uploads/201308/31/1377949455_6792.jpg", 221 "//img-my.csdn.net/uploads/201308/31/1377949455_6345.jpg", 222 "//img-my.csdn.net/uploads/201308/31/1377949442_4553.jpg", 223 "//img-my.csdn.net/uploads/201308/31/1377949441_8987.jpg", 224 "//img-my.csdn.net/uploads/201308/31/1377949441_5454.jpg", 225 "//img-my.csdn.net/uploads/201308/31/1377949454_6367.jpg", 226 "//img-my.csdn.net/uploads/201309/01/1378037235_3453.jpg", 227 "//img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg", 228 "//img-my.csdn.net/uploads/201309/01/1378037235_9280.jpg", 229 "//img-my.csdn.net/uploads/201309/01/1378037234_3539.jpg", 230 "//img-my.csdn.net/uploads/201309/01/1378037234_6318.jpg", 231 "//img-my.csdn.net/uploads/201309/01/1378037194_2965.jpg", 232 "//img-my.csdn.net/uploads/201309/01/1378037193_1687.jpg", 233 "//img-my.csdn.net/uploads/201309/01/1378037193_1286.jpg", 234 "//img-my.csdn.net/uploads/201309/01/1378037192_8379.jpg", 235 "//img-my.csdn.net/uploads/201309/01/1378037178_9374.jpg", 236 "//img-my.csdn.net/uploads/201309/01/1378037177_1254.jpg", 237 "//img-my.csdn.net/uploads/201309/01/1378037177_6203.jpg", 238 "//img-my.csdn.net/uploads/201309/01/1378037152_6352.jpg", 239 "//img-my.csdn.net/uploads/201309/01/1378037151_9565.jpg", 240 "//img-my.csdn.net/uploads/201309/01/1378037151_7904.jpg", 241 "//img-my.csdn.net/uploads/201309/01/1378037148_7104.jpg", 242 "//img-my.csdn.net/uploads/201309/01/1378037129_8825.jpg", 243 "//img-my.csdn.net/uploads/201309/01/1378037128_5291.jpg", 244 "//img-my.csdn.net/uploads/201309/01/1378037128_3531.jpg", 245 "//img-my.csdn.net/uploads/201309/01/1378037127_1085.jpg", 246 "//img-my.csdn.net/uploads/201309/01/1378037095_7515.jpg", 247 "//img-my.csdn.net/uploads/201309/01/1378037094_8001.jpg", 248 "//img-my.csdn.net/uploads/201309/01/1378037093_7168.jpg", 249 "//img-my.csdn.net/uploads/201309/01/1378037091_4950.jpg", 250 "//img-my.csdn.net/uploads/201308/31/1377949643_6410.jpg", 251 "//img-my.csdn.net/uploads/201308/31/1377949642_6939.jpg", 252 "//img-my.csdn.net/uploads/201308/31/1377949630_4505.jpg", 253 "//img-my.csdn.net/uploads/201308/31/1377949630_4593.jpg", 254 "//img-my.csdn.net/uploads/201308/31/1377949629_7309.jpg", 255 "//img-my.csdn.net/uploads/201308/31/1377949629_8247.jpg", 256 "//img-my.csdn.net/uploads/201308/31/1377949615_1986.jpg", 257 "//img-my.csdn.net/uploads/201308/31/1377949614_8482.jpg", 258 "//img-my.csdn.net/uploads/201308/31/1377949614_3743.jpg", 259 "//img-my.csdn.net/uploads/201308/31/1377949614_4199.jpg", 260 "//img-my.csdn.net/uploads/201308/31/1377949599_3416.jpg", 261 "//img-my.csdn.net/uploads/201308/31/1377949599_5269.jpg", 262 "//img-my.csdn.net/uploads/201308/31/1377949598_7858.jpg", 263 "//img-my.csdn.net/uploads/201308/31/1377949598_9982.jpg", 264 "//img-my.csdn.net/uploads/201308/31/1377949578_2770.jpg", 265 "//img-my.csdn.net/uploads/201308/31/1377949578_8744.jpg", 266 "//img-my.csdn.net/uploads/201308/31/1377949577_5210.jpg", 267 "//img-my.csdn.net/uploads/201308/31/1377949577_1998.jpg", 268 "//img-my.csdn.net/uploads/201308/31/1377949482_8813.jpg", 269 "//img-my.csdn.net/uploads/201308/31/1377949481_6577.jpg", 270 "//img-my.csdn.net/uploads/201308/31/1377949480_4490.jpg", 271 "//img-my.csdn.net/uploads/201308/31/1377949455_6792.jpg", 272 "//img-my.csdn.net/uploads/201308/31/1377949455_6345.jpg", 273 "//img-my.csdn.net/uploads/201308/31/1377949442_4553.jpg", 274 "//img-my.csdn.net/uploads/201308/31/1377949441_8987.jpg", 275 "//img-my.csdn.net/uploads/201308/31/1377949441_5454.jpg", 276 "//img-my.csdn.net/uploads/201308/31/1377949454_6367.jpg" }; 277 278 /** 279 * 将大图缩放 280 * 281 * @param path 282 * @param mClolumnWidth 283 */ 284 public Bitmap decodeSimpleBitMapFromResource(String path, int mClolumnWidth) { 285 //第一次解析将inJustDecodeBounds设置为true,来获取图片的大小 286 final Options options = new Options(); 287 options.inJustDecodeBounds = true; 288 Bitmap bitmap = BitmapFactory.decodeFile(path, options); 289 //调用方法计算insampleSize的值 290 options.inSampleSize = decodeSimpleSize(options, mClolumnWidth); 291 //使用获取到的inSampleSize的值 292 options.inJustDecodeBounds = false; 293 return BitmapFactory.decodeFile(path, options); 294 295 } 296 297 /** 298 * 获得simpleSize 299 * 300 * @param options 301 * @return 302 */ 303 private int decodeSimpleSize(Options options, int reqwidth) { 304 //源图片的宽度 305 int width = options.outWidth; 306 int simplesize = 1; 307 if (width > reqwidth) { 308 //计算出实际宽度和目标宽度的比率 309 simplesize = Math.round(width / reqwidth); 310 } 311 return simplesize; 312 } 313 }
第三个类
1 package com.pangzaifei.falls; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.File; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.net.HttpURLConnection; 10 import java.net.URL; 11 import java.util.ArrayList; 12 import java.util.HashSet; 13 import java.util.List; 14 import java.util.Set; 15 16 import android.content.Context; 17 import android.graphics.Bitmap; 18 import android.graphics.BitmapFactory; 19 import android.os.AsyncTask; 20 import android.os.Environment; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.util.AttributeSet; 24 import android.view.MotionEvent; 25 import android.view.View; 26 import android.view.View.OnTouchListener; 27 import android.widget.ImageView; 28 import android.widget.ImageView.ScaleType; 29 import android.widget.LinearLayout; 30 import android.widget.ScrollView; 31 import android.widget.Toast; 32 33 /** 34 * 瀑布流类 <主要用到了LruCache类,缓存图片 > 35 * 36 * 原理: 1:创建3个linearlayout,设置他们的宽度,将获得的图片压缩成和3个linearlayout一样的宽度, 37 * 然后根据3个linearlayout的高度来判断,将bitmap添加到哪一个linearlayout中 38 * 2:翻页处理,根据手势抬起的位置和滑动的末尾处来进行翻页 39 * 40 */ 41 /* 42 ScrollView原理 43 视图的滚动过程,其实是在不断修改原点坐标。当手指触摸后,ScrollView会暂时拦截触摸事件, 44 使用一个计时器。假如在计时器到点后没有发生手指移动事件,那么ScrollView发送tracking events到被点击的subView; 45 若是在计时器到点后发生了移动事件,那么ScrollView取消tracking自己促发滚动。 46 */ 47 /*首先还是讲一下实现原理,瀑布流的布局方式虽然看起来好像排列的很随意,其实它是有很科学的排列规则的。 48 * 整个界面会根据屏幕的宽度划分成等宽的若干列,由于手机的屏幕不是很大,这里我们就分成三列。每当需要添加一张图片时, 49 * 会将这张图片的宽度压缩成和列一样宽,再按照同样的压缩比例对图片的高度进行压缩,然后在这三列中找出当前高度最小的一列, 50 * 将图片添加到这一列中。之后每当需要添加一张新图片时,都去重复上面的操作,就会形成瀑布流格局的照片墙 51 */ 52 public class PhotoFallScrollView extends ScrollView implements OnTouchListener { 53 /** 54 * 记录当前已加载到第几页 55 */ 56 private static int page; 57 /** 58 * 每页显示多少张图片的数量 59 */ 60 private static final int PAGE_SIZE = 8; 61 private Context mContext; 62 /** 63 * 数据源图片 64 */ 65 private Images mImagesThoumb; 66 /** 67 * task请求集合 68 */ 69 private Set<DownLoadTask> mTasks; 70 //判断是否是第一次进入 71 boolean isFirstEntr = true; 72 73 /**三列布局 74 * 75 */ 76 private LinearLayout mFirstColumn; 77 private LinearLayout mSecondColumn; 78 private LinearLayout mThirdColumn; 79 //当前第一列,第二列,第三列的高度 80 private int mFirstColumnHeight; 81 private int mSecondColumnHeight; 82 private int mThirdColumnHeight; 83 /** 84 * 每一列的宽度 85 */ 86 private int mClolumnWidth; 87 88 private long mDelay = 5; 89 /** 90 * 上次滑动的最后位置 91 */ 92 private static int lastScrollY = -1; 93 94 /** 95 * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次 96 */ 97 private boolean loadOnce; 98 /** 99 * 存放图片的集合 100 */ 101 private List<ImageView> mImageViewList = new ArrayList<ImageView>(); 102 103 public PhotoFallScrollView(Context context, AttributeSet attrs, int defStyle) { 104 super(context, attrs, defStyle); 105 this.mContext = context; 106 init(); 107 } 108 109 public PhotoFallScrollView(Context context, AttributeSet attrs) { 110 super(context, attrs); 111 this.mContext = context; 112 init(); 113 } 114 115 public PhotoFallScrollView(Context context) { 116 super(context); 117 this.mContext = context; 118 init(); 119 } 120 121 /** 122 * 初始化 123 */ 124 private void init() { 125 mImagesThoumb = Images.getInstance(); 126 mTasks = new HashSet<DownLoadTask>(); 127 setOnTouchListener(this); 128 } 129 130 /** 131 * 进行一些关键性的初始化操作,获取MyScrollView的高度以及得到第一列的宽度值。 132 * 并在这里开始加载第一页的图片。 133 * 134 */ 135 @Override 136 protected void onLayout(boolean changed, int l, int t, int r, int b) { 137 super.onLayout(changed, l, t, r, b); 138 139 // 第一次进入就加载第一页的图片 140 if (changed && !loadOnce) { 141 mScrollViewHeight = this.getHeight(); 142 mScrollLayout = this.getChildAt(0);//从id的值为0处开始获取 143 mFirstColumn = (LinearLayout) findViewById(R.id.first_column); 144 mSecondColumn = (LinearLayout) findViewById(R.id.second_column); 145 mThirdColumn = (LinearLayout) findViewById(R.id.third_column); 146 //获取每一列的宽度 147 mClolumnWidth = mFirstColumn.getWidth(); 148 loadOnce = true; 149 //开始加载下一页的图片 150 loadMoreImages(); 151 } 152 } 153 154 /**开始加载下一页的图片,每张图片都会开启一个异步线程去下载。 155 * 加载图片 156 */ 157 private void loadMoreImages() { 158 if (hashSdcard()) { 159 160 // 根据页数加载图片 161 int startIndex = page * PAGE_SIZE; 162 int endIndex = page * PAGE_SIZE + PAGE_SIZE; 163 164 if (startIndex < mImagesThoumb.imageThumbs.length) { 165 if (endIndex > mImagesThoumb.imageThumbs.length) { 166 endIndex = mImagesThoumb.imageThumbs.length; 167 } 168 for (int i = startIndex; i < endIndex; i++) { 169 String imageUrl = mImagesThoumb.imageThumbs[i].toString(); 170 if (imageUrl != null && !"".equals(imageUrl)) { 171 //开始下载图片 172 downLoadData(imageUrl); 173 } 174 } 175 page++; 176 } else { 177 Toast.makeText(mContext, "没有更多图片了", 0).show(); 178 } 179 } else { 180 Toast.makeText(mContext, "无sdcard", 0).show(); 181 } 182 } 183 184 /** 185 * 下载 186 * 187 * @param imageUrl 188 */ 189 private void downLoadData(String imageUrl) { 190 DownLoadTask task = new DownLoadTask(); 191 mTasks.add(task); 192 //执行 193 task.execute(imageUrl); 194 } 195 196 /** 197 *异步下载图片 198 */ 199 public class DownLoadTask extends AsyncTask<String, String, Bitmap> { 200 /** 201 * 图片的URL地址 202 */ 203 private String mImageUrl; 204 205 @Override 206 protected Bitmap doInBackground(String... params) { 207 try { 208 mImageUrl = params[0]; 209 Bitmap bitmapFromMemory = mImagesThoumb 210 .getMemoryCache(mImageUrl); 211 if (bitmapFromMemory != null) { 212 return bitmapFromMemory; 213 } 214 if (hashSdcard()) { 215 Bitmap bitmap = loadImage(mImageUrl); 216 return bitmap; 217 } else { 218 Toast.makeText(mContext, "无sdcard,无法获取图片", 0).show(); 219 } 220 221 } catch (Exception e) { 222 e.printStackTrace(); 223 } 224 return null; 225 } 226 227 @Override 228 protected void onPostExecute(Bitmap bitmap) { 229 super.onPostExecute(bitmap); 230 // 展示图片 231 if (bitmap != null) { 232 // 1.缩放图片 233 // 2.新建ImageView 234 // 3.找到需要的linerlayout添加imageView 235 float width = bitmap.getWidth(); 236 float radio = width / mFirstColumn.getWidth(); 237 float scaleHeight = bitmap.getHeight() / radio; 238 addImage(bitmap, mFirstColumn.getWidth(), scaleHeight); 239 } 240 mTasks.remove(this); 241 } 242 243 /** 244 * 向ImageView中添加一张图片 245 * 246 * @param bitmap 247 * 待添加的图片 248 * @param width 249 * 图片的宽度 250 * @param scaleHeight 251 * 图片的高度 252 */ 253 public void addImage(Bitmap bitmap, float width, float scaleHeight) { 254 // 生成缩放的iv 255 ImageView iv = new ImageView(mContext); 256 android.view.ViewGroup.LayoutParams params = new LayoutParams( 257 (int) width, (int) scaleHeight); 258 iv.setLayoutParams(params); 259 if (bitmap != null) { 260 // 解决默认图片有大有小的问题 261 iv.setScaleType(ScaleType.FIT_XY); 262 iv.setPadding(5, 5, 5, 5); 263 264 iv.setImageBitmap(bitmap); 265 iv.setTag(R.string.iamgurl, mImageUrl); 266 findColumnToAdd(iv, (int) scaleHeight).addView(iv); 267 mImageViewList.add(iv); 268 } 269 } 270 271 } 272 273 /**将图片下载到SD卡缓存起来。 274 * @param imageUrl 图片的URL地址。 275 * @return 276 * @throws IOException 277 */ 278 private Bitmap downLoad(String imageUrl) throws IOException { 279 BufferedInputStream bis = null; 280 FileOutputStream fos = null; 281 BufferedOutputStream bos = null; 282 HttpURLConnection conn = null; 283 File imageFile = null; 284 try { 285 URL url = new URL(imageUrl); 286 conn = (HttpURLConnection) url.openConnection(); 287 conn.setReadTimeout(10000); 288 conn.setConnectTimeout(5000); 289 conn.setDoInput(true); 290 conn.setDoOutput(true); 291 InputStream is = conn.getInputStream(); 292 imageFile = new File(getImagePath(imageUrl)); 293 bis = new BufferedInputStream(is); 294 fos = new FileOutputStream(imageFile); 295 bos = new BufferedOutputStream(fos); 296 int len = 0; 297 byte[] buffer = new byte[1024]; 298 while ((len = bis.read(buffer)) != -1) { 299 bos.write(buffer, 0, len); 300 bos.flush(); 301 } 302 } catch (Exception e) { 303 e.printStackTrace(); 304 } finally { 305 if (bis != null) { 306 bis.close(); 307 } 308 if (bos != null) { 309 bos.close(); 310 } 311 if (conn != null) { 312 conn.disconnect(); 313 } 314 } 315 // 如果imageFile不为null,将图片添加到memory中 316 if (imageFile != null) { 317 Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath()); 318 mImagesThoumb.addBitmapToMemoryCache(imageUrl, bitmap); 319 return bitmap; 320 } 321 return null; 322 323 } 324 325 326 /** 327 * 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。 328 * 329 * @param imageUrl 330 * 图片的URL地址 331 * @return 加载到内存的图片。 332 * 判断图片sdcard是否有图片,如果有就用,没有就下载 333 */ 334 public Bitmap loadImage(String mImageUrl) throws Exception { 335 File file = new File(getImagePath(mImageUrl)); 336 if (!file.exists()) { 337 downLoad(mImageUrl); 338 } 339 340 if (mImageUrl != null) { 341 // 处理本地图片,设置大小防止oom 342 Bitmap bitmap = mImagesThoumb.decodeSimpleBitMapFromResource( 343 file.getPath(), mClolumnWidth); 344 // Bitmap bitmap = BitmapFactory.decodeFile(file.getPath()); 345 if (bitmap != null) { 346 mImagesThoumb.addBitmapToMemoryCache(mImageUrl, bitmap); 347 return bitmap; 348 } 349 } 350 return null; 351 } 352 353 /** 354 * 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。 355 * 356 * @param iv 357 * @param imageHeight 358 * @return 应该添加图片的一列 359 */ 360 private LinearLayout findColumnToAdd(ImageView iv, int imageHeight) { 361 if (mFirstColumnHeight <= mSecondColumnHeight) { 362 if (mFirstColumnHeight <= mThirdColumnHeight) { 363 iv.setTag(R.string.border_top, mFirstColumnHeight); 364 mFirstColumnHeight += imageHeight; 365 iv.setTag(R.string.border_bottom, mFirstColumnHeight); 366 return mFirstColumn; 367 } 368 iv.setTag(R.string.border_top, mThirdColumnHeight); 369 mThirdColumnHeight += imageHeight; 370 iv.setTag(R.string.border_bottom, mThirdColumnHeight); 371 return mThirdColumn; 372 373 } else { 374 if (mSecondColumnHeight <= mThirdColumnHeight) { 375 iv.setTag(R.string.border_top, mSecondColumnHeight); 376 mSecondColumnHeight += imageHeight; 377 iv.setTag(R.string.border_bottom, mSecondColumnHeight); 378 return mSecondColumn; 379 } 380 iv.setTag(R.string.border_top, mThirdColumnHeight); 381 mThirdColumnHeight += imageHeight; 382 iv.setTag(R.string.border_bottom, mThirdColumnHeight); 383 return mThirdColumn; 384 } 385 } 386 387 /**获取图片的本地存储路径。 388 * 获得file地址 389 * 390 * @param imageUrl 图片的URL地址。 391 * @return 图片的本地存储路径。 392 */ 393 private String getImagePath(String imageUrl) { 394 int lastIndexOf = imageUrl.lastIndexOf("/"); 395 String imageName = imageUrl.substring(lastIndexOf + 1); 396 String imageDir = Environment.getExternalStorageDirectory().getPath() 397 + "/pangzaifei/"; 398 File file = new File(imageDir); 399 if (!file.exists()) { 400 file.mkdir(); 401 } 402 String imagePath = imageDir + imageName; 403 return imagePath; 404 } 405 406 /**判断手机是否有sd卡 407 * 获得图片的名字 408 * 409 * @param imageUrl 410 */ 411 412 private boolean hashSdcard() { 413 if (Environment.getExternalStorageState().equals( 414 Environment.MEDIA_MOUNTED)) { 415 return true; 416 } 417 return false; 418 } 419 420 @Override 421 /** 422 * 当手势抬起时,开始每个5毫秒计算位置 423 * 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。 424 */ 425 public boolean onTouch(View v, MotionEvent event) { 426 if (event.getAction() == MotionEvent.ACTION_UP) { 427 // 发送handler 428 Message msg = mHandler.obtainMessage(); 429 msg.obj = this; 430 mHandler.sendMessageDelayed(msg, mDelay); 431 } 432 return false; 433 } 434 435 /** 436 * 在Handler中进行图片可见性检测的判断,以及加载更多图片的操作 437 */ 438 private Handler mHandler = new Handler() { 439 440 @Override 441 public void handleMessage(Message msg) { 442 super.handleMessage(msg); 443 // 判断是否已经滑到了最低处,如果滑到了最低处,则加载更多页面,否则继续发送handler扫描 444 PhotoFallScrollView scrollView = (PhotoFallScrollView) msg.obj; 445 int scrollY = scrollView.getScrollY(); 446 //如果当前的滚动位置和上次相同,表示已停止滚动 447 if (scrollY == lastScrollY) { 448 // 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片 449 if (mScrollViewHeight + scrollY >= mScrollLayout.getHeight() 450 && mTasks.isEmpty()) { 451 scrollView.loadMoreImages(); 452 } 453 scrollView.checkVisibile(); 454 } else { 455 lastScrollY = scrollY; 456 Message message = new Message(); 457 message.obj = scrollView; 458 //5毫秒后在对滚动位置进行判断 459 mHandler.sendMessageDelayed(message, mDelay); 460 } 461 } 462 463 }; 464 /** 465 * MyScrollView布局的高度。 466 */ 467 private int mScrollViewHeight; 468 /** 469 * MyScrollView下的直接子布局。 470 */ 471 private View mScrollLayout; 472 473 /** 474 * 遍历imageview中的每一张图片,对图片的可见性进行检测,如果图片已经离开屏幕可见范围, 475 * 则将图片替换成一张空图 476 * 想不可见的变为空图片 477 */ 478 protected void checkVisibile() { 479 if (mImageViewList != null && mImageViewList.size() > 0) { 480 for (int i = 0; i < mImageViewList.size(); i++) { 481 ImageView iv = mImageViewList.get(i); 482 int borderTop = (Integer) iv.getTag(R.string.border_top); 483 int borderBottom = (Integer) iv.getTag(R.string.border_bottom); 484 if (borderBottom > getScrollY() 485 && borderTop < getScrollY() + mScrollViewHeight) { 486 String imageUrl = (String) iv.getTag(R.string.iamgurl); 487 if (imageUrl != null && !"".equals(imageUrl)) { 488 Bitmap bitmap = mImagesThoumb.getMemoryCache(imageUrl); 489 if (bitmap != null) { 490 iv.setImageBitmap(bitmap); 491 } else { 492 downLoadData(imageUrl); 493 } 494 } 495 } else { 496 //离开屏幕加载空图 497 iv.setImageResource(R.drawable.empty_photo); 498 } 499 500 } 501 } 502 } 503 }