摘要:最终的显隐操作都会转化为在方法中进行操作。主要是通过与数组来控制数组中各个的值,即显隐它继承于,顾名思义通过矩阵来改变状态。
上篇文章我们分析了Fresco中的DraweeView,对其中的一些原理以及方法进行了解析。在这过程中我们了解到,DraweeView中是通过DraweeHolder来统一管理的。而DraweeHolder又是用来统一管理相关的Hierarchy与Controller,如果想了解DraweeView相关的知识,可以先看下我的前一篇文章Fresco源码分析之DraweeView。今天这里进一步来分析Fresco中的Hierarchy。
GenericDraweeHierarchyBuilder在GenericDraweeView的构造方法中会调用inflateHierarchy(context, atts)方法来创建一个GenericDraweeHierarchyBuilder对象,通过调用该对象的build方法来生成一个Hierarchy。
如果你看了我上一篇文章,相信对这个方法你会感到很亲切。
所以这个类的主要作用是用来创建一个Hierarchy,通过builder模式来实例化包含相关信息的Hierarchy。如果你一步步深入了解Fresco的话,相信你对builder模式也将习以为常,因为后面你将经常与它碰面。这也是Fresco开源项目的主要设计模式。在该builder类中主要构建的信息有:
Drawable相关:placeholderImage、retryImage、failureImage、progressBarImage、background、overlays与pressedStateOverlay
ScaleType相关:与Drawable相对应的placeholderImageScaleType、retryImageScaleType等等。
其它:fadeDuration渐变过渡时间与RoundingParams圆角相关信息等等
GenericDraweeHierarchy通过上面的GenericDraweeHierarchyBuilder的build方法会创建一个GenericDraweeHierarchy对象。这就是我们今天需要主要分析的类,也是Fresco的一个核心类。
SettableDraweeHierarchy首先我们来分析一下它的类结构,它会实现SettableDraweeHierarchy接口,我们进入该接口发现一共有6个接口方法,它们分别为:
void reset(); 重新初始化Hierarchy
void setImage(Drawable drawable, float progress, boolean immediate); 设置实际需要展示的图片,其中progress表示图片的加载质量进度(在渐进式中会使用到)
void setProgress(float progress, boolean immediate); 更新图片加载进度
void setFailure(Throwable throwable); 图片加载失败时调用,可以设置failureImage
void setRetry(Throwable throwable); 当图片加载失败时重新进行加载,可以设置retryImage
void setControllerOverlay(Drawable drawable); 用来设置图层覆盖
这些方法在GenericDraweeHierarchy中都会做出相应的实现,同时最终都会在对应的DraweeView中的Controller来调用。
至于这些方法中的实现细节,由于代码比较多,这里就不一一列举出来,大家可以自行查看GenericDraweeHierarchy的源码。DraweeHierarchy
上面的SettableDraweeHierarchy还有一个父类接口为DraweeHierarchy,在这个接口中只有一个接口方法为Drawable getTopLevelDrawable();。是不是对这个方法也有点熟悉呢?(路人甲:嗯,好像上篇文章提及过!)它是用来获取视图树的最顶层视图,其实说白了就是显示出来的Drawable。它主要在DraweeHolder中调用,最终也会由void setHierarchy(DH hierarchy) 与 void setController(@Nullable DraweeController draweeController) 来调用
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
从而显示需要展示的图片。
下面我们回到之前的GenericDraweeHierarchy类。在开始分析它之前,我们先来了解一下它的另一个感念-层级树或者说图层树,我这里就把它说成图层树吧。那么我们来看下Fresco的图层树是什么样的:
* o RootDrawable (top level drawable) * | * +--o FadeDrawable * | * +--o ScaleTypeDrawable (placeholder branch, optional) * | | * | +--o Drawable (placeholder image) * | * +--o ScaleTypeDrawable (actual image branch) * | | * | +--o ForwardingDrawable (actual image wrapper) * | | * | +--o Drawable (actual image) * | * +--o null (progress bar branch, optional) * | * +--o Drawable (retry image branch, optional) * | * +--o ScaleTypeDrawable (failure image branch, optional) * | * +--o Drawable (failure image
根据上面所展示的层次结构,我们可以发现最底层是由RootDrawable来构成,它有一个直接子分支为FadeDrawable。而在FadeDrawable中又有5个直接子分支,分别为placeholder branch、actual image branch、progressBar image branch、retry image branch与failure image branch。至于这些image的作用相信不用我再多做说明了,这些image除了actual image是必须要明确指定的,其它的都是可选择的配置。因此RootDrawable与FadeDrawable是一定存在的。虽然其它的都是可选的配置,但无论你是否选择了,它们的层级结构都会保留在图层树中。还有每一个层级都有自己独立的scale type,当然rounding(圆角)也是支持的。
其实除了这5个分支,与它们同一层次的还有background image与overlay image,它们分别位于placeholder image之前与failure image之后。background相信都知道,因为图片都支持background与src,overlay为图层覆盖。至于为什么没有在上面的结构中显示,我这里也不得而知,猜测可能是这两个并不是Fresco主要常用的特性。
那么这些图层结构是通过layers数组来体现的,可以来看下GenericDraweeHierarchy的源码
GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) { mResources = builder.getResources(); mRoundingParams = builder.getRoundingParams(); mActualImageWrapper = new ForwardingDrawable(mEmptyActualImageDrawable); int numOverlays = (builder.getOverlays() != null) ? builder.getOverlays().size() : 1; numOverlays += (builder.getPressedStateOverlay() != null) ? 1 : 0; // layer indices and count int numLayers = OVERLAY_IMAGES_INDEX + numOverlays; // array of layers Drawable[] layers = new Drawable[numLayers]; layers[BACKGROUND_IMAGE_INDEX] = buildBranch(builder.getBackground(), null); layers[PLACEHOLDER_IMAGE_INDEX] = buildBranch( builder.getPlaceholderImage(), builder.getPlaceholderImageScaleType()); layers[ACTUAL_IMAGE_INDEX] = buildActualImageBranch( mActualImageWrapper, builder.getActualImageScaleType(), builder.getActualImageFocusPoint(), builder.getActualImageColorFilter()); layers[PROGRESS_BAR_IMAGE_INDEX] = buildBranch( builder.getProgressBarImage(), builder.getProgressBarImageScaleType()); layers[RETRY_IMAGE_INDEX] = buildBranch( builder.getRetryImage(), builder.getRetryImageScaleType()); layers[FAILURE_IMAGE_INDEX] = buildBranch( builder.getFailureImage(), builder.getFailureImageScaleType()); if (numOverlays > 0) { int index = 0; if (builder.getOverlays() != null) { for (Drawable overlay : builder.getOverlays()) { layers[OVERLAY_IMAGES_INDEX + index++] = buildBranch(overlay, null); } } else { index = 1; // reserve space for one overlay } if (builder.getPressedStateOverlay() != null) { layers[OVERLAY_IMAGES_INDEX + index] = buildBranch(builder.getPressedStateOverlay(), null); } } // fade drawable composed of layers mFadeDrawable = new FadeDrawable(layers); mFadeDrawable.setTransitionDuration(builder.getFadeDuration()); // rounded corners drawable (optional) Drawable maybeRoundedDrawable = WrappingUtils.maybeWrapWithRoundedOverlayColor(mFadeDrawable, mRoundingParams); // top-level drawable mTopLevelDrawable = new RootDrawable(maybeRoundedDrawable); mTopLevelDrawable.mutate(); resetFade(); }
根据源码可以明显的看出不管overlay是否存在,它都会保留图层层次,当然如果存在的话,就根据实际情况在OVERLAY_IMAGES_INDEX之后增加图层层次。layers是一个Drawable数组,这里通过Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 方法来创建对应的Drawable。在最后,创建了FadeDrawable并将layers传递给它,在这里FadeDrawable的特性应该有点眉目了吧,其实它内部做的就是对Drawable数组进行操作。之后RootDrawable也出现了(关于Drawable文章后面会统一分析),这样之前所提到的层级结构就形成了。
既然actual image是一定存在的,那么在它真正展示之前image中的显示用什么来控制的呢?其实就是我们之前所提到的SettableDraweeHierarchy来控制。在真实的图片展示出来之前,它可以用来展示placeholder image等相关图层。具体的我们可以来看一下它里面实现的一些方法。
setImage这里就拿void setImage(Drawable drawable, float progress, boolean immediate) 来进行深入分析,那么下面来看下它的源码:
@Override public void setImage(Drawable drawable, float progress, boolean immediate) { drawable = WrappingUtils.maybeApplyLeafRounding(drawable, mRoundingParams, mResources); drawable.mutate(); mActualImageWrapper.setDrawable(drawable); mFadeDrawable.beginBatchMode(); fadeOutBranches(); fadeInLayer(ACTUAL_IMAGE_INDEX); setProgress(progress); if (immediate) { mFadeDrawable.finishTransitionImmediately(); } mFadeDrawable.endBatchMode(); }
首先该方法会使用WrappingUtils工具类并且结合RoundingParams参数来重新生成一个可以附带圆角的Drawable。然后将新的Drawable交由mActualImageWrapper,结合上面的层次结果分析,会很容易知道这就是需要真实显示的图层。它是一个ForwardingDrawable类型,该类主要是对传入的目标Drawable进行相应的原生方法操作。下一步调用FadeDrawable的beginBatchMode()方法,该方法的作用主要为了图层批处理做标记,防止在批处理时进行invalidate操作。直到最后的endBatchMode()方法调用之后才标识着图层批处理操作结束。该批处理操作的目的是将actual image图层显示出来,所以首先调用fadeOutBranches()方法:
private void fadeOutBranches() { fadeOutLayer(PLACEHOLDER_IMAGE_INDEX); fadeOutLayer(ACTUAL_IMAGE_INDEX); fadeOutLayer(PROGRESS_BAR_IMAGE_INDEX); fadeOutLayer(RETRY_IMAGE_INDEX); fadeOutLayer(FAILURE_IMAGE_INDEX); }
这里对上述提到的所有图层进行fadeOutLayer()操作,继续进入fadeOutLayer()方法
private void fadeOutLayer(int index) { if (index >= 0) { mFadeDrawable.fadeOutLayer(index); } }
发现也很简单,无非就是调用了FadeDrawable的fadeOutLayer()方法。
public void fadeOutLayer(int index) { mTransitionState = TRANSITION_STARTING; mIsLayerOn[index] = false; invalidateSelf(); }
在之前已经提到过FadeDrawable本质上可以理解为时一个Drawable数组,内部都是围绕着数组整体进行操作。对应的还有fadeInLayer()
private void fadeInLayer(int index) { if (index >= 0) { mFadeDrawable.fadeInLayer(index); } }
在FadeDrawable中除了用来保存相应的Drawable数组mLayers,还有与其相对应的mIsLayerOn布尔数组,该数组用来标识各个Hierarchy中的图层是否需要展示。所以fadeOutLayer()是对index处的图层进行隐藏标识。最终的显隐操作都会转化为在void draw(Canvas canvas)方法中进行alpha操作。
那么再回到之前的setImage()方法中,fadeOutBranches()是对相关的图层进行隐藏标识,然后再通过fadeInLayer(ACTUAL_IMAGE_INDEX)方法改变actual image图层的标识,将它改变成显示状态。最后如果有progressBar image图层的话,也将会由setProgress(progress)方法来体现。immediate是用来判断是否之后的显隐操作立马实现或者渐变过渡实现(内部就是对alpha进行百分比操作,内部有个mDurationMs,该时间值也是文章开头提到的builder中的fadeDuration)。这样整个的实际图片展示流程我们已经分析完毕,所以Hierarchy中最重要的还是对图层概念的理解。下面再对GenericDraweeHierarchy中的一些其它方法进行简要的说明:
Drawable buildActualImageBranch(Drawable drawable, @Nullable ScaleType scaleType, @Nullable PointF focusPoint, @Nullable ColorFilter colorFilter) 构建实际展示图片的图层分支,内部对于Drawable的创建还是借助WrappingUtils工具类
Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 构建除实际展示图片之外的其它图层分支,Drawable的创建也是借助WrappingUtils工具类
void resetFade() 初始化图层结构
主要的就这几个吧,其它的都已经在上面分析流程中详细说明了。Drawable
最后再整理一下Fresco中的一些相关的自定义的Drawable子类
ArrayDrawable:Drawable数组的集合体,通过layers数组来管理Drawable,内部的都是对数组集合中的每一个Drawable进行操作,类似与Android原生的LayerDrawable,只是它并不支持add与remove操作
ForwardingDrawable:对传入的目标Drawable即操作对象进行封装处理,该新类的方法可以调用目标对象对应的方法,同时保留目标Drawable的各个状态,不依赖与目标类的细节实现,提高新类的稳定性,该方式可以称之为复合。Fresco中绝大多数自定义Drawable都是它的子类。
AutoRotateDrawable:它继承于ForwardingDrawable,实现的是对Drawable的旋转操作
FadeDrawable:它继承于ArrayDrawable,之前也详细提到过,Hierarchy中的主要图层集合体。主要是通过mLayers与mIsLayerOn数组来控制数组中各个Drawable的alpha值,即显隐
MatrixDrawable:它继承于ForwardingDrawable,顾名思义通过矩阵来改变Drawable状态。
OrientedDrawable:它也继承于ForwardingDrawable,它不同于AutoRotateDrawable的是,它只支持90度的倍数角度旋转。
ProgressBarDrawable:进度条Drawable,支持横竖方向。
RoundedBitmapDrawable:继承于BitmapDrawable,根据Bitmap来创建有关圆角的Drawable,主要在WrappingUtils类中使用,用例构建全新的圆角Drawable
RoundedColorDrawable:继承于Drawable,根据Color来创建圆角Drawable,主要在WrappingUtils类中使用,用例构建全新的圆角Drawable
RoundedCornersDrawable:继承于ForwardingDrawable,用于设计overLay覆盖图片圆角,主要在WrappingUtils类中使用
ScaleTypeDrawable:继承于ForwardingDrawable,用于缩放类型的Drawable
End本篇文章主要是分析Fresco中有关Hierarchy相关的实现与原理,通过分析发现Hierarchy中都是对Drawable图层进行处理,并没有其它的缓存、请求之类的逻辑。所以如果你使用Fresco的时候只使用Hierarchy的话,就与别的ImageView没有多大的区别,真正的图层操作与缓存控制都在Controller中,所以下篇文章将进入Controller解析,来详细了解它的实现细节。
Fresco源码分析系列Github地址
关注 RecommendAndroid共享动画兼容实现
Kotlin最佳实践
RecyclerView下拉刷新与上拉更多
Android高仿微信之mvp实现(四)
php与android的简单交互
tensorflow-梯度下降,有这一篇就足够了
博客
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70792.html
摘要:如果你是第一次看我的的源码分析系列文章,这里强烈推荐你先阅读我的前面两篇文章源码分析之与源码分析之。其中是用来管理推迟资源释放的。发送预处理请求,获取相应类型的缓存数据。当数据源已经获取到时,发送通知给订阅者,因此分别回调订阅者的方法。 如果你是第一次看我的Fresco的源码分析系列文章,这里强烈推荐你先阅读我的前面两篇文章Fresco源码分析之DraweeView与Fresco源码分...
摘要:首先这是对的源码分析,所以在看这篇文章之前你应该要有使用的基础,如果没有的强烈推荐看下官方文档。在中统一由来替代。关于后续文章会详细分析。在其内部的,是用来记录事件的传递,方便的调试。这次主要是分析了中的基本组件与它的子类。 在Android中图片加载的框架很多,例如:Fresco、Picasso、Glide与Imageloader。它们都有各自的优点,但总的来说,使用起来方便简单、可...
阅读 2732·2023-04-25 14:21
阅读 1167·2021-11-23 09:51
阅读 3999·2021-09-22 15:43
阅读 605·2019-08-30 15:55
阅读 1550·2019-08-29 11:28
阅读 2438·2019-08-26 11:44
阅读 1675·2019-08-23 18:15
阅读 2874·2019-08-23 16:42