├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imagelib ├── .gitignore ├── bintray.properties ├── bintrayUpload.gradle ├── build.gradle ├── proguard-rules.pro ├── project.properties └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── baofeng │ │ └── soulrelay │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── baofeng │ │ │ └── soulrelay │ │ │ └── utils │ │ │ ├── CommonUtils.java │ │ │ └── imageloader │ │ │ ├── BaseImageLoaderStrategy.java │ │ │ ├── GlideImageLoaderStrategy.java │ │ │ ├── ImageLoaderConfiguration.java │ │ │ ├── ImageLoaderUtil.java │ │ │ ├── MyGlideModule.java │ │ │ ├── glideprogress │ │ │ ├── ProgressDataFetcher.java │ │ │ ├── ProgressListener.java │ │ │ ├── ProgressLoadListener.java │ │ │ ├── ProgressModelLoader.java │ │ │ ├── ProgressResponseBody.java │ │ │ └── ProgressUIListener.java │ │ │ ├── listener │ │ │ ├── ImageSaveListener.java │ │ │ └── SourceReadyListener.java │ │ │ └── transformation │ │ │ └── GlideCircleTransform.java │ └── res │ │ ├── drawable-xhdpi │ │ ├── avata_default.png │ │ └── bg_default_video_common_small.png │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── raw │ │ ├── catalog.png │ │ └── pic_20170509.gif │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── baofeng │ └── soulrelay │ └── ExampleUnitTest.java ├── mainapp ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── soulrelay │ │ └── image │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── soulrelay │ │ │ └── image │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── soulrelay │ └── image │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 重磅更新 2 | 使用ImageLoaderUtil实现一个真正意义的图集功能,持续完善和更新中 3 | 4 | [Gallery](https://github.com/soulrelay/Gallery) 5 | 6 | [Gallery](https://github.com/soulrelay/Gallery) 7 | 8 | [Gallery](https://github.com/soulrelay/Gallery) 9 | 10 | 重要的东西贴三遍! 11 | 12 | ----------------------------------分隔线--------------------------------------- 13 | 14 | ## Use Gradle 15 | ``` 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | maven { 20 | name 'glide-snapshot' 21 | url 'http://oss.sonatype.org/content/repositories/snapshots' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | //compile project(':imagelib') 28 | compile 'com.sus.library:imagelib:1.0.1' 29 | } 30 | ``` 31 | 32 | 33 | ![这里写图片描述](https://github.com/soulrelay/ImageLoaderUtil/blob/master/imagelib/src/main/res/raw/catalog.png) 34 | 35 | ## 前言 36 | >* 图片加载是Android开发中最最基础的功能,为了降低开发周期和难度,我们经常会选用一些图片加载的开源库 37 | >* [选取第三方SDK需要谨慎](http://blog.csdn.net/s003603u/article/details/53257859) 38 | >* [二次封装](http://blog.csdn.net/s003603u/article/details/53257965) 39 | 40 | 注意:所有改动更新会同步到[GitHub](https://github.com/soulrelay/ImageLoaderUtil) 41 | ## 主流图片加载库的对比 42 | >* 共同点 43 | * 使用简单:一句话实现图片的获取和显示 44 | * 可配置性高:可配置各种解码、缓存、下载机制 45 | * 自适应程度高:根据系统性能调整配置策略(如CPU核数决定最大并发数、内存决定内存缓存大小、网络状态变化调整最大并发数) 46 | * 多级缓存 47 | * 支持多种数据源 48 | * 支持多种Displayer 49 | * 兼容性好(可以配合okhttp等库进行使用) 50 | ### Android-Universal-Image-Loader 51 | >* 简介 52 | * 作者:nostra13 53 | * 面世时间:2011 54 | * star数(截止到发稿):14509 55 | * [https://github.com/nostra13/Android-Universal-Image-Loader](https://github.com/nostra13/Android-Universal-Image-Loader) 56 | >* 优点 57 | * 支持下载进度监听(ImageLoadingListener) 58 | * 可在View滚动中暂停图片加载(PauseOnScrollListener) 59 | * 默认实现多种内存缓存算法(最大最先删除,使用最少最先删除,最近最少使用,先进先删除,当然自己也可以配置缓存算法) 60 | >* 缺点 61 | * 从2015.11.27之后不再维护,项目中不建议使用 62 | 63 | ### Picasso 64 | >* 简介 65 | * 作者:JakeWharton(Square) 66 | * 面世时间:2012 67 | * star数(截止到发稿):12076 68 | * [https://github.com/square/picasso](https://github.com/square/picasso) 69 | >* 优点 70 | * 包较小(100k) 71 | * 取消不在视野范围内图片资源的加载 72 | * 使用最少的内存完成复杂的图片转换 73 | * 自动添加二级缓存 74 | * 任务调度优先级处理 75 | * 并发线程数根据网络类型调整 76 | * 图片的本地缓存交给同为Square出品的okhttp处理,控制图片的过期时间 77 | >* 缺点 78 | * 功能较为简单 79 | * 自身无实现“本地缓存” 80 | 81 | ### Glide 82 | >* 简介 83 | * 作者:Sam sjudd (Google) 84 | * 面世时间:2013 85 | * star数(截止到发稿):12067 86 | * [https://github.com/bumptech/glide](https://github.com/bumptech/glide) 87 | >* 优点 88 | * 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video) 89 | * 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求) 90 | * 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力) 91 | * 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半) 92 | >* 缺点 93 | * 方法较多较复杂,因为相当于在Picasso上的改进,包较大(500k),影响不是很大 94 | 95 | ### Fresco 96 | >* 简介 97 | * 作者:Facebook 98 | * 面世时间:2015 99 | * star数(截止到发稿):11235 100 | * [https://github.com/facebook/fresco](https://github.com/facebook/fresco) 101 | >* 优点 102 | * 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区) 103 | * 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存) 104 | * 适用于需要高性能加载大量图片的场景 105 | >* 缺点 106 | * 包较大(2~3M) 107 | * 用法复杂 108 | * 底层涉及c++领域,阅读源码深入学习难度大 109 | 110 | ## 按需选择图片加载库 111 | >* 图片加载需要支持Gif,之前项目中使用的Android-Universal-Image-Loader不支持Gif且Android-Universal-Image-Loader已经停止维护,遂决定替换图片加载库 112 | >* 分析完优缺点最终选择Glide的其它理由: 113 | * Glide是在Picasso的基础上进行改进的(支持Gif,内存开销小),虽然500k左右的包大小相对于Picasso较大,但是这个数量级的影响可以接受 114 | * 初衷是想一直维持图片的原始ImageView,而 Fresco需要在布局文件中将图片控件声明为库中自定义的SimpleDraweeView,如果切库还需要更改组件,代价会很高 115 | * Google推荐(亲儿子),在Google很多开源项目中广泛使用 116 | 117 | >* 但不可避免的是,Glide在使用的过程中依然存在着许多坑需要我们去填! 118 | 119 | ## 如何更好地封装图片加载库 120 | ### 为什么要封装? 121 | 先从现在面对的情形来看,项目中使用图片加载的地方都是使用的类似下面的语句 122 | ``` 123 | ImageLoader.getInstance().displayImage(imageUrl, imageView,options); 124 | ``` 125 | 然而现在ImageLoader已经停止维护且已经无法满足项目需求,我们需要替换,这时你会发现如果换库的话,所有涉及到的地方都要修改(Android-Universal-Image-Loader已经和图片加载的业务逻辑严重地耦合在一起了),工作量可见一斑,这就是不封装在切库时面临的窘境! 126 | 那怎么解决那? 127 | 计算机史上有个万能的解决方案就是,如果原有层面解决不了问题,那么就请再加一层! 128 | 129 | ``` 130 | /** 131 | * Created by soulrelay on 2016/10/11 13:42. 132 | * Class Note: 133 | * use this class to load image,single instance 134 | */ 135 | public class ImageLoaderUtil { 136 | 137 | //图片默认加载类型 以后有可能有多种类型 138 | public static final int PIC_DEFAULT_TYPE = 0; 139 | 140 | //图片默认加载策略 以后有可能有多种图片加载策略 141 | public static final int LOAD_STRATEGY_DEFAULT = 0; 142 | 143 | private static ImageLoaderUtil mInstance; 144 | 145 | private BaseImageLoaderStrategy mStrategy; 146 | 147 | public ImageLoaderUtil() { 148 | mStrategy = new GlideImageLoaderStrategy(); 149 | } 150 | 151 | //单例模式,节省资源 152 | public static ImageLoaderUtil getInstance() { 153 | if (mInstance == null) { 154 | synchronized (ImageLoaderUtil.class) { 155 | if (mInstance == null) { 156 | mInstance = new ImageLoaderUtil(); 157 | return mInstance; 158 | } 159 | } 160 | } 161 | return mInstance; 162 | } 163 | 164 | /** 165 | * 统一使用App context 166 | * 可能带来的问题:http://stackoverflow.com/questions/31964737/glide-image-loading-with-application-context 167 | * 168 | * @param url 169 | * @param placeholder 170 | * @param imageView 171 | */ 172 | public void loadImage(String url, int placeholder, ImageView imageView) { 173 | mStrategy.loadImage(imageView.getContext(), url, placeholder, imageView); 174 | } 175 | 176 | public void loadGifImage(String url, int placeholder, ImageView imageView) { 177 | mStrategy.loadGifImage(url, placeholder, imageView); 178 | } 179 | 180 | public void loadImage(String url, ImageView imageView) { 181 | mStrategy.loadImage(url, imageView); 182 | } 183 | 184 | /** 185 | * 展示图片加载进度 186 | */ 187 | public void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener) { 188 | mStrategy.loadImageWithProgress(url,imageView,listener); 189 | } 190 | 191 | public void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener) { 192 | mStrategy.loadGifWithProgress(url,imageView,listener); 193 | } 194 | 195 | /** 196 | * 策略模式的注入操作 197 | * 198 | * @param strategy 199 | */ 200 | public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) { 201 | mStrategy = strategy; 202 | } 203 | 204 | /** 205 | * 清除图片磁盘缓存 206 | */ 207 | public void clearImageDiskCache(final Context context) { 208 | mStrategy.clearImageDiskCache(context); 209 | } 210 | 211 | /** 212 | * 清除图片内存缓存 213 | */ 214 | public void clearImageMemoryCache(Context context) { 215 | mStrategy.clearImageMemoryCache(context); 216 | } 217 | 218 | /** 219 | * 根据不同的内存状态,来响应不同的内存释放策略 220 | * 221 | * @param context 222 | * @param level 223 | */ 224 | public void trimMemory(Context context, int level) { 225 | mStrategy.trimMemory(context, level); 226 | } 227 | 228 | /** 229 | * 清除图片所有缓存 230 | */ 231 | public void clearImageAllCache(Context context) { 232 | clearImageDiskCache(context.getApplicationContext()); 233 | clearImageMemoryCache(context.getApplicationContext()); 234 | } 235 | 236 | /** 237 | * 获取缓存大小 238 | * 239 | * @return CacheSize 240 | */ 241 | public String getCacheSize(Context context) { 242 | return mStrategy.getCacheSize(context); 243 | } 244 | 245 | 246 | } 247 | ``` 248 | 所有需要图片显示的地方使用如下方法进行调用: 249 | 250 | >* 入口唯一,所有图片加载都在ImageLoaderUtil这一个地方统一管理,使用了[单例模式](http://blog.csdn.net/s003603u/article/details/51982140)(据说单元素的枚举类型已经成为实现Singleton的最佳方法,你可以试试 ), 251 | >* 高效地封装减少了切库(只需要切换图片加载策略)带来的代价,默认采用GlideImageLoaderStrategy 252 | 253 | 总结:外部表现一致,内部灵活处理原则。 254 | 255 | ``` 256 | /** 257 | * 图片加载库的封装演示案例 258 | * Created by soulrelay on 2016/12/11 19:18 259 | */ 260 | public class MainActivity extends AppCompatActivity { 261 | 262 | @BindView(R.id.iv_normal) 263 | ImageView ivNormal; 264 | @BindView(R.id.iv_gif) 265 | ImageView ivGif; 266 | @BindView(R.id.iv_gif1) 267 | ImageView ivGif1; 268 | 269 | @Override 270 | protected void onCreate(Bundle savedInstanceState) { 271 | super.onCreate(savedInstanceState); 272 | setContentView(R.layout.activity_main); 273 | ButterKnife.bind(this); 274 | initView(); 275 | } 276 | 277 | private void initView() { 278 | ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be", R.drawable.bg_default_video_common_small, ivNormal); 279 | ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif); 280 | ImageLoaderUtil.getInstance().loadGifImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif1); 281 | 282 | } 283 | 284 | } 285 | ``` 286 | 287 | 效果图如下所示: 288 | ![这里写图片描述](https://github.com/soulrelay/ImageLoaderUtil/blob/master/app/src/main/res/raw/pic_20170509.gif) 289 | ### 使用策略模式封装图片加载策略 290 | 如果你对策略模式不是很熟,请先参考[策略模式和状态模式](http://blog.csdn.net/s003603u/article/details/52033391) 291 | 首先我们需要抽象出一个图片加载的基础接口BaseImageLoaderStrategy 292 | 基本功能主要包括 293 | >* 正常加载图片 294 | >* 针对于GIF图片的特殊加载 295 | >* 加载图片的进度回调 296 | >* 清除缓存 297 | >* 获取缓存大小等 298 | >* 其它特殊需求自己封装,最好不要破坏策略模式的整体结构 299 | 300 | ``` 301 | /** 302 | * Created by soulrelay on 2016/10/11. 303 | * Class Note: 304 | * abstract class/interface defined to load image 305 | * (Strategy Pattern used here) 306 | */ 307 | public interface BaseImageLoaderStrategy { 308 | //无占位图 309 | void loadImage(String url, ImageView imageView); 310 | 311 | void loadImage(String url, int placeholder, ImageView imageView); 312 | 313 | void loadImage(Context context, String url, int placeholder, ImageView imageView); 314 | 315 | void loadGifImage(String url, int placeholder, ImageView imageView); 316 | 317 | void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener); 318 | 319 | void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener); 320 | 321 | //清除硬盘缓存 322 | void clearImageDiskCache(final Context context); 323 | //清除内存缓存 324 | void clearImageMemoryCache(Context context); 325 | //根据不同的内存状态,来响应不同的内存释放策略 326 | void trimMemory(Context context, int level); 327 | //获取缓存大小 328 | String getCacheSize(Context context); 329 | 330 | } 331 | ``` 332 | 需要说明的一点是: 333 | >* 当封装的方法参数比较少时可以按照上述方式进行抽象,如果需要传递的参数较多,可以考虑使用建造者模式[建造者模式](http://blog.csdn.net/s003603u/article/details/51967809) 334 | >* 例如封装一个ImageLoaderConfiguration,包含如下参数等等,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 335 | * type 图片加载的类型(大图、小图、中图) 336 | * url 需要解析的url 337 | * placeHolder 当没有成功加载的时候显示的图片 338 | * imgView ImageView的实例 339 | * loadStrategy 加载策略 340 | >* 当然这里我没有使用建造模式,考虑到目前使用的对象还不算复杂(传参比较简单),而且如果使用建造者模式有可能每次都要new一个新的对象实例,虽然开销可以接受 341 | >* 使用ImageLoaderUtil的过程中,注意内存泄露的问题(静态单例的生命周期与App一样,当一个单例的对象长久不用时,不会被垃圾收集机制回收) 342 | 343 | 然后基于每个图片库的各自方式来进行相应策略的封装,需要使用哪种策略,只需要通过ImageLoaderUtil的setLoadImgStrategy(BaseImageLoaderStrategy strategy)方法将相应的策略注入,相关类图关系如下所示: 344 | 345 | ![这里写图片描述](http://img.blog.csdn.net/20161204205952522) 346 | 347 | 不同的图片加载库实现不同的图片加载策略 348 | 这里只是给出Glide的图片加载策略类GlideImageLoaderStrategy作为参考 349 | >* Glide依赖v4包,且需要配置android.permission.INTERNET和android.permission.WRITE_EXTERNAL_STORAGE(忘记配置权限,图片加载不出来,还看不出什么异常) 350 | >* 其中部分方法使用到了RequestListener的回调(这里是因为项目中的一些特殊需求而添加,如统计图片首次加载时长来测试一下图片cdn服务器的速度等) 351 | >* 在使用Glide的过程中遇到了一些问题,部分已经在注释中说明 352 | >* 之所以针对gif单独封装,是因为在使用的过程中发现,当在列表中加载大量gif会有OOM的问题,所以通过asGif进行特殊标明,即使这样也会出现类似问题,同时暂时通过skipMemoryCache(true)跳过内存缓存,之后有更好的办法会继续补充,各位看官如有良策,希望可以不吝赐教 353 | >* Glide本身不提供图片的progress回调,所以关于进度回调的解决方案参照的是 354 | [ProgressGlide](https://github.com/shangmingchao/ProgressGlide),并做了些许改动集成到项目中 355 | >* 期间发现了一个很好的问题[Android的App中线程池的使用,具体使用多少个线程池?](https://www.zhihu.com/question/37804956),其中一个答主的关于图片加载库线程池策略的分析很好,值得体会,简单摘录如下: 356 | * UIL的线程池处理非常简单粗暴,没有根据CPU数量来选择,也没有根据网络状况的变化进行调整; 357 | * Picasso的线程池会根据网络状况的变化进行调整,在Wifi下线程数为4,而4G下线程数为3, 3G下为2, 2G下为1,默认状况为3; 358 | * Glide加载缓存未命中的线程池会根据根据CPU的数量和Java虚拟机中可用的处理器数量来选择合适的线程数,但是最多不超过4;而加载缓存命中的图片的线程池默认大小为1. 359 | 360 | ``` 361 | /** 362 | * Created by soulrelay on 2016/10/11 13:48. 363 | * Class Note: 364 | * using {@link Glide} to load image 365 | */ 366 | public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy { 367 | 368 | @Override 369 | public void loadImage(String url, int placeholder, ImageView imageView) { 370 | loadNormal(imageView.getContext(), url, placeholder, imageView); 371 | } 372 | 373 | @Override 374 | public void loadImage(Context context, String url, int placeholder, ImageView imageView) { 375 | loadNormal(context, url, placeholder, imageView); 376 | } 377 | 378 | /** 379 | * 无holder的gif加载 380 | * 381 | * @param url 382 | * @param imageView 383 | */ 384 | @Override 385 | public void loadImage(String url, ImageView imageView) { 386 | Glide.with(imageView.getContext()).load(url).dontAnimate() 387 | .placeholder(imageView.getDrawable()) 388 | .diskCacheStrategy(DiskCacheStrategy.SOURCE) 389 | .into(imageView); 390 | } 391 | 392 | @Override 393 | public void loadGifImage(String url, int placeholder, ImageView imageView) { 394 | loadGif(imageView.getContext(), url, placeholder, imageView); 395 | } 396 | 397 | @Override 398 | public void loadImageWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) { 399 | Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() { 400 | @Override 401 | public void update(final int bytesRead, final int contentLength) { 402 | imageView.post(new Runnable() { 403 | @Override 404 | public void run() { 405 | listener.update(bytesRead, contentLength); 406 | } 407 | }); 408 | } 409 | })).load(url).asBitmap().dontAnimate(). 410 | listener(new RequestListener() { 411 | @Override 412 | public boolean onException(Exception e, Object model, Target target, boolean isFirstResource) { 413 | listener.onException(); 414 | return false; 415 | } 416 | 417 | @Override 418 | public boolean onResourceReady(Bitmap resource, Object model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 419 | listener.onResourceReady(); 420 | return false; 421 | } 422 | }).into(imageView); 423 | } 424 | 425 | @Override 426 | public void loadGifWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) { 427 | Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() { 428 | @Override 429 | public void update(final int bytesRead, final int contentLength) { 430 | imageView.post(new Runnable() { 431 | @Override 432 | public void run() { 433 | listener.update(bytesRead, contentLength); 434 | } 435 | }); 436 | } 437 | })).load(url).asGif().skipMemoryCache(true).dontAnimate(). 438 | listener(new RequestListener() { 439 | @Override 440 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 441 | listener.onException(); 442 | return false; 443 | } 444 | 445 | @Override 446 | public boolean onResourceReady(GifDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 447 | listener.onResourceReady(); 448 | return false; 449 | } 450 | }).into(imageView); 451 | } 452 | 453 | @Override 454 | public void clearImageDiskCache(final Context context) { 455 | try { 456 | if (Looper.myLooper() == Looper.getMainLooper()) { 457 | new Thread(new Runnable() { 458 | @Override 459 | public void run() { 460 | Glide.get(context.getApplicationContext()).clearDiskCache(); 461 | } 462 | }).start(); 463 | } else { 464 | Glide.get(context.getApplicationContext()).clearDiskCache(); 465 | } 466 | } catch (Exception e) { 467 | e.printStackTrace(); 468 | } 469 | } 470 | 471 | @Override 472 | public void clearImageMemoryCache(Context context) { 473 | try { 474 | if (Looper.myLooper() == Looper.getMainLooper()) { //只能在主线程执行 475 | Glide.get(context.getApplicationContext()).clearMemory(); 476 | } 477 | } catch (Exception e) { 478 | e.printStackTrace(); 479 | } 480 | } 481 | 482 | @Override 483 | public void trimMemory(Context context, int level) { 484 | Glide.get(context).trimMemory(level); 485 | } 486 | 487 | @Override 488 | public String getCacheSize(Context context) { 489 | try { 490 | return CommonUtils.getFormatSize(CommonUtils.getFolderSize(Glide.getPhotoCacheDir(context.getApplicationContext()))); 491 | } catch (Exception e) { 492 | e.printStackTrace(); 493 | } 494 | return ""; 495 | } 496 | 497 | /** 498 | * load image with Glide 499 | */ 500 | private void loadNormal(final Context ctx, final String url, int placeholder, ImageView imageView) { 501 | /** 502 | * 为其添加缓存策略,其中缓存策略可以为:Source及None,None及为不缓存,Source缓存原型.如果为ALL和Result就不行.然后几个issue的连接: 503 | https://github.com/bumptech/glide/issues/513 504 | https://github.com/bumptech/glide/issues/281 505 | https://github.com/bumptech/glide/issues/600 506 | modified by xuqiang 507 | */ 508 | 509 | //去掉动画 解决与CircleImageView冲突的问题 这个只是其中的一个解决方案 510 | //使用SOURCE 图片load结束再显示而不是先显示缩略图再显示最终的图片(导致图片大小不一致变化) 511 | final long startTime = System.currentTimeMillis(); 512 | Glide.with(ctx).load(url).dontAnimate() 513 | .placeholder(placeholder) 514 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener() { 515 | @Override 516 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 517 | return false; 518 | } 519 | 520 | @Override 521 | public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 522 | return false; 523 | } 524 | }) 525 | .into(imageView); 526 | } 527 | 528 | /** 529 | * load image with Glide 530 | */ 531 | private void loadGif(final Context ctx, String url, int placeholder, ImageView imageView) { 532 | final long startTime = System.currentTimeMillis(); 533 | Glide.with(ctx).load(url).asGif().dontAnimate() 534 | .placeholder(placeholder).skipMemoryCache(true) 535 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener() { 536 | @Override 537 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 538 | return false; 539 | } 540 | 541 | @Override 542 | public boolean onResourceReady(GifDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 543 | return false; 544 | } 545 | }) 546 | .into(imageView); 547 | } 548 | 549 | } 550 | 551 | ``` 552 | ## 源码地址 553 | [ImageLoaderUtil](https://github.com/soulrelay/ImageLoaderUtil) 554 | 555 | ## 部分参考链接 556 | [http://www.jianshu.com/p/97994c9693f9](http://www.jianshu.com/p/97994c9693f9) 557 | [https://www.zhihu.com/question/37804956](https://www.zhihu.com/question/37804956) 558 | [http://www.jianshu.com/p/e26130a93289](http://www.jianshu.com/p/e26130a93289) 559 | [http://www.cnblogs.com/android-blogs/p/5737611.html](http://www.cnblogs.com/android-blogs/p/5737611.html) 560 | ## 更新 561 | 562 | ### 2016-12-09 ll You must not call setTag() on a view Glide is targeting 563 | 564 | 565 | 566 | 项目中在使用Glide图片加载框架时遇到该错误 567 | 报错原因大致是因为Glide加载的iamgeView调用了setTag()方法导致的错误,因为Glide已经默认为ImageView设置的Tag 568 | 569 | 相关解决方案已经在Glide 3.6.0[(issue #370)](https://github.com/bumptech/glide/issues/370)被引进,实测可行 570 | 在AndroidManifest.xml中加入 571 | 572 | ``` 573 | 575 | ``` 576 | 然后在App中添加如下代码: 577 | 578 | ``` 579 | public class App extends Application { 580 | @Override public void onCreate() { 581 | super.onCreate(); 582 | ViewTarget.setTagId(R.id.glide_tag); 583 | } 584 | } 585 | ``` 586 | 在src/main/values/ids.xml添加如下代码: 587 | 588 | ``` 589 | 590 | 591 | 592 | ``` 593 | 594 | 595 | ### 2016-12-13 ll 添加loadGifWithPrepareCall方法 596 | 2016.12.13 597 | 598 | 只想知道图片是否准备完毕(包括来自网络或者sdcard),区别于loadImageWithProgress和loadGifWithProgress的进度回调 599 | 600 | Tips:使用Glide加载图片注意ImageView的Scaletype的设置 601 | ``` 602 | public interface BaseImageLoaderStrategy { 603 | void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener); 604 | } 605 | ``` 606 | 607 | ``` 608 | public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy { 609 | 610 | @Override 611 | public void loadGifWithPrepareCall(String url, ImageView imageView, final SourceReadyListener listener) { 612 | Glide.with(imageView.getContext()).load(url).asGif().dontAnimate() 613 | .skipMemoryCache(true) 614 | .diskCacheStrategy(DiskCacheStrategy.SOURCE). 615 | listener(new RequestListener() { 616 | @Override 617 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 618 | return false; 619 | } 620 | 621 | @Override 622 | public boolean onResourceReady(GifDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 623 | listener.onResourceReady(resource.getIntrinsicWidth(),resource.getIntrinsicHeight()); 624 | return false; 625 | } 626 | }).into(imageView); 627 | } 628 | } 629 | 630 | ``` 631 | 632 | ``` 633 | public class ImageLoaderUtil { 634 | public void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener) { 635 | mStrategy.loadGifWithPrepareCall(url,imageView,listener); 636 | } 637 | } 638 | ``` 639 | ### 2016-12-26 ll 更新loadGifWithProgress方法 640 | #### 2017-1-10 ll 统一加载图片进度回调方法为loadImageWithProgress,弃用并删除loadGifWithProgress方法 641 | 具体细节查看GitHub最新代码 642 | ### 2016-12-26 ll 自定义GlideModule 并将 Glide与okhttp3集成 643 | 1.自定义一个GlideModule 644 | ``` 645 | /** 646 | * DES:自定义一个GlideModule 647 | *

648 | * GlideModule 是一个抽象方法,全局改变 Glide 行为的一个方式, 649 | * 通过全局GlideModule 配置Glide,用GlideBuilder设置选项,用Glide注册ModelLoader等。 650 | *

651 | */ 652 | public class MyGlideModule implements GlideModule { 653 | @Override 654 | public void applyOptions(Context context, GlideBuilder builder) { 655 | // Apply options to the builder here. 656 | int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小 657 | int memoryCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一 658 | //设置内存缓存大小 659 | builder.setMemoryCache(new LruResourceCache(memoryCacheSize)); 660 | builder.setBitmapPool(new LruBitmapPool(memoryCacheSize)); 661 | } 662 | 663 | @Override 664 | public void registerComponents(Context context, Glide glide) { 665 | // register ModelLoaders here. 666 | } 667 | } 668 | ``` 669 | 2.AndroidManifest.xml注册 670 | ``` 671 | 672 | 673 | 674 | 675 | 678 | 679 | 680 | 681 | 682 | ``` 683 | 3、 Glide与OkHttp3集成 684 | 685 | ``` 686 | compile 'com.squareup.okhttp3:okhttp:3.4.2' 687 | compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' 688 | ``` 689 | 4、添加混淆处理 690 | 691 | ``` 692 | #--------------------Glide-----------------------# 693 | -keep public class * implements com.bumptech.glide.module.GlideModule 694 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 695 | **[] $VALUES; 696 | public *; 697 | } 698 | 699 | -keepnames class com.baofeng.soulrelay.utils.imageloader.MyGlideModule 700 | # or more generally: 701 | -keep public class * implements com.bumptech.glide.module.GlideModule 702 | -keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule 703 | 704 | #--------------------Glide-----------------------# 705 | ``` 706 | 707 | ### 2017-1-6 ll GIF帧显示不完全 2017-1-10补充说明PS 708 | 相关问题在[issues1649](https://github.com/bumptech/glide/issues/1649)中被提到和解决(目前glide:3.7.0的确存在这个问题) 709 | 具体解决方法是: 710 | glide:3.8.0-SNAPSHOT修复了关于GIF展示的一些bug,实测可用 711 | Gradle配置修改如下: 712 | >* Add the snapshot repo to your list of repositories: 713 | ``` 714 | repositories { 715 | jcenter() 716 | maven { 717 | name 'glide-snapshot' 718 | url 'http://oss.sonatype.org/content/repositories/snapshots' 719 | } 720 | } 721 | ``` 722 | 723 | 724 | >* And then change your dependencies to the v3 snapshot version: 725 | 726 | ``` 727 | dependencies { 728 | compile 'com.github.bumptech.glide:glide:3.8.0-SNAPSHOT' 729 | compile 'com.github.bumptech.glide:okhttp-integration:1.5.0-SNAPSHOT' 730 | } 731 | ``` 732 | #### 2017-1-10补充说明PS 733 | PS:提供一个gif图 帧提取工具[GIFFrame.exe](http://download.csdn.net/detail/s003603u/9733492) 734 | 据我分析,那些没有显示完整的GIF图片,里面的部分帧图片本身就不是完整的,但是之前的Glide并没有做很好的处理,所以显示效果有缺陷,当然最新的3.8.0-SNAPSHOT解决了这个问题,但是在显示的时候仍有瑕疵(有一些重叠,当然我觉得这也跟gif图的做工有关) 735 | ### 2017-1-6 ll You cannot start a load for a destroyed activity 736 | 完整异常信息: 737 | ``` 738 | FATAL EXCEPTION: main 739 | Process: com.sports.baofeng, PID: 9170 740 | java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity 741 | at com.bumptech.glide.d.k.b(SourceFile:134) 742 | at com.bumptech.glide.d.k.a(SourceFile:102) 743 | at com.bumptech.glide.d.k.a(SourceFile:87) 744 | at com.bumptech.glide.i.c(SourceFile:629) 745 | at com.storm.durian.common.utils.imageloader.b.a(SourceFile:1194) 746 | at com.storm.durian.common.utils.imageloader.c.a(SourceFile:52) 747 | at com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:311) 748 | at com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:1347) 749 | at com.sports.baofeng.specialtopic.d.a(SourceFile:1052) 750 | at com.sports.baofeng.specialtopic.c$1.a(SourceFile:1064) 751 | at com.storm.durian.common.b.b$1.onPostExecute(SourceFile:57) 752 | at android.os.AsyncTask.finish(AsyncTask.java:651) 753 | at android.os.AsyncTask.access$500(AsyncTask.java:180) 754 | at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668) 755 | at android.os.Handler.dispatchMessage(Handler.java:102) 756 | at android.os.Looper.loop(Looper.java:158) 757 | at android.app.ActivityThread.main(ActivityThread.java:7225) 758 | at java.lang.reflect.Method.invoke(Native Method) 759 | at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) 760 | at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 761 | 762 | ``` 763 | 以上异常出现的几率为random 764 | 初步分析原因是:进入页面然后又迅速退出,导致AsyncTask中的onPostExecute在调用Glide加载图片时出现如上异常,当然也跟对AsyncTask的管理有关 765 | 同样的问题参考[issues138](https://github.com/bumptech/glide/issues/138) 766 | 简单的摘要: 767 | 768 | > you fire an async task and then finish() in which case you just need to pass getApplicationContext instead of this when creating the callback/asynctask 769 | 770 | 按照上面这个理解的话,如果是在AsyncTask中的onPostExecute执行时 771 | 调用Glide加载图片,context最好使用ApplicationContext 772 | 773 | 对[ImageLoaderUtil](https://github.com/soulrelay/ImageLoaderUtil)做如下更新,添加方法 774 | >* BaseImageLoaderStrategy 775 | ``` 776 | //这里的context指定为ApplicationContext 777 | void loadImageWithAppCxt(String url, ImageView imageView); 778 | ``` 779 | 780 | >* GlideImageLoaderStrategy 781 | ``` 782 | @Override 783 | public void loadImageWithAppCxt(String url, ImageView imageView) { 784 | Glide.with(imageView.getContext().getApplicationContext()).load(url).dontAnimate() 785 | .placeholder(imageView.getDrawable()) 786 | .diskCacheStrategy(DiskCacheStrategy.SOURCE) 787 | .into(imageView); 788 | } 789 | ``` 790 | >* ImageLoaderUtil 791 | ``` 792 | public void loadImageWithAppCxt(String url, ImageView imageView) { 793 | mStrategy.loadImageWithAppCxt(url,imageView); 794 | } 795 | ``` 796 | 797 | ### 2017-1-10 ll 简单说说图片适配的问题 798 | 过多的概念不赘述,可以先参考[Android屏幕适配全攻略(最权威的官方适配指导)](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023) 799 | 这里主要描述一种现象,明白的话自然觉得很简单! 800 | 801 | ``` 802 | 808 | ``` 809 | 假设现在要加载一张200px * 200px的GIF图片(图片基于1280 * 720),这张图片的宽高设置为wrap_content,如果在1920 * 1080分辨率的手机上显示,相对于1280 * 720(假设屏幕尺寸相同),在视觉效果上会显得小,这其实是Android系统基于手机像素密度的一种自适配,单一变化条件下,1920 * 1080分辨率的手机的像素密度是1280 * 720的1.5倍 810 | 假设如果系统的自适配让你觉得在高分辨率手机上显得图片过小(像素密度高,200个像素显示起来就比较挤),可以通过自己的计算来改变这种现象 811 | ImageLoaderUtil提供如下加载成功回调的方法(并且会把图片的宽高告诉你):这里有个参数设置,看需求来计算,粗略点可以只使用宽度比例来算,如下面的例子显示,参数为AppParams.screenWidth / 720,当然也可以获取屏幕密度,1920 * 1080的屏幕密度为3,1280 * 720的为2,所以参数可以设置为AppParams.density/2(在两种分辨率上看着视觉上一样) 812 | 813 | ``` 814 | ImageLoaderUtil.getInstance().loadGifWithPrepareCall(url, mImageView, new SourceReadyListener() { 815 | @Override 816 | public void onResourceReady(int width, int height) { 817 | ViewGroup.LayoutParams params = mImageView.getLayoutParams(); 818 | params.height = height * AppParams.screenWidth / 720; 819 | params.width = width * AppParams.screenWidth / 720; 820 | mImageView.setLayoutParams(params); 821 | progressBar.setVisibility(View.GONE); 822 | } 823 | }); 824 | ``` 825 | 826 | ### 2017-1-10 ll 添加saveImage方法,实现图片的本地自定义保存功能 827 | 已同步到[GitHub ImageLoaderUtil](https://github.com/soulrelay/ImageLoaderUtil) 828 | >* ImageLoaderUtil相关接口: 829 | 830 | ``` 831 | /** 832 | * @param context 833 | * @param url 图片url 834 | * @param savePath 保存路径 835 | * @param saveFileName 保存文件名 836 | * @param listener 文件保存成功与否的监听器 837 | */ 838 | public void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener) { 839 | mStrategy.saveImage(context, url, savePath, saveFileName, listener); 840 | } 841 | ``` 842 | 843 | >* 在工作线程中调用示例如下: 844 | 845 | ``` 846 | ImageLoaderUtil.getInstance().saveImage(getActivity(), url, 847 | Environment.getExternalStorageDirectory().getAbsolutePath() + "/bfsports", 848 | "bfsports" + System.currentTimeMillis(), new ImageSaveListener() { 849 | @Override 850 | public void onSaveSuccess() { 851 | handler.obtainMessage(MSG_PIC_SAVE_SUCC).sendToTarget(); 852 | } 853 | 854 | @Override 855 | public void onSaveFail() { 856 | handler.obtainMessage(MSG_PIC_SAVE_FAIL).sendToTarget(); 857 | } 858 | }); 859 | ``` 860 | 861 | ### 2017-03-08 ll Glide圆形图片加载封装 862 | >* 之前加载圆形图片,一般都是采用自定义的CircleImageview。后来开始使用Glide加载图片,期间遇到Glide和CircleImageview使用冲突的问题(如:有的图片第一次加载的时候只显示占位图,第二次才显示正常的图片,以及CircleImageview带来的崩溃问题),当时采用了一个牺牲动画效果的解决方案 863 | >* 既然已经全面使用Glide,那么就尽量基于Glide来完成加载圆形图片的方案 864 | >* 具体方案请参考[Glide圆形图片加载封装](http://blog.csdn.net/s003603u/article/details/60880308) 865 | ### 2017-05-09 ll 圆形图片加载优化,更新简单使用案例 -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.3' 9 | 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' //用于打包Maven所需文件 11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' //用于上传Maven生成的文件到Bintray 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | maven { 22 | name 'glide-snapshot' 23 | url 'http://oss.sonatype.org/content/repositories/snapshots' 24 | } 25 | } 26 | 27 | tasks.withType(Javadoc) { 28 | options.addStringOption('Xdoclint:none', '-quiet') 29 | options.addStringOption('encoding', 'UTF-8') 30 | } 31 | } 32 | 33 | task clean(type: Delete) { 34 | delete rootProject.buildDir 35 | } 36 | 37 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue May 09 11:11:20 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /imagelib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /imagelib/bintray.properties: -------------------------------------------------------------------------------- 1 | #配置bintray账号相关信息 2 | #bintray用户名,不是登陆邮箱,是个人中心右上角显示的名字 3 | bintray.user=su2008shuai 4 | #bintray的ApiKey 5 | bintray.apiKey=xxxxx 6 | 7 | #bintray的Organization Id 8 | #bintray.organizationId=soulrelay 9 | 10 | 11 | #配置开发者信息 12 | #昵称 13 | developer.id=sushuai 14 | #姓名 15 | developer.name=sushuai 16 | #邮箱 17 | developer.email=su2008shuai@gmail.com -------------------------------------------------------------------------------- /imagelib/bintrayUpload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | apply plugin: 'com.jfrog.bintray' 3 | 4 | //加载属性文件 5 | Properties properties = new Properties() 6 | File localPropertiesFile = project.file("bintray.properties"); 7 | if (localPropertiesFile.exists()) { 8 | properties.load(localPropertiesFile.newDataInputStream()) 9 | } 10 | File projectPropertiesFile = project.file("project.properties"); 11 | if (projectPropertiesFile.exists()) { 12 | properties.load(projectPropertiesFile.newDataInputStream()) 13 | } 14 | 15 | //读取属性 16 | def projectRepositoryName = properties.getProperty("project.repositoryName") 17 | def projectName = properties.getProperty("project.name") 18 | def projectGroupId = properties.getProperty("project.groupId") 19 | def projectArtifactId = properties.getProperty("project.artifactId") 20 | def projectVersionName = android.defaultConfig.versionName 21 | def projectPackaging = properties.getProperty("project.packaging") 22 | def projectSiteUrl = properties.getProperty("project.siteUrl") 23 | def projectGitUrl = properties.getProperty("project.gitUrl") 24 | def projectVersionDesc = properties.getProperty("project.versiondesc") 25 | def projectVersionVcsTag = properties.getProperty("project.versionvcstag") 26 | 27 | def developerId = properties.getProperty("developer.id") 28 | def developerName = properties.getProperty("developer.name") 29 | def developerEmail = properties.getProperty("developer.email") 30 | 31 | def bintrayUser = properties.getProperty("bintray.user") 32 | def bintrayApikey = properties.getProperty("bintray.apiKey") 33 | def bintrayOrganizationId = properties.getProperty("bintray.organizationId"); 34 | 35 | def javadocName = properties.getProperty("javadoc.name") 36 | 37 | /* 38 | *这句代码一定要加否则会出现如下错误 39 | * Could not upload to 'https://api.bintray.com/content/coolcode/maven/LibUiBase/1.0.0/CommonLibrary/LibUiBase/1.0.0/LibUiBase-1.0.0.pom': HTTP/1.1 400 Bad Request [ 40 | message:Unable to upload files: Maven group, artifact or version defined in the pom file do not match the file path 'CommonLibrary/LibUiBase/1.0.0/LibUiBase-1.0.0.p 41 | om'] 42 | * */ 43 | group = projectGroupId 44 | 45 | // 配置生成POM.xml文件的参数 46 | install { 47 | repositories.mavenInstaller { 48 | pom { 49 | project { 50 | name projectName 51 | groupId projectGroupId 52 | artifactId projectArtifactId 53 | version projectVersionName 54 | packaging projectPackaging 55 | url projectSiteUrl 56 | licenses { 57 | license { 58 | name 'The Apache Software License, Version 2.0' 59 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 60 | } 61 | } 62 | developers { 63 | developer { 64 | id developerId 65 | name developerName 66 | email developerEmail 67 | } 68 | } 69 | scm { 70 | connection projectGitUrl 71 | developerConnection projectGitUrl 72 | url projectSiteUrl 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | //生成sources.jar 80 | task sourcesJar(type: Jar) { 81 | from android.sourceSets.main.java.srcDirs 82 | classifier = 'sources' 83 | } 84 | 85 | task javadoc(type: Javadoc) { 86 | source = android.sourceSets.main.java.srcDirs 87 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 88 | } 89 | 90 | //生成javadoc.jar 91 | task javadocJar(type: Jar, dependsOn: javadoc) { 92 | classifier = 'javadoc' 93 | from javadoc.destinationDir 94 | } 95 | 96 | artifacts { 97 | archives javadocJar 98 | archives sourcesJar 99 | } 100 | 101 | //javadoc的配置 102 | javadoc { 103 | options { 104 | encoding "UTF-8" 105 | charSet 'UTF-8' 106 | author true 107 | version projectVersionName 108 | links "http://docs.oracle.com/javase/7/docs/api" 109 | title javadocName 110 | } 111 | } 112 | 113 | /* 114 | *userOrg为bintray账号信息里面的Organization Id 115 | *repo为你创建的仓库名称 116 | * 如果上述两个字段写错了,则会出现下面类似的错误 117 | *Could not create package 'huangxuanheng/maven/fragmentstack': HTTP/1.1 404 Not Found [message:Repo 'maven' was not found] 118 | * 119 | * 120 | * */ 121 | bintray { 122 | user = bintrayUser 123 | key = bintrayApikey 124 | configurations = ['archives'] 125 | pkg { 126 | //userOrg = bintrayOrganizationId 127 | repo = projectRepositoryName 128 | name = projectName 129 | websiteUrl = projectSiteUrl 130 | vcsUrl = projectGitUrl 131 | licenses = ["Apache-2.0"] 132 | publish = true 133 | version { 134 | name = projectVersionName 135 | desc = projectVersionDesc 136 | vcsTag = projectVersionVcsTag 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /imagelib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | 4 | android { 5 | compileSdkVersion 24 6 | buildToolsVersion '25.0.0' 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0.1" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:24.2.1' 28 | testCompile 'junit:junit:4.12' 29 | compile 'com.android.support:support-v4:24.2.1' 30 | compile 'com.squareup.okhttp3:okhttp:3.4.2' 31 | compile 'com.github.bumptech.glide:glide:3.8.0-SNAPSHOT' 32 | compile 'com.github.bumptech.glide:okhttp-integration:1.5.0-SNAPSHOT' 33 | } 34 | 35 | apply from: "bintrayUpload.gradle" 36 | -------------------------------------------------------------------------------- /imagelib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # 指定代码的压缩级别 2 | -optimizationpasses 5 3 | -dontusemixedcaseclassnames 4 | # 是否混淆第三方jar 5 | -dontskipnonpubliclibraryclasses 6 | -dontpreverify 7 | -verbose 8 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 9 | 10 | # 保持哪些类不被混淆 11 | -keep class android.** {*; } 12 | -keep public class * extends android.view 13 | -keep public class * extends android.app.Activity 14 | -keep public class * extends android.app.Application 15 | -keep public class * extends android.app.Service 16 | -keep public class * extends android.content.pm 17 | -keep public class * extends android.content.BroadcastReceiver 18 | -keep public class * extends android.content.ContentProvider 19 | -keep public class * extends android.app.backup.BackupAgentHelper 20 | -keep public class * extends android.preference.Preference 21 | 22 | -keepclassmembers class *{ 23 | public void onEvent*(***); 24 | } 25 | 26 | #ACRA specifics 27 | # we need line numbers in our stack traces otherwise they are pretty useless 28 | -renamesourcefileattribute SourceFile 29 | 30 | -keepclassmembers public class * extends android.view.View { 31 | void set*(***); 32 | *** get*(); 33 | } 34 | 35 | 36 | -keepclasseswithmembers class * { 37 | void onClick*(...); 38 | } 39 | -keepclasseswithmembers class * { 40 | *** *Callback(...); 41 | } 42 | 43 | # keep setters in Views so that animations can still work. 44 | # see http://proguard.sourceforge.net/manual/examples.html#beans 45 | -keepclassmembers public class * extends android.view.View { 46 | void set*(***); 47 | *** get*(); 48 | } 49 | 50 | # 保持自定义控件类不被混淆 51 | -keepclassmembers class * extends android.app.Activity { 52 | public void *(android.view.View); 53 | } 54 | 55 | 56 | -keepclassmembers class **.R$* { 57 | public static ; 58 | } 59 | 60 | #--------------------Glide-----------------------# 61 | -keep public class * implements com.bumptech.glide.module.GlideModule 62 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 63 | **[] $VALUES; 64 | public *; 65 | } 66 | 67 | -keepnames class com.baofeng.soulrelay.utils.imageloader.MyGlideModule 68 | # or more generally: 69 | -keep public class * implements com.bumptech.glide.module.GlideModule 70 | -keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule 71 | 72 | #--------------------Glide-----------------------# 73 | 74 | -------------------------------------------------------------------------------- /imagelib/project.properties: -------------------------------------------------------------------------------- 1 | #project 2 | #仓库名称,就是在bintray官网建立的仓库的名称 3 | project.repositoryName=me 4 | #项目名称 5 | project.name=imagelib 6 | #项目组id 7 | project.groupId=com.sus.library 8 | #项目id,一般同project.name 9 | project.artifactId=imagelib 10 | #打包类型 11 | project.packaging=aar 12 | #项目官方网站地址 13 | project.siteUrl=https://github.com/soulrelay/ImageLoaderUtil 14 | #项目git地址 15 | project.gitUrl=https://github.com/soulrelay/ImageLoaderUtil 16 | #生成的javadoc名称 17 | javadoc.name=imagelib 18 | 19 | project.versiondesc = 1.0.1 normal 20 | project.versionvcstag = 1.0.1 tag 21 | -------------------------------------------------------------------------------- /imagelib/src/androidTest/java/com/baofeng/soulrelay/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.soulrelay.imageloaderutil", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /imagelib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/CommonUtils.java: -------------------------------------------------------------------------------- 1 | 2 | package com.baofeng.soulrelay.utils; 3 | 4 | import android.content.Context; 5 | import android.graphics.BitmapFactory; 6 | import android.os.Environment; 7 | import android.text.TextUtils; 8 | 9 | import java.io.File; 10 | import java.math.BigDecimal; 11 | 12 | public class CommonUtils { 13 | /** 14 | * 获取指定文件夹内所有文件大小的和 15 | * 16 | * @param file file 17 | * @return size 18 | * @throws Exception 19 | */ 20 | public static long getFolderSize(File file) throws Exception { 21 | long size = 0; 22 | try { 23 | File[] fileList = file.listFiles(); 24 | for (File aFileList : fileList) { 25 | if (aFileList.isDirectory()) { 26 | size = size + getFolderSize(aFileList); 27 | } else { 28 | size = size + aFileList.length(); 29 | } 30 | } 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | } 34 | return size; 35 | } 36 | 37 | /** 38 | * 格式化单位 39 | * 40 | * @param size size 41 | * @return size 42 | */ 43 | public static String getFormatSize(double size) { 44 | 45 | double kiloByte = size / 1024; 46 | if (kiloByte < 1) { 47 | return size + "Byte"; 48 | } 49 | 50 | double megaByte = kiloByte / 1024; 51 | if (megaByte < 1) { 52 | BigDecimal result1 = new BigDecimal(Double.toString(kiloByte)); 53 | return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB"; 54 | } 55 | 56 | double gigaByte = megaByte / 1024; 57 | if (gigaByte < 1) { 58 | BigDecimal result2 = new BigDecimal(Double.toString(megaByte)); 59 | return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB"; 60 | } 61 | 62 | double teraBytes = gigaByte / 1024; 63 | if (teraBytes < 1) { 64 | BigDecimal result3 = new BigDecimal(Double.toString(gigaByte)); 65 | return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB"; 66 | } 67 | BigDecimal result4 = new BigDecimal(teraBytes); 68 | 69 | return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB"; 70 | } 71 | 72 | public static String getPicType(String pathName) { 73 | BitmapFactory.Options options = new BitmapFactory.Options(); 74 | options.inJustDecodeBounds = true; 75 | BitmapFactory.decodeFile(pathName, options); 76 | String type = options.outMimeType; 77 | if (!TextUtils.isEmpty(type)) { 78 | try { 79 | type = type.substring(6, type.length()); 80 | if ("gif".equals(type)) { 81 | return ".gif"; 82 | } 83 | } catch (IndexOutOfBoundsException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | return ".jpg"; 88 | } 89 | 90 | public static boolean isSDCardExsit() { 91 | String state = Environment.getExternalStorageState(); 92 | if (Environment.MEDIA_MOUNTED.equals(state)) { 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | /** 99 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 100 | */ 101 | public static int dip2px(Context context, float dpValue) { 102 | if (context == null) { 103 | return 0; 104 | } 105 | final float scale = context.getResources().getDisplayMetrics().density; 106 | return (int) (dpValue * scale + 0.5f); 107 | } 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/BaseImageLoaderStrategy.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.baofeng.soulrelay.utils.imageloader.glideprogress.ProgressLoadListener; 7 | import com.baofeng.soulrelay.utils.imageloader.listener.ImageSaveListener; 8 | import com.baofeng.soulrelay.utils.imageloader.listener.SourceReadyListener; 9 | 10 | 11 | /** 12 | * Created by soulrelay on 2016/10/11. 13 | * Class Note: 14 | * abstract class/interface defined to load image 15 | * (Strategy Pattern used here) 16 | */ 17 | public interface BaseImageLoaderStrategy { 18 | //无占位图 19 | void loadImage(String url, ImageView imageView); 20 | 21 | //这里的context指定为ApplicationContext 22 | void loadImageWithAppCxt(String url, ImageView imageView); 23 | 24 | void loadImage(String url, int placeholder, ImageView imageView); 25 | 26 | void loadImage(Context context, String url, int placeholder, ImageView imageView); 27 | 28 | void loadCircleImage(String url, int placeholder, ImageView imageView); 29 | 30 | void loadCircleBorderImage(String url, int placeholder, ImageView imageView,float borderWidth, int borderColor); 31 | 32 | void loadCircleBorderImage(String url, int placeholder, ImageView imageView,float borderWidth, int borderColor, int heightPx, int widthPx); 33 | 34 | void loadGifImage(String url, int placeholder, ImageView imageView); 35 | 36 | void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener); 37 | 38 | void loadImageWithPrepareCall(String url, ImageView imageView, int placeholder, SourceReadyListener listener); 39 | 40 | void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener); 41 | 42 | //清除硬盘缓存 43 | void clearImageDiskCache(final Context context); 44 | 45 | //清除内存缓存 46 | void clearImageMemoryCache(Context context); 47 | 48 | //根据不同的内存状态,来响应不同的内存释放策略 49 | void trimMemory(Context context, int level); 50 | 51 | //获取缓存大小 52 | String getCacheSize(Context context); 53 | 54 | void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/GlideImageLoaderStrategy.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Looper; 7 | import android.text.TextUtils; 8 | import android.widget.ImageView; 9 | 10 | import com.baofeng.soulrelay.utils.CommonUtils; 11 | import com.baofeng.soulrelay.utils.imageloader.glideprogress.ProgressLoadListener; 12 | import com.baofeng.soulrelay.utils.imageloader.glideprogress.ProgressModelLoader; 13 | import com.baofeng.soulrelay.utils.imageloader.glideprogress.ProgressUIListener; 14 | import com.baofeng.soulrelay.utils.imageloader.listener.ImageSaveListener; 15 | import com.baofeng.soulrelay.utils.imageloader.listener.SourceReadyListener; 16 | import com.baofeng.soulrelay.utils.imageloader.transformation.GlideCircleTransform; 17 | import com.bumptech.glide.Glide; 18 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 19 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 20 | import com.bumptech.glide.load.resource.gif.GifDrawable; 21 | import com.bumptech.glide.request.RequestListener; 22 | import com.bumptech.glide.request.target.Target; 23 | 24 | import java.io.File; 25 | import java.io.FileInputStream; 26 | import java.io.FileOutputStream; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.OutputStream; 30 | 31 | 32 | /** 33 | * Created by soulrelay on 2016/10/11 13:48. 34 | * Class Note: 35 | * using {@link Glide} to load image 36 | */ 37 | public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy { 38 | 39 | @Override 40 | public void loadImage(String url, int placeholder, ImageView imageView) { 41 | loadNormal(imageView.getContext(), url, placeholder, imageView); 42 | } 43 | 44 | @Override 45 | public void loadImage(Context context, String url, int placeholder, ImageView imageView) { 46 | loadNormal(context, url, placeholder, imageView); 47 | } 48 | 49 | /** 50 | * 无holder的gif加载 51 | * 52 | * @param url 53 | * @param imageView 54 | */ 55 | @Override 56 | public void loadImage(String url, ImageView imageView) { 57 | Glide.with(imageView.getContext()).load(url) 58 | .placeholder(imageView.getDrawable()) 59 | .diskCacheStrategy(DiskCacheStrategy.SOURCE) 60 | .into(imageView); 61 | } 62 | 63 | @Override 64 | public void loadCircleImage(String url, int placeholder, ImageView imageView) { 65 | Glide.with(imageView.getContext()).load(url).placeholder(placeholder).dontAnimate() 66 | .transform(new GlideCircleTransform(imageView.getContext())) 67 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView); 68 | } 69 | 70 | @Override 71 | public void loadCircleBorderImage(String url, int placeholder, ImageView imageView, float borderWidth, int borderColor) { 72 | Glide.with(imageView.getContext()).load(url).placeholder(placeholder).dontAnimate() 73 | .transform(new GlideCircleTransform(imageView.getContext(),borderWidth,borderColor)) 74 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView); 75 | } 76 | 77 | @Override 78 | public void loadCircleBorderImage(String url, int placeholder, ImageView imageView, float borderWidth, int borderColor, int heightPx, int widthPx) { 79 | Glide.with(imageView.getContext()).load(url).placeholder(placeholder).dontAnimate() 80 | .transform(new GlideCircleTransform(imageView.getContext(),borderWidth,borderColor,heightPx,widthPx)) 81 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView); 82 | } 83 | 84 | 85 | @Override 86 | public void loadImageWithAppCxt(String url, ImageView imageView) { 87 | Glide.with(imageView.getContext().getApplicationContext()).load(url) 88 | .placeholder(imageView.getDrawable()) 89 | .diskCacheStrategy(DiskCacheStrategy.SOURCE) 90 | .into(imageView); 91 | } 92 | 93 | @Override 94 | public void loadGifImage(String url, int placeholder, ImageView imageView) { 95 | loadGif(imageView.getContext(), url, placeholder, imageView); 96 | } 97 | 98 | @Override 99 | public void loadImageWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) { 100 | Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() { 101 | @Override 102 | public void update(final int bytesRead, final int contentLength) { 103 | imageView.post(new Runnable() { 104 | @Override 105 | public void run() { 106 | listener.update(bytesRead, contentLength); 107 | } 108 | }); 109 | } 110 | })).load(url).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE). 111 | listener(new RequestListener() { 112 | @Override 113 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 114 | listener.onException(); 115 | return false; 116 | } 117 | 118 | @Override 119 | public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 120 | listener.onResourceReady(); 121 | return false; 122 | } 123 | }).into(imageView); 124 | } 125 | 126 | 127 | @Override 128 | public void loadGifWithPrepareCall(String url, ImageView imageView, final SourceReadyListener listener) { 129 | Glide.with(imageView.getContext()).load(url).asGif() 130 | .skipMemoryCache(true) 131 | .diskCacheStrategy(DiskCacheStrategy.SOURCE). 132 | listener(new RequestListener() { 133 | @Override 134 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 135 | return false; 136 | } 137 | 138 | @Override 139 | public boolean onResourceReady(GifDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 140 | listener.onResourceReady(resource.getIntrinsicWidth(), resource.getIntrinsicHeight()); 141 | return false; 142 | } 143 | }).into(imageView); 144 | } 145 | 146 | @Override 147 | public void loadImageWithPrepareCall(String url, ImageView imageView, int placeholder, final SourceReadyListener listener) { 148 | Glide.with(imageView.getContext()).load(url) 149 | .skipMemoryCache(true) 150 | .diskCacheStrategy(DiskCacheStrategy.SOURCE) 151 | .placeholder(placeholder) 152 | .listener(new RequestListener() { 153 | @Override 154 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 155 | return false; 156 | } 157 | 158 | @Override 159 | public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 160 | listener.onResourceReady(resource.getIntrinsicWidth(), resource.getIntrinsicHeight()); 161 | return false; 162 | } 163 | }).into(imageView); 164 | } 165 | 166 | @Override 167 | public void clearImageDiskCache(final Context context) { 168 | try { 169 | if (Looper.myLooper() == Looper.getMainLooper()) { 170 | new Thread(new Runnable() { 171 | @Override 172 | public void run() { 173 | Glide.get(context.getApplicationContext()).clearDiskCache(); 174 | } 175 | }).start(); 176 | } else { 177 | Glide.get(context.getApplicationContext()).clearDiskCache(); 178 | } 179 | } catch (Exception e) { 180 | e.printStackTrace(); 181 | } 182 | } 183 | 184 | @Override 185 | public void clearImageMemoryCache(Context context) { 186 | try { 187 | if (Looper.myLooper() == Looper.getMainLooper()) { //只能在主线程执行 188 | Glide.get(context.getApplicationContext()).clearMemory(); 189 | } 190 | } catch (Exception e) { 191 | e.printStackTrace(); 192 | } 193 | } 194 | 195 | @Override 196 | public void trimMemory(Context context, int level) { 197 | Glide.get(context).trimMemory(level); 198 | } 199 | 200 | @Override 201 | public String getCacheSize(Context context) { 202 | try { 203 | return CommonUtils.getFormatSize(CommonUtils.getFolderSize(Glide.getPhotoCacheDir(context.getApplicationContext()))); 204 | } catch (Exception e) { 205 | e.printStackTrace(); 206 | } 207 | return ""; 208 | } 209 | 210 | @Override 211 | public void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener) { 212 | if (!CommonUtils.isSDCardExsit() || TextUtils.isEmpty(url)) { 213 | listener.onSaveFail(); 214 | return; 215 | } 216 | InputStream fromStream = null; 217 | OutputStream toStream = null; 218 | try { 219 | File cacheFile = Glide.with(context).load(url).downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).get(); 220 | if (cacheFile == null || !cacheFile.exists()) { 221 | listener.onSaveFail(); 222 | return; 223 | } 224 | File dir = new File(savePath); 225 | if (!dir.exists()) { 226 | dir.mkdir(); 227 | } 228 | File file = new File(dir, saveFileName + CommonUtils.getPicType(cacheFile.getAbsolutePath())); 229 | 230 | fromStream = new FileInputStream(cacheFile); 231 | toStream = new FileOutputStream(file); 232 | byte length[] = new byte[1024]; 233 | int count; 234 | while ((count = fromStream.read(length)) > 0) { 235 | toStream.write(length, 0, count); 236 | } 237 | //用广播通知相册进行更新相册 238 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 239 | Uri uri = Uri.fromFile(file); 240 | intent.setData(uri); 241 | context.sendBroadcast(intent); 242 | listener.onSaveSuccess(); 243 | } catch (Exception e) { 244 | e.printStackTrace(); 245 | listener.onSaveFail(); 246 | } finally { 247 | if (fromStream != null) { 248 | try { 249 | fromStream.close(); 250 | toStream.close(); 251 | } catch (IOException e) { 252 | e.printStackTrace(); 253 | fromStream = null; 254 | toStream = null; 255 | } 256 | } 257 | } 258 | 259 | } 260 | 261 | /** 262 | * load image with Glide 263 | */ 264 | private void loadNormal(final Context ctx, final String url, int placeholder, ImageView imageView) { 265 | /** 266 | * 为其添加缓存策略,其中缓存策略可以为:Source及None,None及为不缓存,Source缓存原型.如果为ALL和Result就不行.然后几个issue的连接: 267 | https://github.com/bumptech/glide/issues/513 268 | https://github.com/bumptech/glide/issues/281 269 | https://github.com/bumptech/glide/issues/600 270 | modified by xuqiang 271 | */ 272 | 273 | //去掉动画 解决与CircleImageView冲突的问题 这个只是其中的一个解决方案 274 | //使用SOURCE 图片load结束再显示而不是先显示缩略图再显示最终的图片(导致图片大小不一致变化) 275 | final long startTime = System.currentTimeMillis(); 276 | Glide.with(ctx).load(url) 277 | .placeholder(placeholder) 278 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener() { 279 | @Override 280 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 281 | return false; 282 | } 283 | 284 | @Override 285 | public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 286 | return false; 287 | } 288 | }) 289 | .into(imageView); 290 | } 291 | 292 | /** 293 | * load image with Glide 294 | */ 295 | private void loadGif(final Context ctx, String url, int placeholder, ImageView imageView) { 296 | final long startTime = System.currentTimeMillis(); 297 | Glide.with(ctx).load(url).asGif() 298 | .placeholder(placeholder).skipMemoryCache(true) 299 | .diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener() { 300 | @Override 301 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 302 | return false; 303 | } 304 | 305 | @Override 306 | public boolean onResourceReady(GifDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 307 | return false; 308 | } 309 | }) 310 | .into(imageView); 311 | } 312 | 313 | } 314 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/ImageLoaderConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader; 2 | 3 | import android.widget.ImageView; 4 | 5 | import com.baofeng.soulrelay.R; 6 | 7 | 8 | /** 9 | * Created by soulrelay on 2016/10/11 13:44. 10 | * Class Note: 11 | * encapsulation of ImageView,Build Pattern used 12 | */ 13 | public class ImageLoaderConfiguration { 14 | private int type; //图片加载类型,目前只有默认类型,以后可以扩展 15 | 16 | private String url; //需要解析的url 17 | 18 | private int placeHolder; //当没有成功加载的时候显示的图片 19 | 20 | private ImageView imgView; //ImageView的实例 21 | 22 | private int loadStrategy;//加载策略,目前只有默认加载策略,以后可以扩展 23 | 24 | private ImageLoaderConfiguration(Builder builder) { 25 | this.type = builder.type; 26 | this.url = builder.url; 27 | this.placeHolder = builder.placeHolder; 28 | this.imgView = builder.imgView; 29 | this.loadStrategy = builder.loadStrategy; 30 | } 31 | 32 | public int getType() { 33 | return type; 34 | } 35 | 36 | public String getUrl() { 37 | return url; 38 | } 39 | 40 | public int getPlaceHolder() { 41 | return placeHolder; 42 | } 43 | 44 | public ImageView getImgView() { 45 | return imgView; 46 | } 47 | 48 | public int getLoadStrategy() { 49 | return loadStrategy; 50 | } 51 | 52 | public static class Builder { 53 | private int type; 54 | private String url; 55 | private int placeHolder; 56 | private ImageView imgView; 57 | private int loadStrategy; 58 | 59 | public Builder() { 60 | this.type = ImageLoaderUtil.PIC_DEFAULT_TYPE; 61 | this.url = ""; 62 | this.placeHolder = R.drawable.bg_default_video_common_small; 63 | this.imgView = null; 64 | this.loadStrategy = ImageLoaderUtil.LOAD_STRATEGY_DEFAULT; 65 | } 66 | 67 | public Builder type(int type) { 68 | this.type = type; 69 | return this; 70 | } 71 | 72 | public Builder url(String url) { 73 | this.url = url; 74 | return this; 75 | } 76 | 77 | public Builder placeHolder(int placeHolder) { 78 | this.placeHolder = placeHolder; 79 | return this; 80 | } 81 | 82 | public Builder imgView(ImageView imgView) { 83 | this.imgView = imgView; 84 | return this; 85 | } 86 | 87 | public Builder strategy(int strategy) { 88 | this.loadStrategy = strategy; 89 | return this; 90 | } 91 | 92 | public ImageLoaderConfiguration build() { 93 | return new ImageLoaderConfiguration(this); 94 | } 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/ImageLoaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.baofeng.soulrelay.utils.imageloader.glideprogress.ProgressLoadListener; 7 | import com.baofeng.soulrelay.utils.imageloader.listener.ImageSaveListener; 8 | import com.baofeng.soulrelay.utils.imageloader.listener.SourceReadyListener; 9 | 10 | 11 | /** 12 | * Created by soulrelay on 2016/10/11 13:42. 13 | * Class Note: 14 | * use this class to load image,single instance 15 | */ 16 | public class ImageLoaderUtil { 17 | 18 | //图片默认加载类型 以后有可能有多种类型 19 | public static final int PIC_DEFAULT_TYPE = 0; 20 | 21 | //图片默认加载策略 以后有可能有多种图片加载策略 22 | public static final int LOAD_STRATEGY_DEFAULT = 0; 23 | 24 | private static ImageLoaderUtil mInstance; 25 | //本应该使用策略模式,用基类声明,但是因为Glide特殊问题 26 | //持续优化更新 27 | private BaseImageLoaderStrategy mStrategy; 28 | 29 | public ImageLoaderUtil() { 30 | mStrategy = new GlideImageLoaderStrategy(); 31 | } 32 | 33 | //单例模式,节省资源 34 | public static ImageLoaderUtil getInstance() { 35 | if (mInstance == null) { 36 | synchronized (ImageLoaderUtil.class) { 37 | if (mInstance == null) { 38 | mInstance = new ImageLoaderUtil(); 39 | return mInstance; 40 | } 41 | } 42 | } 43 | return mInstance; 44 | } 45 | 46 | /** 47 | * 统一使用App context 48 | * 可能带来的问题:http://stackoverflow.com/questions/31964737/glide-image-loading-with-application-context 49 | * 50 | * @param url 51 | * @param placeholder 52 | * @param imageView 53 | */ 54 | public void loadImage(String url, int placeholder, ImageView imageView) { 55 | mStrategy.loadImage(imageView.getContext(), url, placeholder, imageView); 56 | } 57 | 58 | public void loadGifImage(String url, int placeholder, ImageView imageView) { 59 | mStrategy.loadGifImage(url, placeholder, imageView); 60 | } 61 | 62 | public void loadCircleImage(String url, int placeholder, ImageView imageView) { 63 | mStrategy.loadCircleImage(url,placeholder,imageView); 64 | } 65 | 66 | public void loadCircleBorderImage(String url, int placeholder, ImageView imageView, float borderWidth, int borderColor) { 67 | mStrategy.loadCircleBorderImage(url, placeholder, imageView, borderWidth, borderColor); 68 | } 69 | 70 | public void loadCircleBorderImage(String url, int placeholder, ImageView imageView, float borderWidth, int borderColor, int heightPX,int widthPX) { 71 | mStrategy.loadCircleBorderImage(url, placeholder, imageView, borderWidth, borderColor, heightPX,widthPX); 72 | } 73 | 74 | public void loadImage(String url, ImageView imageView) { 75 | mStrategy.loadImage(url, imageView); 76 | } 77 | 78 | public void loadImageWithAppCxt(String url, ImageView imageView) { 79 | mStrategy.loadImageWithAppCxt(url,imageView); 80 | } 81 | 82 | public void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener) { 83 | mStrategy.loadImageWithProgress(url,imageView,listener); 84 | } 85 | 86 | public void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener) { 87 | mStrategy.loadGifWithPrepareCall(url,imageView,listener); 88 | } 89 | 90 | public void loadImageWithPrepareCall(String url, ImageView imageView,int placeholder, SourceReadyListener listener) { 91 | mStrategy.loadImageWithPrepareCall(url, imageView, placeholder, listener); 92 | } 93 | 94 | /** 95 | * 策略模式的注入操作 96 | * 97 | * @param strategy 98 | */ 99 | public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) { 100 | mStrategy = strategy; 101 | } 102 | 103 | /** 104 | * 需要展示图片加载进度的请参考 GalleryAdapter 105 | * 样例如下所示 106 | */ 107 | 108 | /** 109 | * 清除图片磁盘缓存 110 | */ 111 | public void clearImageDiskCache(final Context context) { 112 | mStrategy.clearImageDiskCache(context); 113 | } 114 | 115 | /** 116 | * 清除图片内存缓存 117 | */ 118 | public void clearImageMemoryCache(Context context) { 119 | mStrategy.clearImageMemoryCache(context); 120 | } 121 | 122 | /** 123 | * 根据不同的内存状态,来响应不同的内存释放策略 124 | * 125 | * @param context 126 | * @param level 127 | */ 128 | public void trimMemory(Context context, int level) { 129 | mStrategy.trimMemory(context, level); 130 | } 131 | 132 | /** 133 | * 清除图片所有缓存 134 | */ 135 | public void clearImageAllCache(Context context) { 136 | clearImageDiskCache(context.getApplicationContext()); 137 | clearImageMemoryCache(context.getApplicationContext()); 138 | } 139 | 140 | /** 141 | * 获取缓存大小 142 | * 143 | * @return CacheSize 144 | */ 145 | public String getCacheSize(Context context) { 146 | return mStrategy.getCacheSize(context); 147 | } 148 | 149 | public void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener) { 150 | mStrategy.saveImage(context, url, savePath, saveFileName, listener); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/MyGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader; 2 | 3 | import android.content.Context; 4 | 5 | import com.bumptech.glide.Glide; 6 | import com.bumptech.glide.GlideBuilder; 7 | import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool; 8 | import com.bumptech.glide.load.engine.cache.LruResourceCache; 9 | import com.bumptech.glide.module.GlideModule; 10 | 11 | /** 12 | * DES:自定义一个GlideModule 13 | *

14 | * GlideModule 是一个抽象方法,全局改变 Glide 行为的一个方式, 15 | * 通过全局GlideModule 配置Glide,用GlideBuilder设置选项,用Glide注册ModelLoader等。 16 | *

17 | */ 18 | public class MyGlideModule implements GlideModule { 19 | @Override 20 | public void applyOptions(Context context, GlideBuilder builder) { 21 | // Apply options to the builder here. 22 | int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小 23 | int memoryCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一 24 | //设置内存缓存大小 25 | builder.setMemoryCache(new LruResourceCache(memoryCacheSize)); 26 | builder.setBitmapPool(new LruBitmapPool(memoryCacheSize)); 27 | } 28 | 29 | @Override 30 | public void registerComponents(Context context, Glide glide) { 31 | // register ModelLoaders here. 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/glideprogress/ProgressDataFetcher.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.glideprogress; 2 | 3 | import com.bumptech.glide.Priority; 4 | import com.bumptech.glide.load.data.DataFetcher; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import okhttp3.Call; 10 | import okhttp3.Interceptor; 11 | import okhttp3.OkHttpClient; 12 | import okhttp3.Request; 13 | import okhttp3.Response; 14 | 15 | /** 16 | * @see com.bumptech.glide.load.data.HttpUrlFetcher 17 | * @see OkHttp sample 18 | * modified by soulrelay 19 | */ 20 | public class ProgressDataFetcher implements DataFetcher { 21 | 22 | private static final String TAG = "ProgressDataFetcher"; 23 | 24 | private final String url; 25 | private Call progressCall; 26 | private InputStream stream; 27 | private volatile boolean isCancelled; 28 | private ProgressUIListener proListener; 29 | 30 | public ProgressDataFetcher(String url, ProgressUIListener listener) { 31 | this.url = url; 32 | this.proListener = listener; 33 | } 34 | 35 | @Override 36 | public InputStream loadData(Priority priority) throws Exception { 37 | Request request = new Request.Builder() 38 | .url(url) 39 | .build(); 40 | final ProgressListener progressListener = new ProgressListener() { 41 | 42 | @Override 43 | public void update(long bytesRead, long contentLength, boolean done) { 44 | if(proListener != null){ 45 | proListener.update((int)bytesRead,(int)contentLength); 46 | } 47 | } 48 | }; 49 | OkHttpClient client = new OkHttpClient.Builder() 50 | .addNetworkInterceptor(new Interceptor() { 51 | 52 | @Override 53 | public Response intercept(Chain chain) throws IOException { 54 | Response originalResponse = chain.proceed(chain.request()); 55 | return originalResponse.newBuilder() 56 | .body(new ProgressResponseBody(originalResponse.body(), progressListener)) 57 | .build(); 58 | } 59 | }) 60 | .build(); 61 | try { 62 | progressCall = client.newCall(request); 63 | Response response = progressCall.execute(); 64 | if (isCancelled) { 65 | return null; 66 | } 67 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 68 | stream = response.body().byteStream(); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | } 72 | return stream; 73 | } 74 | 75 | @Override 76 | public void cleanup() { 77 | if (stream != null) { 78 | try { 79 | stream.close(); 80 | } catch (IOException e) { 81 | // Ignore 82 | } 83 | } 84 | if (progressCall != null) { 85 | progressCall.cancel(); 86 | } 87 | } 88 | 89 | @Override 90 | public String getId() { 91 | return url; 92 | } 93 | 94 | @Override 95 | public void cancel() { 96 | // TODO: we should consider disconnecting the url connection here, but we can't do so directly because cancel is 97 | // often called on the main thread. 98 | isCancelled = true; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/glideprogress/ProgressListener.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.glideprogress; 2 | /** 3 | * @author "https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java" 4 | * @see OkHttp sample 5 | * 通知下载进度 6 | * modified by soulrelay 7 | */ 8 | interface ProgressListener { 9 | void update(long bytesRead, long contentLength, boolean done); 10 | } 11 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/glideprogress/ProgressLoadListener.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.glideprogress; 2 | 3 | /** 4 | * 通知图片加载进度 5 | * modified by soulrelay 6 | */ 7 | public interface ProgressLoadListener { 8 | 9 | void update(int bytesRead, int contentLength); 10 | 11 | void onException(); 12 | 13 | void onResourceReady(); 14 | } 15 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/glideprogress/ProgressModelLoader.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.glideprogress; 2 | 3 | import android.content.Context; 4 | 5 | import com.bumptech.glide.load.data.DataFetcher; 6 | import com.bumptech.glide.load.model.GenericLoaderFactory; 7 | import com.bumptech.glide.load.model.ModelCache; 8 | import com.bumptech.glide.load.model.ModelLoader; 9 | import com.bumptech.glide.load.model.ModelLoaderFactory; 10 | import com.bumptech.glide.load.model.stream.StreamModelLoader; 11 | 12 | import java.io.InputStream; 13 | 14 | /** 15 | * modified by soulrelay 16 | */ 17 | public class ProgressModelLoader implements StreamModelLoader { 18 | 19 | private final ModelCache modelCache; 20 | private ProgressUIListener proListener; 21 | 22 | public ProgressModelLoader(ProgressUIListener listener) { 23 | this(null, listener); 24 | } 25 | 26 | public ProgressModelLoader(ModelCache modelCache) { 27 | this(modelCache, null); 28 | } 29 | 30 | 31 | public ProgressModelLoader(ModelCache modelCache, ProgressUIListener listener) { 32 | this.modelCache = modelCache; 33 | this.proListener = listener; 34 | } 35 | 36 | 37 | @Override 38 | public DataFetcher getResourceFetcher(String model, int width, int height) { 39 | String result = null; 40 | if (modelCache != null) { 41 | result = modelCache.get(model, width, height); 42 | } 43 | if (result == null) { 44 | result = model; 45 | if (modelCache != null) { 46 | modelCache.put(model, width, height, result); 47 | } 48 | } 49 | return new ProgressDataFetcher(result, proListener); 50 | } 51 | 52 | public static class Factory implements ModelLoaderFactory { 53 | 54 | private final ModelCache mModelCache = new ModelCache<>(500); 55 | 56 | @Override 57 | public ModelLoader build(Context context, GenericLoaderFactory factories) { 58 | return new ProgressModelLoader(mModelCache); 59 | } 60 | 61 | @Override 62 | public void teardown() { 63 | 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/glideprogress/ProgressResponseBody.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.glideprogress; 2 | 3 | import java.io.IOException; 4 | 5 | import okhttp3.MediaType; 6 | import okhttp3.ResponseBody; 7 | import okio.Buffer; 8 | import okio.BufferedSource; 9 | import okio.ForwardingSource; 10 | import okio.Okio; 11 | import okio.Source; 12 | 13 | /** 14 | * @author "https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java" 15 | * @see OkHttp sample 16 | */ 17 | class ProgressResponseBody extends ResponseBody { 18 | 19 | private final ResponseBody responseBody; 20 | private final ProgressListener progressListener; 21 | private BufferedSource bufferedSource; 22 | 23 | public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) { 24 | this.responseBody = responseBody; 25 | this.progressListener = progressListener; 26 | } 27 | 28 | @Override 29 | public MediaType contentType() { 30 | return responseBody.contentType(); 31 | } 32 | 33 | @Override 34 | public long contentLength() { 35 | return responseBody.contentLength(); 36 | } 37 | 38 | @Override 39 | public BufferedSource source() { 40 | if (bufferedSource == null) { 41 | bufferedSource = Okio.buffer(source(responseBody.source())); 42 | } 43 | return bufferedSource; 44 | } 45 | 46 | private Source source(Source source) { 47 | return new ForwardingSource(source) { 48 | long totalBytesRead = 0L; 49 | 50 | @Override 51 | public long read(Buffer sink, long byteCount) throws IOException { 52 | long bytesRead = super.read(sink, byteCount); 53 | // read() returns the number of bytes read, or -1 if this source is exhausted. 54 | totalBytesRead += bytesRead != -1 ? bytesRead : 0; 55 | progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); 56 | return bytesRead; 57 | } 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/glideprogress/ProgressUIListener.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.glideprogress; 2 | 3 | /** 4 | * 通知UI进度 5 | * modified by soulrelay 6 | */ 7 | public interface ProgressUIListener { 8 | void update(int bytesRead, int contentLength); 9 | } 10 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/listener/ImageSaveListener.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.listener; 2 | 3 | /** 4 | * 图片保存监听器 5 | * added by sushuai 2017/1/10 6 | */ 7 | public interface ImageSaveListener { 8 | 9 | void onSaveSuccess(); 10 | 11 | void onSaveFail(); 12 | } 13 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/listener/SourceReadyListener.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.listener; 2 | 3 | /** 4 | * 通知准备就绪 5 | * modified by soulrelay 6 | */ 7 | public interface SourceReadyListener { 8 | 9 | void onResourceReady(int width, int height); 10 | } 11 | -------------------------------------------------------------------------------- /imagelib/src/main/java/com/baofeng/soulrelay/utils/imageloader/transformation/GlideCircleTransform.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay.utils.imageloader.transformation; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapShader; 7 | import android.graphics.Canvas; 8 | import android.graphics.Paint; 9 | import android.graphics.PorterDuff; 10 | import android.graphics.PorterDuffXfermode; 11 | import android.graphics.Rect; 12 | import android.graphics.RectF; 13 | 14 | import com.baofeng.soulrelay.utils.CommonUtils; 15 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 16 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 17 | 18 | /** 19 | * DES:自定义BitmapTransformation来实现圆形图片加载 20 | * Created by SuS on 2017/3/8. 21 | */ 22 | public class GlideCircleTransform extends BitmapTransformation { 23 | 24 | private Paint mBorderPaint; 25 | private float mBorderWidth; 26 | 27 | private int height = 0; 28 | private int width = 0; 29 | private int borderColor = 0; 30 | public GlideCircleTransform(Context context) { 31 | super(context); 32 | } 33 | 34 | public GlideCircleTransform(Context context, float borderWidth, int borderColor) { 35 | super(context); 36 | mBorderWidth = Resources.getSystem().getDisplayMetrics().density * borderWidth; 37 | mBorderWidth = CommonUtils.dip2px(context,borderWidth); 38 | mBorderPaint = new Paint(); 39 | mBorderPaint.setDither(true); 40 | mBorderPaint.setAntiAlias(true); 41 | mBorderPaint.setColor(borderColor); 42 | mBorderPaint.setStyle(Paint.Style.STROKE); 43 | mBorderPaint.setStrokeWidth(mBorderWidth); 44 | } 45 | 46 | public GlideCircleTransform(Context context, float borderWidth, int borderColor, int heightPX, int widthPx) { 47 | super(context); 48 | mBorderWidth = Resources.getSystem().getDisplayMetrics().density * borderWidth; 49 | mBorderWidth = CommonUtils.dip2px(context,borderWidth); 50 | mBorderPaint = new Paint(); 51 | mBorderPaint.setDither(true); 52 | mBorderPaint.setAntiAlias(true); 53 | mBorderPaint.setColor(borderColor); 54 | mBorderPaint.setStyle(Paint.Style.STROKE); 55 | mBorderPaint.setStrokeWidth(mBorderWidth); 56 | width = widthPx; 57 | height = heightPX; 58 | this.borderColor = borderColor; 59 | } 60 | 61 | 62 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 63 | if(width>0){ 64 | return circleCrop(pool, toTransform, outWidth, outHeight); 65 | }else{ 66 | return circleCrop(pool, toTransform); 67 | } 68 | } 69 | 70 | private Bitmap circleCrop(BitmapPool pool, Bitmap source) { 71 | if (source == null) return null; 72 | 73 | int size = (int) (Math.min(source.getWidth(), source.getHeight()) - (mBorderWidth / 2)); 74 | int x = (source.getWidth() - size) / 2; 75 | int y = (source.getHeight() - size) / 2; 76 | // TODO this could be acquired from the pool too 77 | Bitmap squared = Bitmap.createBitmap(source, x, y, size, size); 78 | Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888); 79 | if (result == null) { 80 | result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); 81 | } 82 | Canvas canvas = new Canvas(result); 83 | Paint paint = new Paint(); 84 | paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 85 | paint.setAntiAlias(true); 86 | float r = size / 2f; 87 | canvas.drawCircle(r, r, r, paint); 88 | if (mBorderPaint != null) { 89 | float borderRadius = r - mBorderWidth / 2; 90 | canvas.drawCircle(r, r, borderRadius, mBorderPaint); 91 | } 92 | return result; 93 | } 94 | 95 | private Bitmap circleCrop(BitmapPool pool, Bitmap source, int outWidth, int outHeight) { 96 | if (source == null) return null; 97 | Bitmap result = pool.get(width, width, Bitmap.Config.ARGB_8888); 98 | Bitmap output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888); 99 | Canvas canvas = new Canvas(output); 100 | Paint paint = new Paint(); 101 | paint.setColor(borderColor); 102 | paint.setAntiAlias(true); 103 | Rect rect = new Rect(0, 0, width, width); 104 | RectF rectF = new RectF(rect); 105 | canvas.drawRoundRect(rectF, width, width, paint); 106 | output = makeRoundCorner(output,width,borderColor); 107 | result = makeRoundCorner(source,(int)(width-2*mBorderWidth),0); 108 | 109 | result = getAvatarInRoundBg(output,result); 110 | 111 | return result; 112 | } 113 | 114 | //把头像保存成圆形图片 115 | public Bitmap makeRoundCorner(Bitmap bitmap,int px,int borderColor) { 116 | Bitmap output = Bitmap.createBitmap(px, px, Bitmap.Config.ARGB_8888); 117 | Canvas canvas = new Canvas(output); 118 | int color = 0xffffffff; 119 | if(borderColor!=0){ 120 | color = borderColor; 121 | } 122 | Paint paint = new Paint(); 123 | Rect rect = new Rect(0, 0, px, px); 124 | RectF rectF = new RectF(rect); 125 | paint.setAntiAlias(true); 126 | canvas.drawARGB(0, 0, 0, 0); 127 | paint.setColor(color); 128 | canvas.drawRoundRect(rectF, px, px, paint); 129 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 130 | 131 | // if(bitmap.getWidth()/2-67>0 && bitmap.getHeight()/2-67>0) { 132 | bitmap = Bitmap.createScaledBitmap(bitmap, px, px, true); 133 | // } 134 | 135 | canvas.drawBitmap(bitmap, rect, rect, paint); 136 | return output; 137 | } 138 | 139 | //加上圆形背景环 140 | private Bitmap getAvatarInRoundBg(Bitmap bg, Bitmap avatar){ 141 | // 生成画布图像 142 | Bitmap resultBitmap = Bitmap.createBitmap(width, 143 | width, Bitmap.Config.ARGB_8888); 144 | Canvas canvas = new Canvas(resultBitmap);// 使用空白图片生成canvas 145 | 146 | // 将bmp1绘制在画布上 147 | bg = Bitmap.createScaledBitmap(bg, width, width, true); 148 | Rect srcRect = new Rect(0, 0, width,width);// 截取bmp1中的矩形区域 149 | Rect dstRect = new Rect(0, 0, width,width);// bmp1在目标画布中的位置 150 | canvas.drawBitmap(bg, srcRect, dstRect, null); 151 | 152 | avatar = Bitmap.createScaledBitmap(avatar, (int)(width-2*mBorderWidth), (int)(width-2*mBorderWidth), true); 153 | // 将bmp2绘制在画布上 154 | srcRect = new Rect(0, 0, (int)(width-2*mBorderWidth), (int)(width-2*mBorderWidth));// 截取bmp1中的矩形区域 155 | dstRect = new Rect((int)mBorderWidth ,(int)mBorderWidth,(int)(width-mBorderWidth),(int)(width-mBorderWidth));// bmp2在目标画布中的位置 156 | canvas.drawBitmap(avatar, srcRect, dstRect, null); 157 | // 将bmp1,bmp2合并显示 158 | return resultBitmap; 159 | } 160 | 161 | @Override 162 | public String getId() { 163 | return getClass().getName(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /imagelib/src/main/res/drawable-xhdpi/avata_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/drawable-xhdpi/avata_default.png -------------------------------------------------------------------------------- /imagelib/src/main/res/drawable-xhdpi/bg_default_video_common_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/drawable-xhdpi/bg_default_video_common_small.png -------------------------------------------------------------------------------- /imagelib/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /imagelib/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /imagelib/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /imagelib/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /imagelib/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /imagelib/src/main/res/raw/catalog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/raw/catalog.png -------------------------------------------------------------------------------- /imagelib/src/main/res/raw/pic_20170509.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/imagelib/src/main/res/raw/pic_20170509.gif -------------------------------------------------------------------------------- /imagelib/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /imagelib/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #de0b02 7 | 8 | -------------------------------------------------------------------------------- /imagelib/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /imagelib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ImageLoaderUtil 3 | 4 | -------------------------------------------------------------------------------- /imagelib/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /imagelib/src/test/java/com/baofeng/soulrelay/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.baofeng.soulrelay; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /mainapp/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mainapp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "25.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.soulrelay.image" 9 | minSdkVersion 15 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(dir: 'libs', include: ['*.jar']) 27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 28 | exclude group: 'com.android.support', module: 'support-annotations' 29 | }) 30 | compile 'com.android.support:appcompat-v7:25.3.1' 31 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 32 | compile 'com.jakewharton:butterknife:8.4.0' 33 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' 34 | 35 | //compile project(':imagelib') 36 | compile 'com.sus.library:imagelib:1.0.0' 37 | } 38 | -------------------------------------------------------------------------------- /mainapp/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/didi/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /mainapp/src/androidTest/java/com/soulrelay/image/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.soulrelay.image; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.soulrelay.image", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /mainapp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mainapp/src/main/java/com/soulrelay/image/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.soulrelay.image; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.widget.ImageView; 6 | 7 | import com.baofeng.soulrelay.utils.CommonUtils; 8 | import com.baofeng.soulrelay.utils.imageloader.ImageLoaderUtil; 9 | 10 | import butterknife.BindView; 11 | import butterknife.ButterKnife; 12 | 13 | /** 14 | * 图片加载库的封装演示案例 15 | * Created by soulrelay on 2016/12/11 19:18 16 | */ 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | @BindView(R.id.iv_normal) 20 | ImageView ivNormal; 21 | @BindView(R.id.iv_gif) 22 | ImageView ivGif; 23 | @BindView(R.id.iv_circle) 24 | ImageView ivCircle; 25 | @BindView(R.id.iv_circle1) 26 | ImageView ivCircle1; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_main); 32 | ButterKnife.bind(this); 33 | initView(); 34 | } 35 | 36 | private void initView() { 37 | ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be", R.drawable.bg_default_video_common_small, ivNormal); 38 | ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif); 39 | ImageLoaderUtil.getInstance().loadCircleBorderImage("http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be", R.drawable.avata_default, 40 | ivCircle, 2, this.getResources().getColor(R.color.de0b02), CommonUtils.dip2px(this,38),CommonUtils.dip2px(this,38)); 41 | ImageLoaderUtil.getInstance().loadCircleImage("http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be", R.drawable.avata_default, 42 | ivCircle1); 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /mainapp/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 25 | 26 | 35 | 44 | 45 | 56 | 57 | -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mainapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulrelay/ImageLoaderUtil/7cf70aac618977f2431eb7200676d6341249fcad/mainapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /mainapp/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /mainapp/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MainApp 3 | 4 | -------------------------------------------------------------------------------- /mainapp/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mainapp/src/test/java/com/soulrelay/image/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.soulrelay.image; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':imagelib', ':mainapp' 2 | --------------------------------------------------------------------------------