上篇文章咱们分析了Fresco中的DraweeView,对其中的一些原理以及方法进行了解析。在这过程当中咱们了解到,DraweeView中是经过DraweeHolder来统一管理的。而DraweeHolder又是用来统一管理相关的Hierarchy与Controller,若是想了解DraweeView相关的知识,能够先看下个人前一篇文章Fresco源码分析之DraweeView。今天这里进一步来分析Fresco中的Hierarchy。php
在GenericDraweeView的构造方法中会调用*inflateHierarchy(context, atts)*方法来建立一个GenericDraweeHierarchyBuilder对象,经过调用该对象的build方法来生成一个Hierarchy。android
若是你看了我上一篇文章,相信对这个方法你会感到很亲切。git
因此这个类的主要做用是用来建立一个Hierarchy,经过builder模式来实例化包含相关信息的Hierarchy。若是你一步步深刻了解Fresco的话,相信你对builder模式也将习觉得常,由于后面你将常常与它碰面。这也是Fresco开源项目的主要设计模式。在该builder类中主要构建的信息有:github
经过上面的GenericDraweeHierarchyBuilder的build方法会建立一个GenericDraweeHierarchy对象。这就是咱们今天须要主要分析的类,也是Fresco的一个核心类。canvas
首先咱们来分析一下它的类结构,它会实现SettableDraweeHierarchy接口,咱们进入该接口发现一共有6个接口方法,它们分别为:设计模式
这些方法在GenericDraweeHierarchy中都会作出相应的实现,同时最终都会在对应的DraweeView中的Controller来调用。数组
至于这些方法中的实现细节,因为代码比较多,这里就不一一列举出来,你们能够自行查看GenericDraweeHierarchy的源码。缓存
上面的SettableDraweeHierarchy还有一个父类接口为DraweeHierarchy,在这个接口中只有一个接口方法为Drawable getTopLevelDrawable();。是否是对这个方法也有点熟悉呢?(路人甲:嗯,好像上篇文章说起过!)它是用来获取视图树的最顶层视图,其实说白了就是显示出来的Drawable。它主要在DraweeHolder中调用,最终也会由void setHierarchy(DH hierarchy) 与 void setController(@Nullable DraweeController draweeController) 来调用bash
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等相关图层。具体的咱们能够来看一下它里面实现的一些方法。
这里就拿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中的一些其它方法进行简要的说明:
主要的就这几个吧,其它的都已经在上面分析流程中详细说明了。
最后再整理一下Fresco中的一些相关的自定义的Drawable子类
本篇文章主要是分析Fresco中有关Hierarchy相关的实现与原理,经过分析发现Hierarchy中都是对Drawable图层进行处理,并无其它的缓存、请求之类的逻辑。因此若是你使用Fresco的时候只使用Hierarchy的话,就与别的ImageView没有多大的区别,真正的图层操做与缓存控制都在Controller中,因此下篇文章将进入Controller解析,来详细了解它的实现细节。