Fresco:SimpleDraweeView如何顯示並載入圖片
使用Fresco, 如果僅僅是想簡單下載一張網路圖片,在下載完成之前,顯示一張佔位圖,那麼簡單使用 SimpleDraweeView 即可。那麼SimpleDraweeView是如何如何顯示並載入圖片呢?
我們知道在使用Fresco前,需要呼叫
Fresco.initialize(context);
Fresco.initialize做哪些工作
該函式有兩個實現:
/** Initializes Fresco with the default config. */
public static void initialize(Context context) {
ImagePipelineFactory.initialize(context);
initializeDrawee(context);
}
/** Initializes Fresco with the specified config. */
public static void initialize(Context context, ImagePipelineConfig imagePipelineConfig) {
ImagePipelineFactory.initialize(imagePipelineConfig);
initializeDrawee(context);
}
第一個無需配置ImagePipelineConfig,這種情況下使用預設配置。會構造一個只包含mContext值的ImagePipelineConfig物件,該物件中其他成員變數為空。
該函式主要做兩件事,第一初始化ImagePipelineFactory,第二初始化Drawee:將使用配置的ImagePipelineConfig(預設的,或使用自定義引數的)構造的ImagePipeline賦值給Fresco的靜態成員變數sDraweeControllerBuilderSupplier 的成員變數mImagePipeline.
private static void initializeDrawee(Context context) {
sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
}
然後使用sDraweeControllerBuilderSupplier初始化SimpleDraweeView。sDraweeControllerBuilderSupplier的型別為:
public class PipelineDraweeControllerBuilderSupplier implements
Supplier
SimpleDraweeView的建構函式
SimpleDraweeView的建構函式會呼叫init()函式,該函式會初始化成員變數mSimpleDraweeControllerBuilder ,型別為:SimpleDraweeControllerBuilder。
private void init() {
if (isInEditMode()) {
return;
}
Preconditions.checkNotNull(
sDraweeControllerBuilderSupplier,
"SimpleDraweeView was not initialized!");
mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
}
sDraweeControllerBuilderSupplier.get()返回PipelineDraweeControllerBuilder型別的資料,該類繼承AbstractDraweeControllerBuilder類,後者繼承了SimpleDraweeControllerBuilder介面。
@Override
public PipelineDraweeControllerBuilder get() {
return new PipelineDraweeControllerBuilder(
mContext,
mPipelineDraweeControllerFactory,
mImagePipeline,
mBoundControllerListeners);
}
SimpleDraweeView::setImageURI()
我們通過setImageURI為呼叫SimpleDraweeView設定圖片,
該函式分兩部分,第一建立DraweeController ,第二設定Controller.
建立DraweeController
通過呼叫mSimpleDraweeControllerBuilder的build()函式實現,程式碼如下:
/**
* 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);
}
即呼叫drawee\src\main\java\com\facebook\drawee\controller\AbstractDraweeControllerBuilder.java 的build():
/** Builds the specified controller. */
@Override
public AbstractDraweeController build() {
validate();
// if only a low-res request is specified, treat it as a final request.
if (mImageRequest == null && mMultiImageRequests == null && mLowResImageRequest != null) {
mImageRequest = mLowResImageRequest;
mLowResImageRequest = null;
}
return buildController();
}
/** Builds a regular controller. */
protected AbstractDraweeController buildController() {
AbstractDraweeController controller = obtainController();
controller.setRetainImageOnFailure(getRetainImageOnFailure());
maybeBuildAndSetRetryManager(controller);
maybeAttachListeners(controller);
return controller;
}
obtainController()的實現在PipelineDraweeControllerBuilder.java中:
@Override
protected PipelineDraweeController obtainController() {
DraweeController oldController = getOldController();
PipelineDraweeController controller;
if (oldController instanceof PipelineDraweeController) {
controller = (PipelineDraweeController) oldController;
controller.initialize(
obtainDataSourceSupplier(),
generateUniqueControllerId(),
getCallerContext());
} else {
controller = mPipelineDraweeControllerFactory.newController(
obtainDataSourceSupplier(),
generateUniqueControllerId(),
getCallerContext());
}
return controller;
}
呼叫AbstractDraweeControllerBuilder.java的obtainDataSourceSupplier():
/** Gets the top-level data source supplier to be used by a controller. */
protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier() {
if (mDataSourceSupplier != null) {
return mDataSourceSupplier;
}
Supplier<DataSource<IMAGE>> supplier = null;
// final image supplier;
if (mImageRequest != null) {
supplier = getDataSourceSupplierForRequest(mImageRequest);
} else if (mMultiImageRequests != null) {
supplier = getFirstAvailableDataSourceSupplier(mMultiImageRequests, mTryCacheOnlyFirst);
}
// increasing-quality supplier; highest-quality supplier goes first
if (supplier != null && mLowResImageRequest != null) {
List<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList<>(2);
suppliers.add(supplier);
suppliers.add(getDataSourceSupplierForRequest(mLowResImageRequest));
supplier = IncreasingQualityDataSourceSupplier.create(suppliers);
}
// no image requests; use null data source supplier
if (supplier == null) {
supplier = DataSources.getFailedDataSourceSupplier(NO_REQUEST_EXCEPTION);
}
return supplier;
}
/** Creates a data source supplier for the given image request. */
protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
final REQUEST imageRequest,
final boolean bitmapCacheOnly) {
final Object callerContext = getCallerContext();
return new Supplier<DataSource<IMAGE>>() {
@Override
public DataSource<IMAGE> get() {
return getDataSourceForRequest(imageRequest, callerContext, bitmapCacheOnly);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("request", imageRequest.toString())
.toString();
}
};
}
最後通過PipelineDraweeControllerBuilder.java的getDataSourceForRequest()返回DataSourceSupplier。該DataSourceSupplier的get()會呼叫getDataSourceForRequest()將獲取到的圖片資料返回,返回資料型別為:DataSource
@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
ImageRequest imageRequest,
Object callerContext,
boolean bitmapCacheOnly) {
if (bitmapCacheOnly) {
return mImagePipeline.fetchImageFromBitmapCache(imageRequest, callerContext);
} else {
return mImagePipeline.fetchDecodedImage(imageRequest, callerContext);
}
}
設定Controller
接著回到 setImageURI函式中的 setController(controller)的實現:
/** Sets the controller. */
public void setController(@Nullable DraweeController draweeController) {
mDraweeHolder.setController(draweeController);
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}
呼叫DraweeHolder.java的setController()函式:
/**
* Sets a new controller.
*/
public void setController(@Nullable DraweeController draweeController) {
boolean wasAttached = mIsControllerAttached;
if (wasAttached) {
detachController();
}
// Clear the old controller
if (mController != null) {
mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER);
mController.setHierarchy(null);
}
mController = draweeController;
if (mController != null) {
mEventTracker.recordEvent(Event.ON_SET_CONTROLLER);
mController.setHierarchy(mHierarchy);
} else {
mEventTracker.recordEvent(Event.ON_CLEAR_CONTROLLER);
}
if (wasAttached) {
attachController();
}
}
該函式主要的功能是呼叫mController.setHierarchy(mHierarchy);關於mHierarchy,是在SimpleDraweeView的父類GenericDraweeView的建構函式中設定的:
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
GenericDraweeHierarchyBuilder builder =
GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);
setAspectRatio(builder.getDesiredAspectRatio());
setHierarchy(builder.build());
}
根據我們在XML中宣告的SimpleDraweeView的一些屬性建立GenericDraweeHierarchyBuilder,然後呼叫它的build()函式生成GenericDraweeHierarchy:
/**
* 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;
}
再返回setController(controller)中,可以看到呼叫 super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()),即ImageView的setImageDrawable函式,讓圖片在SimpleDraweeView中顯示。
GenericDraweeHierarchy
這是Fresco的特點,每個SimpleDraweeView設定的圖片都是RootDrawable型別的,對應GenericDraweeHierarchy的mTopLevelDrawable成員變數,而GenericDraweeHierarchy是類似下圖的hierachy:
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)
在該類的建構函式中我們可以看到對上述各層圖片的建立過程:
GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) {
mResources = builder.getResources();
mRoundingParams = builder.getRoundingParams();
mActualImageWrapper = new ForwardingDrawable(mEmptyActualImageDrawable);
int numBackgrounds = (builder.getBackgrounds() != null) ? builder.getBackgrounds().size() : 0;
int numOverlays = (builder.getOverlays() != null) ? builder.getOverlays().size() : 0;
numOverlays += (builder.getPressedStateOverlay() != null) ? 1 : 0;
// layer indices and count
int numLayers = 0;
int backgroundsIndex = numLayers;
numLayers += numBackgrounds;
mPlaceholderImageIndex = numLayers++;
mActualImageIndex = numLayers++;
mProgressBarImageIndex = numLayers++;
mRetryImageIndex = numLayers++;
mFailureImageIndex = numLayers++;
int overlaysIndex = numLayers;
numLayers += numOverlays;
// array of layers
Drawable[] layers = new Drawable[numLayers];
if (numBackgrounds > 0) {
int index = 0;
for (Drawable background : builder.getBackgrounds()) {
layers[backgroundsIndex + index++] = buildBranch(background, null);
}
}
layers[mPlaceholderImageIndex] = buildBranch(
builder.getPlaceholderImage(),
builder.getPlaceholderImageScaleType());
layers[mActualImageIndex] = buildActualImageBranch(
mActualImageWrapper,
builder.getActualImageScaleType(),
builder.getActualImageFocusPoint(),
builder.getActualImageMatrix(),
builder.getActualImageColorFilter());
layers[mProgressBarImageIndex] = buildBranch(
builder.getProgressBarImage(),
builder.getProgressBarImageScaleType());
layers[mRetryImageIndex] = buildBranch(
builder.getRetryImage(),
builder.getRetryImageScaleType());
layers[mFailureImageIndex] = buildBranch(
builder.getFailureImage(),
builder.getFailureImageScaleType());
if (numOverlays > 0) {
int index = 0;
if (builder.getOverlays() != null) {
for (Drawable overlay : builder.getOverlays()) {
layers[overlaysIndex + index++] = buildBranch(overlay, null);
}
}
if (builder.getPressedStateOverlay() != null) {
layers[overlaysIndex + 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();
}
在獲取到圖片後,只需呼叫該類的setImage()函式將圖片設定給mActualImageWrapper即可:
@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(mActualImageIndex);
setProgress(progress);
if (immediate) {
mFadeDrawable.finishTransitionImmediately();
}
mFadeDrawable.endBatchMode();
}
上述設定圖片的過程是在com/facebook/drawee/controller/AbstractDraweeController.java中定義的:
protected void submitRequest() {
...
final DataSubscriber<T> dataSubscriber =
new BaseDataSubscriber<T>() {
@Override
public void onNewResultImpl(DataSource<T> dataSource) {
// isFinished must be obtained before image, otherwise we might set intermediate result
// as final image.
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
T image = dataSource.getResult();
if (image != null) {
onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);
} else if (isFinished) {
onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
}
}
...
}
private void onNewResultInternal(
String id,
DataSource<T> dataSource,
@Nullable T image,
float progress,
boolean isFinished,
boolean wasImmediate) {
...
try {
drawable = createDrawable(image);
} catch (Exception exception) {
logMessageAndImage("drawable_failed @ onNewResult", image);
releaseImage(image);
onFailureInternal(id, dataSource, exception, isFinished);
return;
}
T previousImage = mFetchedImage;
Drawable previousDrawable = mDrawable;
mFetchedImage = image;
mDrawable = drawable;
try {
// set the new image
if (isFinished) {
logMessageAndImage("set_final_result @ onNewResult", image);
mDataSource = null;
mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
// IMPORTANT: do not execute any instance-specific code after this point
...
}
其中mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);將圖片設定到SimpleDraweeView對應的DraweeHierarchy中。