本文GitHub项目地址 : 统一的图片加载架构html
咱们目前的项目对于图片加载的需求很大,一直以来,咱们使用的是Glide做为图片加载的底层包,为了节省图片占用的空间,但愿使用webp的格式来展现动图。因为Glide不支持Animated WebP(即WebP动图) 格式,因此咱们须要把底层的Glide替换为Fresco。相信用过这两个包的同窗都知道,二者的差别仍是比较大的,想要保证在不大量修改代码,也不入侵业务代码的前提下迁移到Fresco上,是个值得思考的问题。java
因为以前已经把项目的图片加载模块重构整合了一次,把图片加载和业务代码有效分离开,所以此次迁移也算有一个较好的基础。至于整合的具体思路以及为何整合这些模块的意义,我在《封装并实现统一的图片加载架构》文章里面已经讲了不少,没看过那篇文章的同窗建议看看,由于接下来就是在这篇文章的基础上去分析如何迁移到Fresco上的问题。git
咱们都知道,Glide是使用了ImageView来加载图片的,而实际项目中还会有咱们自定义的ImageView,好比CircleImageView等,而Fresco则是经过DraweeView来加载图片的,这是一个很是严重的问题。github
因此咱们须要考虑的是在项目迁移到Fresco上的时候,究竟是想办法沿用ImageView来加载图片,仍是想办法使用SimpleDraweeView来加载图片,若是是前者,那么咱们应该怎么作?假如是后者,要怎样才能保证Fresco的代码不会大量入侵到业务代码中,同时又能兼容原来项目的接口呢?web
Tips : 对于Fresco本身的图片加载容器DraweeView,目前暂时继承自ImageView,可是官方表示将来会直接继承自View,咱们开发时经常使用DraweeView的子类SimpleDraweeView来加载图片。缓存
优势bash
在我看官方文档的时候,暗喜了良久。由于若是能够这么作的话,那么这就是最简单的直接的迁移到Fresco上的方法。架构
缺陷:框架
结论:ide
具体操做大概就是当调用了以下加载接口的时候,把ImageView动态替换成SimpleDraweeView,而后加载图片
void showImage(ImageLoaderOptions options); // ImageLoaderOptions包含ImageView,Url 等等复制代码
上面四个方案是我所想到的全部的实现方案,基本上方案三和方案四实现后的效果是最佳的。可是方案三的难度更大,因此综合来看,方案四的性价比更好。(目前项目使用的就是方案四,效果良好,运行正常)
肯定了方案,咱们能够开始实践了,结合方案四的思路,代码实现上基本上是没有什么难度了。
对于如何整合图片加载模块,请务必参考以前的文章《封装并实现统一的图片加载架构》
Fresco加载模块的重点代码实现以下:
FrescoImageLoader.java
// 项目种几乎全部的图片加载都调用到了这里
@Override
public void showImage(@NonNull ImageLoaderOptions options) {
showImgaeDrawee(options);
}
private void showImgaeDrawee(ImageLoaderOptions options) {
// 这个View就是加载图片的ImageView
View view=options.getViewContainer();
SimpleDraweeView drawee=null;
Class clazz=null;
GenericDraweeHierarchy hierarchy=null;
GenericDraweeHierarchyBuilder hierarchyBuilder = GenericDraweeHierarchyBuilder.newInstance(getResources());
// 因为本身的项目中有好几种ImageView,所以须要一一判断
if (view instanceof SquareRImageView) {
clazz= SquareRImageView.class;
drawee=getDraweeView(view,clazz);
if (drawee != null) {
drawee.setAspectRatio(1);
}
}else if (view instanceof CircleImageView){
clazz= CircleImageView.class;
// 传入
drawee=getDraweeView(view,clazz);
hierarchyBuilder.setFadeDuration(400).setRoundingParams(RoundingParams.asCircle());
}else if (view instanceof SimpleDraweeView){
drawee= (SimpleDraweeView) view;
hierarchy=drawee.getHierarchy();
}else if(view instanceof ImageView){
clazz= ImageView.class;
drawee=getDraweeView(view,clazz);
}
else {
Logger.i("no type !!");
return;
}
if (drawee != null) {
// 图片地址
Uri uri=Uri.parse(options.getUrl());
if (options.getHolderDrawable()!=-1) {
hierarchyBuilder.setPlaceholderImage(options.getHolderDrawable());
}
if (options.getErrorDrawable()!=-1) {
hierarchyBuilder.setFailureImage(options.getErrorDrawable());
}
if (hierarchy == null) {
hierarchy= hierarchyBuilder.build();
}
drawee.setHierarchy(hierarchy);
PipelineDraweeControllerBuilder controllerBuilder=Fresco.newDraweeControllerBuilder().setUri(uri).setAutoPlayAnimations(true);
ImageRequestBuilder imageRequestBuilder= ImageRequestBuilder.newBuilderWithSource(uri);
if (options.getImageSize() != null) {
imageRequestBuilder.setResizeOptions(new ResizeOptions(getSize(options.getImageSize().getWidth(),view), getSize(options.getImageSize().getWidth(),view)));
}
if (options.isBlurImage()) {
// 是否作高斯模糊
imageRequestBuilder.setPostprocessor(new BlurPostprocessor(view.getContext().getApplicationContext(), 15));
}
ImageRequest request =imageRequestBuilder.build();
controllerBuilder.setImageRequest(request);
DraweeController controller=controllerBuilder.build();
drawee.setController(controller);
}
}复制代码
在图片加载时,首先须要判断加载图片的容器是ImageView仍是ImageView的子类,由于这意味着对图片不一样的处理,好比CircleImageView意味着是加载一个圆图,因此咱们须要设置SimpleDraweeView为圆图等等。
// 传入加载图片的ImageView,返回一个相同位置,相同大小的SimpleDraweeView
private SimpleDraweeView getDraweeView(View viewContainer,Class<?> classType) {
if (viewContainer instanceof SimpleDraweeView){
return (SimpleDraweeView) viewContainer;
}
SimpleDraweeView mDraweeView=null;
if (classType.isInstance(viewContainer)){
FrameLayout layout=new FrameLayout(viewContainer.getContext());
if(viewContainer.getParent() instanceof FrameLayout){
FrameLayout parent= (FrameLayout) viewContainer.getParent();
FrameLayout.LayoutParams params= (FrameLayout.LayoutParams) viewContainer.getLayoutParams();
// 这个方法来完成最终的添加
mDraweeView=exchangeChilde(parent,viewContainer,params);
}else if(viewContainer.getParent() instanceof RelativeLayout){
RelativeLayout parent= (RelativeLayout) viewContainer.getParent();
RelativeLayout.LayoutParams params= (RelativeLayout.LayoutParams) viewContainer.getLayoutParams();
mDraweeView=exchangeChilde(parent,viewContainer,params);
}else if(viewContainer.getParent() instanceof LinearLayout){
// 当ImageView 的Parent时LinearLayout的时候,处理会有一些不一样
LinearLayout parent= (LinearLayout) viewContainer.getParent();
LinearLayout.LayoutParams params= (LinearLayout.LayoutParams) viewContainer.getLayoutParams();
layout.setLayoutParams(params);
addToViewGroup(parent,viewContainer,layout);
layout.addView(viewContainer);
mDraweeView=exchangeChilde(layout,viewContainer,params);
}else{
//基本上能够涵盖上面一个项目中用到的布局类型了,
//其余的类型如Tablayout等等,视实际状况而定
ViewParent parent=viewContainer.getParent();
Logger.i("");
}
}else{
Logger.i("");
}
return mDraweeView;
}
// 该方将ImageView从原来的Parent种移除,并添加到一个FrameLayout中去
private void addToViewGroup(ViewGroup parent,View viewOld,View viewNew){
for (int i = 0; i < parent.getChildCount(); i++) {
if (parent.getChildAt(i).equals(viewOld)) {
parent.removeView(viewOld);
parent.addView(viewNew,i);
return;
}
}
}复制代码
这里须要判断ImageView的父容器ViewGroup是那些,须要着重区分LinearLayout这个父布局,由于若是ImageView的父容器是LinearLayout,那么咱们就没法在LinearLayout中添加一个大小相同,位置和ImageView重合的SimpleDraweeView来加载图片了,所以,此时咱们须要把这个ImageView拿出来,把它和SimpleDraweeView一块儿装在FrameLayout中,而后在把FrameLayout添加到ImageView原来在LinearLayout中所处的位置。
// 紧挨着ImageView添加SimpleDraweeView到原来的ImageView的位置
private SimpleDraweeView exchangeChilde(ViewGroup parent, View testImageView, ViewGroup.LayoutParams layoutParams) {
SimpleDraweeView draweeview =null;
for (int i = 0; i < parent.getChildCount(); i++) {
if (testImageView.equals(parent.getChildAt(i))) {
if (testImageView instanceof ImageView) {
ImageView img= (ImageView) testImageView;
img.setBackgroundDrawable(null);
img.setImageDrawable(null);
}
if (i+1 < parent.getChildCount()) {
View child=parent.getChildAt(i+1);
// 此处理应作更加仔细的判断
if (child instanceof SimpleDraweeView) {
return (SimpleDraweeView) child;
}
}
draweeview=new SimpleDraweeView(testImageView.getContext());
draweeview.setLayoutParams(layoutParams);
parent.addView(draweeview,i+1);
return draweeview;
}
}
return draweeview;
}复制代码
以上基本上就是以Fresco来实现图片加载模块的核心代码了,基本能够覆盖原有的Glide的功能,而且入侵度低,无需修改原有代码,随时可替换。
暂无
项目已经上传了github,点此获取,求star! 求follow !