Fresco 网络请求在前面2篇介绍完了,我们来看看Fresco Hierarchy(view分层结构)
/**
* This view takes a uri as input and internally builds and sets a controller.
*
* <p>This class must be statically initialized in order to be used. If you are using the Fresco
* image pipeline, use {@link com.facebook.drawee.backends.pipeline.Fresco#initialize} to do this.
*/
public class SimpleDraweeView extends GenericDraweeView {
private static Supplier<? extends SimpleDraweeControllerBuilder> sDraweeControllerBuilderSupplier;
/** Initializes {@link SimpleDraweeView} with supplier of Drawee controller builders. */
public static void initialize(
Supplier<? extends SimpleDraweeControllerBuilder> draweeControllerBuilderSupplier) {
sDraweeControllerBuilderSupplier = draweeControllerBuilderSupplier;
}
/** Shuts {@link SimpleDraweeView} down. */
public static void shutDown() {
sDraweeControllerBuilderSupplier = null;
}
private SimpleDraweeControllerBuilder mSimpleDraweeControllerBuilder;
......
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SimpleDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
if (isInEditMode()) {
return;
}
Preconditions.checkNotNull(
sDraweeControllerBuilderSupplier,
"SimpleDraweeView was not initialized!");
mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
if (attrs != null) {
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.SimpleDraweeView);
try {
if (gdhAttrs.hasValue(R.styleable.SimpleDraweeView_actualImageUri)) {
setImageURI(Uri.parse(gdhAttrs.getString(R.styleable.SimpleDraweeView_actualImageUri)), null);
}
} finally {
gdhAttrs.recycle();
}
}
}
......
/**
* Displays an image given by the uri.
*
* @param uri uri of the image
* @param callerContext caller context
*/
public void setImageURI(Uri uri, @Nullable Object callerContext) {
DraweeController controller = mSimpleDraweeControllerBuilder
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}
}
这个类用于你界面布局,父类是imageview,他会自己创建DraweeController和自身绑定,关于Supplier是外部提供关于下载和维护,Cache提供的工厂类,在Frescoc:init()时候创建。
SimpleDraweeView用法:
感觉还是少了很多东西,接着我们看下基类:
/**
* DraweeView that uses GenericDraweeHierarchy.
*
* The hierarchy can be set either programmatically or inflated from XML.
* See {@link GenericDraweeHierarchyInflater} for supported XML attributes.
*/
public class GenericDraweeView extends DraweeView<GenericDraweeHierarchy> {
.......
public GenericDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
inflateHierarchy(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public GenericDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
inflateHierarchy(context, attrs);
}
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
GenericDraweeHierarchyBuilder builder =
GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);
setAspectRatio(builder.getDesiredAspectRatio());
setHierarchy(builder.build());
}
}
GenericDraweeView 说明是可支持可编程和支持xml布局属性写法的,最后它会初始化一个Hierarchy
来看看GenericDraweeHierarchyBuilder和GenericDraweeHierarchyInflater干了啥
GenericDraweeHierarchyBuilder:
/**
* Class to construct a {@link GenericDraweeHierarchy}.
*
* <p/> Drawables must not be reused by multiple hierarchies. Each hierarchy needs to have its own
* drawable instances. Since this builder does not do deep copies of the input parameters, it is
* the caller's responsibility to pass a different drawable instances for each hierarchy built.
* Likewise, hierarchies must not be reused by multiple views. Each view needs to have its own
* instance of a hierarchy. The caller is responsible for building a new hierarchy for each view.
*/
public class GenericDraweeHierarchyBuilder {
public static final int DEFAULT_FADE_DURATION = 300;
public static final ScaleType DEFAULT_SCALE_TYPE = ScaleType.CENTER_INSIDE;
public static final ScaleType DEFAULT_ACTUAL_IMAGE_SCALE_TYPE = ScaleType.CENTER_CROP;
private Resources mResources;
private int mFadeDuration;
private float mDesiredAspectRatio;
private Drawable mPlaceholderImage;
private @Nullable ScaleType mPlaceholderImageScaleType;
private Drawable mRetryImage;
private ScaleType mRetryImageScaleType;
private Drawable mFailureImage;
private ScaleType mFailureImageScaleType;
private Drawable mProgressBarImage;
private ScaleType mProgressBarImageScaleType;
private ScaleType mActualImageScaleType;
private Matrix mActualImageMatrix;
private PointF mActualImageFocusPoint;
private ColorFilter mActualImageColorFilter;
private Drawable mBackground;
private List<Drawable> mOverlays;
private Drawable mPressedStateOverlay;
private RoundingParams mRoundingParams;
public GenericDraweeHierarchyBuilder(Resources resources) {
mResources = resources;
init();
}
/**
* Initializes this builder to its defaults.
*/
private void init() {
mFadeDuration = DEFAULT_FADE_DURATION;
mDesiredAspectRatio = 0;
mPlaceholderImage = null;
mPlaceholderImageScaleType = DEFAULT_SCALE_TYPE;
mRetryImage = null;
mRetryImageScaleType = DEFAULT_SCALE_TYPE;
mFailureImage = null;
mFailureImageScaleType = DEFAULT_SCALE_TYPE;
mProgressBarImage = null;
mProgressBarImageScaleType = DEFAULT_SCALE_TYPE;
mActualImageScaleType = DEFAULT_ACTUAL_IMAGE_SCALE_TYPE;
mActualImageMatrix = null;
mActualImageFocusPoint = null;
mActualImageColorFilter = null;
mBackground = null;
mOverlays = null;
mPressedStateOverlay = null;
mRoundingParams = null;
}
/**
* Sets a background.
*
* @param background background drawable
* @return modified instance of this builder
*/
public GenericDraweeHierarchyBuilder setBackground(@Nullable Drawable background) {
mBackground = background;
return this;
}
private void validate() {
if (mOverlays != null) {
for (Drawable overlay : mOverlays) {
Preconditions.checkNotNull(overlay);
}
}
}
/**
* Builds the hierarchy.
*/
public GenericDraweeHierarchy build() {
validate();
return new GenericDraweeHierarchy(this);
}
}
这个类一看就是builder模式,一堆set和get方法与GenericDraweeHierarchy绑定
GenericDraweeHierarchyInflater:
/**
* Inflater for the {@code GenericDraweeHierarchy}.
* 解析AttributeSet
*/
public class GenericDraweeHierarchyInflater {
/**
* Inflates a new hierarchy builder from XML.
* The builder can then be modified in order to override XML attributes if necessary.
*/
public static GenericDraweeHierarchyBuilder inflateBuilder(
Context context,
@Nullable AttributeSet attrs) {
Resources resources = context.getResources();
GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources);
return updateBuilder(builder, context, attrs);
}
/**
* Updates the existing hierarchy builder based on the XML attributes.
*
* This method is useful if a custom view uses different default values. In that case a
* builder with adjusted default values can be passed to this method and only the properties
* explicitly specified in XML will be overridden.
* The builder can be modified afterwards in case some XML attributes needs to be overridden.
*
* @param builder a hierarchy builder to be updated
* @return the modified instance of the same builder
*/
public static GenericDraweeHierarchyBuilder updateBuilder(
GenericDraweeHierarchyBuilder builder,
Context context,
@Nullable AttributeSet attrs) {
// these paramters cannot be applied immediately so we store them first
int progressBarAutoRotateInterval = 0;
int roundedCornerRadius = 0;
boolean roundTopLeft = true;
boolean roundTopRight = true;
boolean roundBottomLeft = true;
boolean roundBottomRight = true;
if (attrs != null) {
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.GenericDraweeHierarchy);
try {
final int indexCount = gdhAttrs.getIndexCount();
for (int i = 0; i < indexCount; i++) {
final int attr = gdhAttrs.getIndex(i);
// most popular ones first
if (attr == R.styleable.GenericDraweeHierarchy_actualImageScaleType) {
builder.setActualImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_placeholderImage) {
builder.setPlaceholderImage(getDrawable(context, gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_pressedStateOverlayImage) {
builder.setPressedStateOverlay(getDrawable(context, gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_progressBarImage) {
builder.setProgressBarImage(getDrawable(context, gdhAttrs, attr));
// the remaining ones without any particular order
} else if (attr == R.styleable.GenericDraweeHierarchy_fadeDuration) {
builder.setFadeDuration(gdhAttrs.getInt(attr, 0));
} else if (attr == R.styleable.GenericDraweeHierarchy_viewAspectRatio) {
builder.setDesiredAspectRatio(gdhAttrs.getFloat(attr, 0));
} else if (attr == R.styleable.GenericDraweeHierarchy_placeholderImageScaleType) {
builder.setPlaceholderImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_retryImage) {
builder.setRetryImage(getDrawable(context, gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_retryImageScaleType) {
builder.setRetryImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_failureImage) {
builder.setFailureImage(getDrawable(context, gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_failureImageScaleType) {
builder.setFailureImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_progressBarImageScaleType) {
builder.setProgressBarImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_progressBarAutoRotateInterval) {
progressBarAutoRotateInterval =
gdhAttrs.getInteger(attr, progressBarAutoRotateInterval);
} else if (attr == R.styleable.GenericDraweeHierarchy_backgroundImage) {
builder.setBackground(getDrawable(context, gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_overlayImage) {
builder.setOverlay(getDrawable(context, gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_roundAsCircle) {
getRoundingParams(builder).setRoundAsCircle(gdhAttrs.getBoolean(attr, false));
} else if (attr == R.styleable.GenericDraweeHierarchy_roundedCornerRadius) {
roundedCornerRadius = gdhAttrs.getDimensionPixelSize(attr, roundedCornerRadius);
} else if (attr == R.styleable.GenericDraweeHierarchy_roundTopLeft) {
roundTopLeft = gdhAttrs.getBoolean(attr, roundTopLeft);
} else if (attr == R.styleable.GenericDraweeHierarchy_roundTopRight) {
roundTopRight = gdhAttrs.getBoolean(attr, roundTopRight);
} else if (attr == R.styleable.GenericDraweeHierarchy_roundBottomLeft) {
roundBottomLeft = gdhAttrs.getBoolean(attr, roundBottomLeft);
} else if (attr == R.styleable.GenericDraweeHierarchy_roundBottomRight) {
roundBottomRight = gdhAttrs.getBoolean(attr, roundBottomRight);
} else if (attr == R.styleable.GenericDraweeHierarchy_roundWithOverlayColor) {
getRoundingParams(builder).setOverlayColor(gdhAttrs.getColor(attr, 0));
} else if (attr == R.styleable.GenericDraweeHierarchy_roundingBorderWidth) {
getRoundingParams(builder).setBorderWidth(gdhAttrs.getDimensionPixelSize(attr, 0));
} else if (attr == R.styleable.GenericDraweeHierarchy_roundingBorderColor) {
getRoundingParams(builder).setBorderColor(gdhAttrs.getColor(attr, 0));
} else if (attr == R.styleable.GenericDraweeHierarchy_roundingBorderPadding) {
getRoundingParams(builder).setPadding(gdhAttrs.getDimensionPixelSize(attr, 0));
}
}
} finally {
gdhAttrs.recycle();
}
}
// wrap progress bar if auto-rotating requested
if (builder.getProgressBarImage() != null && progressBarAutoRotateInterval > 0) {
builder.setProgressBarImage(
new AutoRotateDrawable(builder.getProgressBarImage(), progressBarAutoRotateInterval));
}
// set rounded corner radii if requested
if (roundedCornerRadius > 0) {
getRoundingParams(builder).setCornersRadii(
roundTopLeft ? roundedCornerRadius : 0,
roundTopRight ? roundedCornerRadius : 0,
roundBottomRight ? roundedCornerRadius : 0,
roundBottomLeft ? roundedCornerRadius : 0);
}
return builder;
}
.......
/**
* Returns the scale type indicated in XML, or null if the special 'none' value was found.
* Important: these values need to be in sync with GenericDraweeHierarchy styleable attributes.
*/
@Nullable
private static ScaleType getScaleTypeFromXml(
TypedArray gdhAttrs,
int attrId) {
switch (gdhAttrs.getInt(attrId, -2)) {
case -1: // none
return null;
case 0: // fitXY
return ScaleType.FIT_XY;
case 1: // fitStart
return ScaleType.FIT_START;
case 2: // fitCenter
return ScaleType.FIT_CENTER;
case 3: // fitEnd
return ScaleType.FIT_END;
case 4: // center
return ScaleType.CENTER;
case 5: // centerInside
return ScaleType.CENTER_INSIDE;
case 6: // centerCrop
return ScaleType.CENTER_CROP;
case 7: // focusCrop
return ScaleType.FOCUS_CROP;
default:
// this method is supposed to be called only when XML attribute is specified.
throw new RuntimeException("XML attribute not specified!");
}
}
}
这个类主要干xml中拿AttributeSet解析,没有包括java代码自己手动动态设置的。
好了通过builder模式我们拿到GenericDraweeHierarchy
GenericDraweeHierarchy:
/**
* A SettableDraweeHierarchy that displays placeholder image until the actual image is set.
* If provided, failure image will be used in case of failure (placeholder otherwise).
* If provided, retry image will be used in case of failure when retrying is enabled.
* If provided, progressbar will be displayed until fully loaded.
* Each image can be displayed with a different scale type (or no scaling at all).
* Fading between the layers is supported. Rounding is supported.
*
* <p>
* Example hierarchy with a placeholder, retry, failure and the actual image:
* <pre>
* 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)
* </pre>
*
* <p>
* Note:
* <ul>
* <li> RootDrawable and FadeDrawable are always created.
* <li> All branches except the actual image branch are optional (placeholder, failure, retry,
* progress bar). If some branch is not specified it won't be created. Index in FadeDrawable will
* still be reserved though.
* <li> If overlays and/or background are specified, they are added to the same fade drawable, and
* are always being displayed.
* <li> ScaleType and Matrix transformations will be added only if specified. If both are
* unspecified, then the branch for that image is attached to FadeDrawable directly. Matrix
* transformation is only supported for the actual image, and it is not recommended to be used.
* <li> Rounding, if specified, is applied to all layers. Rounded drawable can either wrap
* FadeDrawable, or if leaf rounding is specified, each leaf drawable will be rounded separately.
* <li> A particular drawable instance should be used by only one DH. If more than one DH is being
* built with the same builder, different drawable instances must be specified for each DH.
* </ul>
*/
public class GenericDraweeHierarchy implements SettableDraweeHierarchy {
private static final int BACKGROUND_IMAGE_INDEX = 0;
private static final int PLACEHOLDER_IMAGE_INDEX = 1;
private static final int ACTUAL_IMAGE_INDEX = 2;
private static final int PROGRESS_BAR_IMAGE_INDEX = 3;
private static final int RETRY_IMAGE_INDEX = 4;
private static final int FAILURE_IMAGE_INDEX = 5;
private static final int OVERLAY_IMAGES_INDEX = 6;
private final Drawable mEmptyActualImageDrawable = new ColorDrawable(Color.TRANSPARENT);
private final Resources mResources;
private @Nullable RoundingParams mRoundingParams;
private final RootDrawable mTopLevelDrawable;
private final FadeDrawable mFadeDrawable;
private final ForwardingDrawable mActualImageWrapper;
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.getActualImageMatrix(),
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();
}
@Nullable
private Drawable buildActualImageBranch(
Drawable drawable,
@Nullable ScaleType scaleType,
@Nullable PointF focusPoint,
@Nullable Matrix matrix,
@Nullable ColorFilter colorFilter) {
drawable.setColorFilter(colorFilter);
drawable = WrappingUtils.maybeWrapWithScaleType(drawable, scaleType, focusPoint);
drawable = WrappingUtils.maybeWrapWithMatrix(drawable, matrix);
return drawable;
}
/** Applies scale type and rounding (both if specified). */
@Nullable
private Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) {
drawable = WrappingUtils.maybeApplyLeafRounding(drawable, mRoundingParams, mResources);
drawable = WrappingUtils.maybeWrapWithScaleType(drawable, scaleType);
return drawable;
}
private void fadeOutBranches() {
fadeOutLayer(PLACEHOLDER_IMAGE_INDEX);
fadeOutLayer(ACTUAL_IMAGE_INDEX);
fadeOutLayer(PROGRESS_BAR_IMAGE_INDEX);
fadeOutLayer(RETRY_IMAGE_INDEX);
fadeOutLayer(FAILURE_IMAGE_INDEX);
}
@Override
public Drawable getTopLevelDrawable() {
return mTopLevelDrawable;
}
.......
@Override
public void setRetry(Throwable throwable) {
mFadeDrawable.beginBatchMode();
fadeOutBranches();
if (mFadeDrawable.getDrawable(RETRY_IMAGE_INDEX) != null) {
fadeInLayer(RETRY_IMAGE_INDEX);
} else {
fadeInLayer(PLACEHOLDER_IMAGE_INDEX);
}
mFadeDrawable.endBatchMode();
}
@Override
public void setControllerOverlay(@Nullable Drawable drawable) {
mTopLevelDrawable.setControllerOverlay(drawable);
}
// Helper methods for accessing layers
/**
* Gets the lowest parent drawable for the layer at the specified index.
*
* Following drawables are considered as parents: FadeDrawable, MatrixDrawable, ScaleTypeDrawable.
* This is because those drawables are added automatically by the hierarchy (if specified),
* whereas their children are created externally by the client code. When we need to change the
* previously set drawable this is the parent whose child needs to be replaced.
*/
private DrawableParent getParentDrawableAtIndex(int index) {
DrawableParent parent = mFadeDrawable.getDrawableParentForIndex(index);
if (parent.getDrawable() instanceof MatrixDrawable) {
parent = (MatrixDrawable) parent.getDrawable();
}
if (parent.getDrawable() instanceof ScaleTypeDrawable) {
parent = (ScaleTypeDrawable) parent.getDrawable();
}
return parent;
}
/**
* Sets the drawable at the specified index while keeping the old scale type and rounding.
* In case the given drawable is null, scale type gets cleared too.
*/
private void setChildDrawableAtIndex(int index, @Nullable Drawable drawable) {
if (drawable == null) {
mFadeDrawable.setDrawable(index, null);
return;
}
drawable = WrappingUtils.maybeApplyLeafRounding(drawable, mRoundingParams, mResources);
getParentDrawableAtIndex(index).setDrawable(drawable);
}
/**
* Gets the ScaleTypeDrawable at the specified index.
* In case there is no child at the specified index, a NullPointerException is thrown.
* In case there is a child, but the ScaleTypeDrawable does not exist,
* the child will be wrapped with a new ScaleTypeDrawable.
*/
private ScaleTypeDrawable getScaleTypeDrawableAtIndex(int index) {
DrawableParent parent = getParentDrawableAtIndex(index);
if (parent instanceof ScaleTypeDrawable) {
return (ScaleTypeDrawable) parent;
} else {
return WrappingUtils.wrapChildWithScaleType(parent, ScaleType.FIT_XY);
}
}
.......
}
这个类实现SettableDraweeHierarchy,这货将hold住一个 placeholder image(默认图),直到新图被装备好替换上来,这个类可以支持failure image(下载失败)和retry image(下载重试)和 progressbar 更新数据,
这个类构造方法就是把层次分好,一层层绘制。
最后GenericDraweeView会把SettableDraweeHierarchy设置到AbstractDraweeController抽象父类中,实现类是PipelineDraweeController,在sumbit()订阅回掉中,更新界面数据和层次绘制。
接着再看基类:
/**
* View that displays a {@link DraweeHierarchy}.
*
* <p> Hierarchy should be set prior to using this view. See {@code setHierarchy}. Because creating
* a hierarchy is an expensive operation, it is recommended this be done once per view, typically
* near creation time.
*
* <p> In order to display an image, controller has to be set. See {@code setController}.
* <p> Although ImageView is subclassed instead of subclassing View directly, this class does not
* support ImageView's setImageXxx, setScaleType and similar methods. Extending ImageView is a short
* term solution in order to inherit some of its implementation (padding calculations, etc.).
* This class is likely to be converted to extend View directly in the future, so avoid using
* ImageView's methods and properties.
*/
public class DraweeView<DH extends DraweeHierarchy> extends ImageView {
private final AspectRatioMeasure.Spec mMeasureSpec = new AspectRatioMeasure.Spec();
private float mAspectRatio = 0;
private DraweeHolder<DH> mDraweeHolder;
private boolean mInitialised = false;
......
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context,attrs,defStyleAttr,defStyleRes);
init(context);
}
/** This method is idempotent so it only has effect the first time it's called */
private void init(Context context) {
if (mInitialised) {
return;
}
mInitialised = true;
mDraweeHolder = DraweeHolder.create(null, context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ColorStateList imageTintList = getImageTintList();
if (imageTintList == null) {
return;
}
setColorFilter(imageTintList.getDefaultColor());
}
}
/** Sets the hierarchy. */
public void setHierarchy(DH hierarchy) {
mDraweeHolder.setHierarchy(hierarchy);
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}
/** Returns whether the hierarchy is set or not. */
public boolean hasHierarchy() {
return mDraweeHolder.hasHierarchy();
}
/** Gets the top-level drawable if hierarchy is set, null otherwise. */
@Nullable public Drawable getTopLevelDrawable() {
return mDraweeHolder.getTopLevelDrawable();
}
/** Sets the controller. */
public void setController(@Nullable DraweeController draweeController) {
mDraweeHolder.setController(draweeController);
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
onAttach();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
onDetach();
}
@Override
public void onStartTemporaryDetach() {
super.onStartTemporaryDetach();
onDetach();
}
@Override
public void onFinishTemporaryDetach() {
super.onFinishTemporaryDetach();
onAttach();
}
........
@Override
@Deprecated
public void setImageURI(Uri uri) {
init(getContext());
mDraweeHolder.setController(null);
super.setImageURI(uri);
}
}
这个类是imageview包装,但没有实现imageview方法和属性,为了未来的扩展,创建好了时候,必须要把Hierarchy创建好,这个类接收IMS和WMS事件回掉了,开启生命周期维护和任务
这个类还有个重要成员属性代理类DraweeHolder,类似Activity中mInstrumentation执行一样
总结:SimpleDraweeView相当与包装了ImageView, 自身通过设置controller和PipelineDraweeController类绑定,自身接收ims和wsm事件通过DraweeHolder向controller类中安排任务,controller通过订阅的模式向DataSource(model层)生成回调接口,再通过Hierarchy去控制刷新SimpleDraweeView的layer层次view树