借鉴Glide思想二次封装Fresco
本篇文章已授权微信公众号 dasu_Android(大苏)独家发布
最近封装了个 Fresco 的组件库:DFresco,就顺便来讲讲。
背景
Fresco 图片库很强大,我们项目中就是使用的 Fresco,但有一点就是,不怎么好使用,略麻烦。不同项目中,多多少少都需要对 Fresco 进行一层封装才能在 ui 里快速使用。
这就导致了,不同项目都根据自己的业务需求场景来进行封装,每次有新项目,复制粘贴时又得解决好多业务耦合的错误,麻烦,是真的麻烦~
而且,首次接触 Fresco,接入上手的成本相比其他图片库,如 Glide,成本都要大很多。
举个例子,假如你有这么个需求:加载一张网络上的 gif 图片,为了防止内存占用过多,需要设置分辨率压缩,最后显示到圆形控件上,同时,需要设置占位图,错误图,拉伸方式等。
那么此时,你的代码可能就是这样的:
ImageDecodeOptions imageDecodeOptions = ImageDecodeOptions.newBuilder()
    	.setDecodePreviewFrame(true).build();
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(mUri)
   		.setProgressiveRenderingEnabled(true)
    	.setImageDecodeOptions(imageDecodeOptions);
if (mWidth > 0 && mHeight > 0) {
    builder.setResizeOptions(new ResizeOptions(mWidth, mHeight));
}
ImageRequest request = builder.build();
AbstractDraweeController controller = Fresco.newDraweeControllerBuilder()
    		.setImageRequest(request)
   		 	.setControllerListener(listener)
    		.setOldController(draweeView.getController())
    		.setAutoPlayAnimations(true).build();
draweeView.setController(controller);
同时,你可能还需要在 xml 中对 SimpleDrawwView 控件进行占位图等等的配置:
<com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco"
        android:layout_width="500dp"
        android:layout_height="500dp"
        fresco:actualImageScaleType="centerCrop"
        fresco:fadeDuration="3000"
        fresco:failureImage="@mipmap/ic_launcher"
        fresco:failureImageScaleType="centerCrop"
        fresco:placeholderImage="@mipmap/ic_launcher"
        fresco:placeholderImageScaleType="centerCrop"
        fresco:progressBarAutoRotateInterval="1000"
        fresco:progressBarImage="@drawable/ani_rotate"
        fresco:progressBarImageScaleType="centerCrop"
        fresco:retryImage="@mipmap/ic_launcher"
        fresco:retryImageScaleType="centerCrop"
        fresco:backgroundImage="@mipmap/ic_launcher"
        fresco:overlayImage="@mipmap/ic_launcher"
        fresco:pressedStateOverlayImage="@mipmap/ic_launcher"
        fresco:roundAsCircle="false"
        fresco:roundingBorderWidth="2dip"
        fresco:roundingBorderColor="@color/colorPrimary"/>
如果忘记了某个自定义属性名是什么的时候,还得到网上搜索下资料,是吧。
小结一下,使用 Fresco,你的接入学习成本至少需要知道 Fresco 的如下信息:
- SimpleDraweeView 的自定义属性
 - ImageRequestBuilder 用法及大概用途
 - AbstractDraweeController 用法及大概用途
 - GenericDraweeHierarchy 用法及大概用途
 
如果涉及到一些网络下载监听,缓存之类的,那么你还要了解:
- Imagepipeline 用法及大概用途
 
总之,Fresco 强大是强大,但使用起来不方便,不得不封装一层。
既然要封装,那么就直接借鉴 Glide 的使用思想来进行封装好了,如果有使用过 Glide 的应该很清楚,要实现以上功能,全程一个调用链即可。
二次封装
封装要达到的目的有两点:
- 使用简洁、方便
 - 其他人接入直接上手的成本尽可能少,最好不用去看文档,去看源码
 
第一点可以参考 Glide 的使用方式来设计,那么第二点我的想法是借助 AndroidStudio 的代码提示功能来实现。
比如,你只需知道,组件的入口是 DFresco 即可,其他都通过 AndroidStudio 来给你提示,如:

当你在 AndroidStudio 上输入 DFresco. 后,界面上会弹出你可用 api,这些就是我开放给你的入口,我将这个使用过程划分成几个步骤,每个步骤能做什么,该做什么,我都给你规定好了,你参照着提示,直接从方法命名上就能够知道该如何使用了,AndroidStudio 会一步步引导你。
这里就两个入口,一个是用来初始化 Fresco 的:
init(Context)
这个内部封装了一些默认的初始化配置,比如内存大小配置,内部日志配置等等。
init(Context, ImagePipelineConfig)
这个是开放给你的自定义配置,如果你不想使用默认的配置的话。
source(String url):加载网络上的图片source(File localFile):加载磁盘上的图片source(Context context, int resId):加载 res 内的 drawable 资源图片source(Uri uri):通用的加载方式
我将常用的几种图片来源单独封装出来使用,方便。

当调用了 source() 后就进入了第二个步骤,这个步骤中,我将图片相关的配置设计到另外一个步骤中去,否则连同图片配置的 api 也都在这里的话,会搞得蛮乱的,可能让使用者到这里后不清楚该调用哪些接口了。
所以,我会把控每个步骤的 api,尽量让每个步骤的 api 做的事都比较相近,比如这里:
intoTarget(SimpleDraweeView)加载图片显示到控件上intoTarget(SimpleDraweeView,ControllerListener)加载图片显示到控件上,允许监听这个过程intoTarget(BaseBitmapDataSubscriber)只加载图片到内存中,以 Bitmap 形式存在
我的需求场景大概就是直接加载图片显示到控件上,或者有时候只是需要将图片加载到内存中,但不用显示到某个控件上,反而要取得图片的 Bitmap 对象,所以我将这些都封装起来了。
resize(int width, int height)
这个实际上就是对 Fresco 中的 ResizeOptions 的一层封装而已,简化使用,不至于像以前那么麻烦。
enterImageConfig()
如果你都使用默认配置的话,那么是不用再去调用那些各种配置的接口的,所以我才将图片配置封装到另外一个步骤中,这个步骤你可进,可不进,如果有需求,那么通过这个方法进入图片配置步骤:

这里的配置项很多,也是因为这个原因,所以才不想让这些接口跟上一个步骤放一起,不然很容易让使用者懵掉。而进入了图片配置这个步骤后,这里提供的 api 其实就是对 GenericDraweeHierarchy 的用途进行了一层封装,或者说对 SimpleDraweeView 的自定义属性进行了一层封装。
如果你不熟悉,没关系,其实就是一些常用的功能,如设置控件为圆形、圆角、边框,设置占位图、失败图、进度图、图片拉伸方式、淡入淡出动画时长等等。
这样封装的目的在于,你可以通过一条调用链的形式就设置完所有的配置,就像 Glide 的使用一样,而不用再去 new 很多 Fresco 的类,再去拼接。
进入图片配置步骤只是一个可选的步骤,进来之后当然就要出去,所以当完成了你的配置后,需要调用:finishImageConfig(),如:

这样就完成了图片配置,将流程切回主线了,就可以继续根据你的需要设置图片显示的目标了。
当然,为了防止再次进入图片配置步骤这样造成之前的配置项失效的场景,我借鉴了 Fresco 的 init 处理方法,即,如果一次使用过程中,重复进入图片配置步骤,那么程序会抛异常来提醒你不能这么做。
以上,就是 DFresco 组件的封装思想,欢迎指点一下哈~
另外,参考了 Glide 的一些处理,当你的 intoTarget 是传入了 SimpleDraweeView 控件时,DFresco 内部会自动根据控件的大小对图片进行一次分辨率压缩,降低图片占用内存,当然,如果你有手动调用了 resize(),那么以你的为主。
使用示例
compile 'com.dasu.image:fresco:0.0.1'
使用之前,需先初始化,建议在 Application 中进行:
DFresco.init(this);
//加载 res 中的 drawable 图片到 SimpleDraweeView 控件上(默认支持 gif 图,并且会自动根据控件宽高进行分辨率压缩,降低内存占用
DFresco.source(mContext, R.drawable.weixin).intoTarget(mSimpleDraweeView);
//加载磁盘中的图片,手动设置分辨率的压缩,并获取 bitmap 对象,监听回调,手动显示到 ImageView 控件上
DFresco.source(new File("/mnt/sdcard/weixin.jpg"))
        .resize(500, 500)
        .intoTarget(new BaseBitmapDataSubscriber() {
                @Override
                protected void onNewResultImpl(Bitmap bitmap) {
                    Log.w("!!!!!!", "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount());
                    Log.w("!!!!!!", "width:" + bitmap.getWidth() + ":::height:" + bitmap.getHeight());
                    mImageView.setImageBitmap(bitmap);
                }
                @Override
                protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
                    Log.e("!!!!!!", "onFailureImpl");
                }
            });
//加载网络图片,进行各种配置,如缩放方式,占位图,圆形,圆角,动画时长等等,最后自动显示到 SimpleDraweeView 控件上
DFresco.source("https://upload-images.jianshu.io/upload_images/1924341-9e528ee638e837a5.png")
                    .enterImageConfig() //进入配置步骤
                    .allFitXY()  //所有图片,包括占位图等等的拉伸方式
                    .animFade(3000) //淡入淡出动画时长
                    .placeholderScaleType(ScalingUtils.ScaleType.CENTER_INSIDE) //设置占位图的拉伸方式,后面设置的会覆盖前面的
                    .actualScaleType(ScalingUtils.ScaleType.CENTER)
//                    .asRound(50) //设置圆角,(圆角和圆形不能同时设置)
                    .asCircle() //设置控件显示为圆形控件
                    .roundBorderColor(Color.RED) //设置圆角或圆形的边框颜色
                    .roundBorderWidth(20)  //设置圆角或圆形的边框宽度
                    .failure(R.drawable.timg) //设置失败图
                    .progressBar(R.drawable.aaaa) //设置加载进度图
                    .retry(R.drawable.weixin) //设置重试时的图
                    .placeholder(R.drawable.image) //设置占位图
                    .finishImageConfig() //退出配置步骤
                    .intoTarget(mSimpleDraweeView);
Github 地址
DFresco:https://github.com/woshidasusu/base-module/tree/master/fresco
大家好,我是 dasu,欢迎关注我的公众号(dasuAndroidTv),如果你觉得本篇内容有帮助到你,可以转载但记得要关注,要标明原文哦,谢谢支持~

借鉴Glide思想二次封装Fresco的更多相关文章
- Glide二次封装库的使用
		
更多代码可以查询本人GitHub:欢迎阅读,star点起来. Glide二次封装库源码 前言 为什么选择Glide? Glide 轻量级 速度快 可以根据所需加载图片的大小自动适配所需分辨率的图 支持 ...
 - .Net Framework下对Dapper二次封装迁移到.Net Core2.0遇到的问题以及对Dapper的封装介绍
		
今天成功把.Net Framework下使用Dapper进行封装的ORM成功迁移到.Net Core 2.0上,在迁移的过程中也遇到一些很有意思的问题,值得和大家分享一下.下面我会还原迁移的每一个过程 ...
 - 毕加索的艺术——Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选
		
毕加索的艺术--Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选 官网: http://square.github.i ...
 - 深入理解MVC          C#+HtmlAgilityPack+Dapper走一波爬虫    StackExchange.Redis 二次封装   C# WPF 用MediaElement控件实现视频循环播放    net 异步与同步
		
深入理解MVC MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性 ...
 - Java 面向对象(二)封装
		
一.封装(Encapsulation) 1.概述 封装是面向对象编程的核心思想.把对象的属性和行为封装起来,其载体就是类. 面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的, ...
 - 二次封装这几个 element-ui 组件后,大大减少了我 CRUD 的时间
		
element-ui 因其组件丰富.可拓展性强.文档详细等优点成为 Vue 最火的第三方 UI 框架.element-ui 其本身就针对后台系统设计了很多实用的组件,基本上满足了平时的开发需求. 既然 ...
 - 《手把手教你》系列基础篇(九十七)-java+ selenium自动化测试-框架设计篇-Selenium方法的二次封装和页面基类(详解教程)
		
1.简介 上一篇宏哥介绍了如何设计支持不同浏览器测试,宏哥的方法就是通过来切换配置文件设置的浏览器名称的值,来确定启动什么浏览器进行脚本测试.宏哥将这个叫做浏览器引擎类.这个类负责获取浏览器类型和启动 ...
 - 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)
		
前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...
 - iOS项目相关@AFN&SDWeb的二次封装
		
一,AFNetworking跟SDWebImge是功能强大且常用的第三方,然而在实际应用中需要封装用来复用今天就跟大家分享一下AFN&SDWeb的二次封装 1. HttpClient.h及.m ...
 
随机推荐
- 对称加密AES
			
static void Main(string[] args) { //string str = "rqiJI7eEICT+YZmScpAdbjzLnA4mgL3uU5uHXLBeaE6s8 ...
 - git添加远程仓库
			
3种方式 # 未创建项目目录git clone https://github.com/xxx/zzz.git [这里指定目标文件夹,不填写,默认创建 zzz目录] # 已存在项目文件,未设置远程仓库的 ...
 - Python档案袋( 时间 和 随机数 模块 )
			
时间模块 #导入必要模块 import time #获取时间戳 print(time.time()) #输出:1547651016.03502 #格式化时间元组 # 参数可传递自定义的时间戳,如:ti ...
 - [原创] 详解云计算网络底层技术——虚拟网络设备 tap/tun 原理解析
			
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 在云计算时代, ...
 - [P4886] 快递员
			
考虑在树上选个点rt作为根,并且快递中心就选这儿.计算出所有配送的代价(2*两段之和),设他们的最大值为Max.若此时存在下列情况时,可以判定Max已经为最优解. 1)存在代价为Max的配送(u,v) ...
 - 从零基础到拿到网易Java实习offer,谈谈我的学习经验
			
微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...
 - 关于tensorflow conv2d卷积备忘的一点理解
			
**************input************** [[[[-0.36166722 0.04847232 1.20818889 -0.1794038 -0.53244466] [ ...
 - Java网络编程的Java流介绍
			
前言 网络程序所做的很大一部分工作都是简单的输入输出:将数据字节从一个系统移动到另一个系统.Java的I/O建立于流(stream)之上.输入流读取数据,输出流写入数据.过滤器流(filter)流可以 ...
 - python函数的参数细节
			
按"指针"传递 python中变量赋值.参数传递都是通过"指针"拷贝的方式进行的.除了按"指针"拷贝,还有一种按值拷贝的方式,关于按值.按指 ...
 - 动手实践Mybatis插件
			
前言 Mybatis的插件开发过程的前提是必须要对Mybatis整个SQL执行过程十分熟悉,这样才能正确覆盖源码保证插件运行,总的来说Mybatis的插件式一种侵入式插件,使用时应该十分注意. 在之前 ...