项目开发中,往往会随着需求的改变而切换到其它图片加载框架上去。如果最初代码设计的耦合度太高,那么恭喜你,成功入坑了。至今无法忘却整个项目一行行去复制粘贴被支配的恐惧。:)

那么是否存在一种方式 能够一劳永逸地解决这个痛点呢?下面我们来分析一下图片加载框架面对的现状和解决思路。

问题现状

一个优秀的框架一般在代码设计的时候已经封装很不错了,对于开发者而言框架的使用也是很方便,但是为什么说我们往往还要去做这方面的框架封装呢?原因很简单,实际项目开发中,我们不得不面对着日新月异的需求变化,想要在这个变化中最大程度的实现代码的可扩展性和变通性(当然还可以偷懒),不能因为牵一发而动全身,同时要将框架适配到实际项目,框架的再封装设计显得尤为重要。

不多废话,我们可以开始今天的图片封装之路了。

设计思路

图片框架的封装主要需要满足以下三点:

低耦合,方便将来的代码扩展。至少要支持目前市场上使用率最高的图片框架Fresco、Glide、Picasso三者之间的切换
满足项目中各种需求
调用方便
谈到图片封装,最先想到的是把一些常用的功能点作为参数传入到方法内,然后调用图片加载框架实现我们图片的加载工作。比如说像下面这样

public interface ImageLoader {void loadImage(ImageView view, String path, int placeholderId, int errorId,boolean skipMemory);void loadImage(ImageView view, File file, int placeholderId, int errorId, boolean skipMemory);

}
然后分别写对应的ImageLoader实现类FrescoImageLoader、GlideImageLoader、PicassoImageLoader,最后采用策略的设计模式实现代码的切换。那么这种方式实际效果如何呢?实际开发中很明显的一个 问题就是,对于每一个需要的参数都需要进行对应的封装,就不止上面所提到的两个方法,我们需要封装大量的方法去满足实际的项目需要,而且每个框架的很多属性不一致,如果切换图片框架的话,还是需要大量的切换成本的。

于是我们想到了下面的这种思路

public interface ILoaderStrategy {

void loadImage(LoaderOptions options);/*** 清理内存缓存*/
void clearMemoryCache();/*** 清理磁盘缓存*/
void clearDiskCache();

}
提取各个框架通用的View,path/file文件路径,通过LoaderOptions解决大量不同参数传入的问题。这里需要说明的是,LoaderOptions中采用控件View,而不是ImageView,主要考虑到Fresco图片框架采用了DraweeView,这里保留了设计的扩展性。而图片参数类LoaderOptions采用了Builder设计模式:

public class LoaderOptions {public int placeholderResId;public int errorResId;public boolean isCenterCrop;public boolean isCenterInside;public boolean skipLocalCache; //是否缓存到本地public boolean skipNetCache;public Bitmap.Config config = Bitmap.Config.RGB_565;public int targetWidth;public int targetHeight;public float bitmapAngle; //圆角角度public float degrees; //旋转角度.注意:picasso针对三星等本地图片,默认旋转回0度,即正常位置。此时不需要自己rotatepublic Drawable placeholder;public View targetView;//targetView展示图片public BitmapCallBack callBack;public String url;public File file;public int drawableResId;public Uri uri;public LoaderOptions(String url) {this.url = url;}public LoaderOptions(File file) {this.file = file;}public LoaderOptions(int drawableResId) {this.drawableResId = drawableResId;}public LoaderOptions(Uri uri) {this.uri = uri;}public void into(View targetView) {this.targetView = targetView;ImageLoader.getInstance().loadOptions(this);}public void bitmap(BitmapCallBack callBack) {this.callBack = callBack;ImageLoader.getInstance().loadOptions(this);}public LoaderOptions placeholder(@DrawableRes int placeholderResId) {this.placeholderResId = placeholderResId;return this;}public LoaderOptions placeholder(Drawable placeholder) {this.placeholder = placeholder;return this;}public LoaderOptions error(@DrawableRes int errorResId) {this.errorResId = errorResId;return this;}public LoaderOptions centerCrop() {isCenterCrop = true;return this;}public LoaderOptions centerInside() {isCenterInside = true;return this;}public LoaderOptions config(Bitmap.Config config) {this.config = config;return this;}public LoaderOptions resize(int targetWidth, int targetHeight) {this.targetWidth = targetWidth;this.targetHeight = targetHeight;return this;}/*** 圆角* @param bitmapAngle   度数* @return*/public LoaderOptions angle(float bitmapAngle) {this.bitmapAngle = bitmapAngle;return this;}public LoaderOptions skipLocalCache(boolean skipLocalCache) {this.skipLocalCache = skipLocalCache;return this;}public LoaderOptions skipNetCache(boolean skipNetCache) {this.skipNetCache = skipNetCache;return this;}public LoaderOptions rotate(float degrees) {this.degrees = degrees;return this;}}

当然了,如果觉得有项目中需要可以以LoderOptions为基类继续扩展LoderOptions,不过现在这样在LoaderOptions上自行扩展基本上可以满足所有日常需要了。现在解决了代码设计的方向,那么接下来 我们要采取策略的方式实现图片框架的解耦。

import android.view.View;import com.squareup.picasso.Callback;import java.io.File;/*** 图片管理类,提供对外接口。* 静态代理模式,开发者只需要关心ImageLoader + LoaderOptions* Created by MhListener on 2017/6/27.*/public class ImageLoader{private static ILoaderStrategy sLoader;private static volatile ImageLoader sInstance;private ImageLoader() {}//单例模式public static ImageLoader getInstance() {if (sInstance == null) {synchronized (ImageLoader.class) {if (sInstance == null) {//若切换其它图片加载框架,可以实现一键替换sInstance = new ImageLoader();}}}return sInstance;}//提供实时替换图片加载框架的接口public void setImageLoader(ILoaderStrategy loader) {if (loader != null) {sLoader = loader;}}public LoaderOptions load(String path) {return new LoaderOptions(path);}public LoaderOptions load(int drawable) {return new LoaderOptions(drawable);}public LoaderOptions load(File file) {return new LoaderOptions(file);}public LoaderOptions load(Uri uri) {return new LoaderOptions(uri);}public void loadOptions(LoaderOptions options) {sLoader.loadImage(options);}public void clearMemoryCache() {sLoader.clearMemoryCache();}public void clearDiskCache() {sLoader.clearDiskCache();}
}

最后我们开始图片加载框架的具体实现方式,这里我实现了Picasso图片加载,开发者可以根据此例自行扩展GlideLoader或者FrescoLoader。

public class PicassoLoader implements ILoaderStrategy {private volatile static Picasso sPicassoSingleton;private final String PICASSO_CACHE = "picasso-cache";private static LruCache sLruCache = new LruCache(App.gApp);private static Picasso getPicasso() {if (sPicassoSingleton == null) {synchronized (PicassoLoader.class) {if (sPicassoSingleton == null) {sPicassoSingleton = new Picasso.Builder(App.gApp).memoryCache(sLruCache).build();}}}return sPicassoSingleton;}@Overridepublic void clearMemoryCache() {sLruCache.clear();}@Overridepublic void clearDiskCache() {File diskFile = new File(App.gApp.getCacheDir(), PICASSO_CACHE);if (diskFile.exists()) {//这边自行写删除代码
//          FileUtil.deleteFile(diskFile);}}@Overridepublic void loadImage(LoaderOptions options) {RequestCreator requestCreator = null;if (options.url != null) {requestCreator = getPicasso().load(options.url);} else if (options.file != null) {requestCreator = getPicasso().load(options.file);}else if (options.drawableResId != 0) {requestCreator = getPicasso().load(options.drawableResId);} else if (options.uri != null){requestCreator = getPicasso().load(options.uri);}if (requestCreator == null) {throw new NullPointerException("requestCreator must not be null");}if (options.targetHeight > 0 && options.targetWidth > 0) {requestCreator.resize(options.targetWidth, options.targetHeight);}if (options.isCenterInside) {requestCreator.centerInside();} else if (options.isCenterCrop) {requestCreator.centerCrop();}if (options.config != null) {requestCreator.config(options.config);}if (options.errorResId != 0) {requestCreator.error(options.errorResId);}if (options.placeholderResId != 0) {requestCreator.placeholder(options.placeholderResId);}if (options.bitmapAngle != 0) {requestCreator.transform(new PicassoTransformation(options.bitmapAngle));}if (options.skipLocalCache) {requestCreator.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE);}if (options.skipNetCache) {requestCreator.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE);}if (options.degrees != 0) {requestCreator.rotate(options.degrees);}if (options.targetView instanceof ImageView) {requestCreator.into(((ImageView)options.targetView));} else if (options.callBack != null){requestCreator.into(new PicassoTarget(options.callBack));}}class PicassoTarget implements Target {BitmapCallBack callBack;protected PicassoTarget(BitmapCallBack callBack) {this.callBack = callBack;}@Overridepublic void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {if (this.callBack != null) {this.callBack.onBitmapLoaded(bitmap);}}@Overridepublic void onBitmapFailed(Exception e, Drawable errorDrawable) {if (this.callBack != null) {this.callBack.onBitmapFailed(e);}}@Overridepublic void onPrepareLoad(Drawable placeHolderDrawable) {}}class PicassoTransformation implements Transformation {private float bitmapAngle;protected PicassoTransformation(float corner){this.bitmapAngle = corner;}@Overridepublic Bitmap transform(Bitmap source) {float roundPx = bitmapAngle;//圆角的横向半径和纵向半径Bitmap output = Bitmap.createBitmap(source.getWidth(),source.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(output);final int color = 0xff424242;final Paint paint = new Paint();final Rect rect = new Rect(0, 0, source.getWidth(),source.getHeight());final RectF rectF = new RectF(rect);paint.setAntiAlias(true);canvas.drawARGB(0, 0, 0, 0);paint.setColor(color);canvas.drawRoundRect(rectF, roundPx, roundPx, paint);paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(source, rect, rect, paint);source.recycle();return output;}@Overridepublic String key() {return "bitmapAngle()";}}}

好了,到了这里,关于图片框架的封装已经全部完成。而且该图片框架的封装已经成功应用到公司项目上,目前反馈良好。如有问题,欢迎交流指教!


如果感兴趣的话,欢迎在github给个star。代码已上传github链接
如果有考虑引用该封装的话,可以采用下面的方式:

//根目录下build.gradle配置
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}

//项目build.gradle依赖
dependencies {
compile 'com.github.mhlistener:ImageLoader:1.0.5'
}

//使用方式
1.Application中全局设置
ImageLoader.getInstance().setGlobalImageLoader(new PicassoLoader());

2.界面中使用封装
ImageView imageView = findViewById(R.id.imageview);
String url = "http://ww2.sinaimg.cn/large/7a8aed7bgw1eutsd0pgiwj20go0p0djn.jpg";
ImageLoader.getInstance()
.load(url)
.angle(80)
.resize(400, 600)
.centerCrop()
.config(Bitmap.Config.RGB_565)
.placeholder(R.mipmap.test)
.error(R.mipmap.test)
.skipLocalCache(true)
.into(imageView);

转载于:https://blog.51cto.com/13983283/2311417

优雅地实现Android主流图片加载框架封装,可无侵入切换框架相关推荐

  1. Android常用图片加载库介绍及对比

    前言 图片加载在 Android开发项目中是必不可少的,为了降低开发周期和难度,我们经常会选用一些图片加载的开源库,而Android发展到现在图片加载开源库也越来越多了,下面介绍几种开发中主流的图片加 ...

  2. android 加载进度,Android实现图片加载进度提示

    本文实例为大家分享了Android实现图片加载进度提示的具体代码,供大家参考,具体内容如下 先上图: 实现原理: 第一个控件的实现原理是重写ImageView的onDraw()方法,利用Canvas的 ...

  3. Android Glide图片加载框架(四)回调与监听

    文章目录 Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源码解析之with() Android Gl ...

  4. Android Glide图片加载框架(三)缓存机制

    文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...

  5. Android Glide图片加载框架(二)源码解析之into()

    文章目录 一.前言 二.源码解析 1.into(ImageView) 2.GlideContext.buildImageViewTarget() 3.RequestBuilder.into(Targe ...

  6. Android Glide图片加载框架(二)源码解析之load()

    文章目录 一.前言 二.源码分析 1.load() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源 ...

  7. Android Glide图片加载框架(二)源码解析之with()

    文章目录 一.前言 二.如何阅读源码 三.源码解析 1.with() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图 ...

  8. Android Glide图片加载框架(一)基本用法

    文章目录 一.前言 二.简介 三.基本用法 第一步:调用 Glide.with() 方法创建加载图片的实例 第二步:调用 load() 方法指定待加载的图片资源 第三步:调用 into() 方法绑定显 ...

  9. Android平滑图片加载和缓存库Glide使用详解

    在图片加载库烂大街的今天,选择一个适合自己使用的图片加载库已经成为了每一个Android开发者的必经之路.现在市面上知名的图片加载库有UIL,Picasso,Volley ImageLoader,Fr ...

最新文章

  1. CowNew开源团队4月14日聚会
  2. Fiddler抓取数据并分析(完整的配置教程)
  3. java链表寻找中间节点
  4. 面试文员计算机水平考题,一般电脑文员面试上机操作的时候会考什么?
  5. Qt Post上传图片文件到服务器
  6. DCMTK:测试DcmSCP和DcmSCU类
  7. mysql definer_mysql常见问题之视图权限控制--安全性为DEFINER
  8. No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? ide
  9. 用ghost备份和还原Linux系统
  10. 《DSP using MATLAB》示例Example4.6
  11. RecSys Challenge 历年推荐赛题汇总
  12. HBase 完全分布式的安装
  13. 今天是有纪念意义的一天--中国13亿人口日
  14. 微信小程序云开发教程-WXML入门-基本语法
  15. java web 基础知识 流程图
  16. 计算机东北大学与兰州大学哪个好,东北大学和兰州大学如何选择?上哪个比较好?...
  17. 如何用java股票量化交易接口读取股票数据?
  18. WPF——后台代码实现将多张图片拼接成一张
  19. 【过了】系统分析师考试心得
  20. python图像锐化,图像加强、锐化,利用 Python-OpenCV 来实现 4 种方法!

热门文章

  1. BurpSuite -Repeater
  2. Failed to load project configuration:xxx cannot read file .idea/misc.xml
  3. 中百信玄武库Kubernetes实践与探索
  4. 为什么华为a1路由器网速变慢_凭什么网速就比别人快?华为路由 A1 畅享版体验...
  5. 网上跳蚤市场网站系统HTML5+Vue+nodejs
  6. LaTeX入门(刘海洋)_百度云
  7. UReport2 多报表文件打印,ZIP下载实现
  8. PP实施经验分享(21)——(ECC版本)生产版本\BOM\工艺路线选择(涉及批量大小应用)
  9. Juniper路由器
  10. go get connectex: A connection attempt failed because the connected party did not properly respond