摘要:最終的顯隱操作都會轉(zhuǎn)化為在方法中進行操作。主要是通過與數(shù)組來控制數(shù)組中各個的值,即顯隱它繼承于,顧名思義通過矩陣來改變狀態(tài)。
上篇文章我們分析了Fresco中的DraweeView,對其中的一些原理以及方法進行了解析。在這過程中我們了解到,DraweeView中是通過DraweeHolder來統(tǒng)一管理的。而DraweeHolder又是用來統(tǒng)一管理相關(guān)的Hierarchy與Controller,如果想了解DraweeView相關(guān)的知識,可以先看下我的前一篇文章Fresco源碼分析之DraweeView。今天這里進一步來分析Fresco中的Hierarchy。
GenericDraweeHierarchyBuilder在GenericDraweeView的構(gòu)造方法中會調(diào)用inflateHierarchy(context, atts)方法來創(chuàng)建一個GenericDraweeHierarchyBuilder對象,通過調(diào)用該對象的build方法來生成一個Hierarchy。
如果你看了我上一篇文章,相信對這個方法你會感到很親切。
所以這個類的主要作用是用來創(chuàng)建一個Hierarchy,通過builder模式來實例化包含相關(guān)信息的Hierarchy。如果你一步步深入了解Fresco的話,相信你對builder模式也將習以為常,因為后面你將經(jīng)常與它碰面。這也是Fresco開源項目的主要設(shè)計模式。在該builder類中主要構(gòu)建的信息有:
Drawable相關(guān):placeholderImage、retryImage、failureImage、progressBarImage、background、overlays與pressedStateOverlay
ScaleType相關(guān):與Drawable相對應的placeholderImageScaleType、retryImageScaleType等等。
其它:fadeDuration漸變過渡時間與RoundingParams圓角相關(guān)信息等等
GenericDraweeHierarchy通過上面的GenericDraweeHierarchyBuilder的build方法會創(chuàng)建一個GenericDraweeHierarchy對象。這就是我們今天需要主要分析的類,也是Fresco的一個核心類。
SettableDraweeHierarchy首先我們來分析一下它的類結(jié)構(gòu),它會實現(xiàn)SettableDraweeHierarchy接口,我們進入該接口發(fā)現(xiàn)一共有6個接口方法,它們分別為:
void reset(); 重新初始化Hierarchy
void setImage(Drawable drawable, float progress, boolean immediate); 設(shè)置實際需要展示的圖片,其中progress表示圖片的加載質(zhì)量進度(在漸進式中會使用到)
void setProgress(float progress, boolean immediate); 更新圖片加載進度
void setFailure(Throwable throwable); 圖片加載失敗時調(diào)用,可以設(shè)置failureImage
void setRetry(Throwable throwable); 當圖片加載失敗時重新進行加載,可以設(shè)置retryImage
void setControllerOverlay(Drawable drawable); 用來設(shè)置圖層覆蓋
這些方法在GenericDraweeHierarchy中都會做出相應的實現(xiàn),同時最終都會在對應的DraweeView中的Controller來調(diào)用。
至于這些方法中的實現(xiàn)細節(jié),由于代碼比較多,這里就不一一列舉出來,大家可以自行查看GenericDraweeHierarchy的源碼。DraweeHierarchy
上面的SettableDraweeHierarchy還有一個父類接口為DraweeHierarchy,在這個接口中只有一個接口方法為Drawable getTopLevelDrawable();。是不是對這個方法也有點熟悉呢?(路人甲:嗯,好像上篇文章提及過!)它是用來獲取視圖樹的最頂層視圖,其實說白了就是顯示出來的Drawable。它主要在DraweeHolder中調(diào)用,最終也會由void setHierarchy(DH hierarchy) 與 void setController(@Nullable DraweeController draweeController) 來調(diào)用
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
根據(jù)上面所展示的層次結(jié)構(gòu),我們可以發(fā)現(xiàn)最底層是由RootDrawable來構(gòu)成,它有一個直接子分支為FadeDrawable。而在FadeDrawable中又有5個直接子分支,分別為placeholder branch、actual image branch、progressBar image branch、retry image branch與failure image branch。至于這些image的作用相信不用我再多做說明了,這些image除了actual image是必須要明確指定的,其它的都是可選擇的配置。因此RootDrawable與FadeDrawable是一定存在的。雖然其它的都是可選的配置,但無論你是否選擇了,它們的層級結(jié)構(gòu)都會保留在圖層樹中。還有每一個層級都有自己獨立的scale type,當然rounding(圓角)也是支持的。
其實除了這5個分支,與它們同一層次的還有background image與overlay image,它們分別位于placeholder image之前與failure image之后。background相信都知道,因為圖片都支持background與src,overlay為圖層覆蓋。至于為什么沒有在上面的結(jié)構(gòu)中顯示,我這里也不得而知,猜測可能是這兩個并不是Fresco主要常用的特性。
那么這些圖層結(jié)構(gòu)是通過layers數(shù)組來體現(xiàn)的,可以來看下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(); }
根據(jù)源碼可以明顯的看出不管overlay是否存在,它都會保留圖層層次,當然如果存在的話,就根據(jù)實際情況在OVERLAY_IMAGES_INDEX之后增加圖層層次。layers是一個Drawable數(shù)組,這里通過Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 方法來創(chuàng)建對應的Drawable。在最后,創(chuàng)建了FadeDrawable并將layers傳遞給它,在這里FadeDrawable的特性應該有點眉目了吧,其實它內(nèi)部做的就是對Drawable數(shù)組進行操作。之后RootDrawable也出現(xiàn)了(關(guān)于Drawable文章后面會統(tǒng)一分析),這樣之前所提到的層級結(jié)構(gòu)就形成了。
既然actual image是一定存在的,那么在它真正展示之前image中的顯示用什么來控制的呢?其實就是我們之前所提到的SettableDraweeHierarchy來控制。在真實的圖片展示出來之前,它可以用來展示placeholder image等相關(guān)圖層。具體的我們可以來看一下它里面實現(xiàn)的一些方法。
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工具類并且結(jié)合RoundingParams參數(shù)來重新生成一個可以附帶圓角的Drawable。然后將新的Drawable交由mActualImageWrapper,結(jié)合上面的層次結(jié)果分析,會很容易知道這就是需要真實顯示的圖層。它是一個ForwardingDrawable類型,該類主要是對傳入的目標Drawable進行相應的原生方法操作。下一步調(diào)用FadeDrawable的beginBatchMode()方法,該方法的作用主要為了圖層批處理做標記,防止在批處理時進行invalidate操作。直到最后的endBatchMode()方法調(diào)用之后才標識著圖層批處理操作結(jié)束。該批處理操作的目的是將actual image圖層顯示出來,所以首先調(diào)用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()操作,繼續(xù)進入fadeOutLayer()方法
private void fadeOutLayer(int index) { if (index >= 0) { mFadeDrawable.fadeOutLayer(index); } }
發(fā)現(xiàn)也很簡單,無非就是調(diào)用了FadeDrawable的fadeOutLayer()方法。
public void fadeOutLayer(int index) { mTransitionState = TRANSITION_STARTING; mIsLayerOn[index] = false; invalidateSelf(); }
在之前已經(jīng)提到過FadeDrawable本質(zhì)上可以理解為時一個Drawable數(shù)組,內(nèi)部都是圍繞著數(shù)組整體進行操作。對應的還有fadeInLayer()
private void fadeInLayer(int index) { if (index >= 0) { mFadeDrawable.fadeInLayer(index); } }
在FadeDrawable中除了用來保存相應的Drawable數(shù)組mLayers,還有與其相對應的mIsLayerOn布爾數(shù)組,該數(shù)組用來標識各個Hierarchy中的圖層是否需要展示。所以fadeOutLayer()是對index處的圖層進行隱藏標識。最終的顯隱操作都會轉(zhuǎn)化為在void draw(Canvas canvas)方法中進行alpha操作。
那么再回到之前的setImage()方法中,fadeOutBranches()是對相關(guān)的圖層進行隱藏標識,然后再通過fadeInLayer(ACTUAL_IMAGE_INDEX)方法改變actual image圖層的標識,將它改變成顯示狀態(tài)。最后如果有progressBar image圖層的話,也將會由setProgress(progress)方法來體現(xiàn)。immediate是用來判斷是否之后的顯隱操作立馬實現(xiàn)或者漸變過渡實現(xiàn)(內(nèi)部就是對alpha進行百分比操作,內(nèi)部有個mDurationMs,該時間值也是文章開頭提到的builder中的fadeDuration)。這樣整個的實際圖片展示流程我們已經(jīng)分析完畢,所以Hierarchy中最重要的還是對圖層概念的理解。下面再對GenericDraweeHierarchy中的一些其它方法進行簡要的說明:
Drawable buildActualImageBranch(Drawable drawable, @Nullable ScaleType scaleType, @Nullable PointF focusPoint, @Nullable ColorFilter colorFilter) 構(gòu)建實際展示圖片的圖層分支,內(nèi)部對于Drawable的創(chuàng)建還是借助WrappingUtils工具類
Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 構(gòu)建除實際展示圖片之外的其它圖層分支,Drawable的創(chuàng)建也是借助WrappingUtils工具類
void resetFade() 初始化圖層結(jié)構(gòu)
主要的就這幾個吧,其它的都已經(jīng)在上面分析流程中詳細說明了。Drawable
最后再整理一下Fresco中的一些相關(guān)的自定義的Drawable子類
ArrayDrawable:Drawable數(shù)組的集合體,通過layers數(shù)組來管理Drawable,內(nèi)部的都是對數(shù)組集合中的每一個Drawable進行操作,類似與Android原生的LayerDrawable,只是它并不支持add與remove操作
ForwardingDrawable:對傳入的目標Drawable即操作對象進行封裝處理,該新類的方法可以調(diào)用目標對象對應的方法,同時保留目標Drawable的各個狀態(tài),不依賴與目標類的細節(jié)實現(xiàn),提高新類的穩(wěn)定性,該方式可以稱之為復合。Fresco中絕大多數(shù)自定義Drawable都是它的子類。
AutoRotateDrawable:它繼承于ForwardingDrawable,實現(xiàn)的是對Drawable的旋轉(zhuǎn)操作
FadeDrawable:它繼承于ArrayDrawable,之前也詳細提到過,Hierarchy中的主要圖層集合體。主要是通過mLayers與mIsLayerOn數(shù)組來控制數(shù)組中各個Drawable的alpha值,即顯隱
MatrixDrawable:它繼承于ForwardingDrawable,顧名思義通過矩陣來改變Drawable狀態(tài)。
OrientedDrawable:它也繼承于ForwardingDrawable,它不同于AutoRotateDrawable的是,它只支持90度的倍數(shù)角度旋轉(zhuǎn)。
ProgressBarDrawable:進度條Drawable,支持橫豎方向。
RoundedBitmapDrawable:繼承于BitmapDrawable,根據(jù)Bitmap來創(chuàng)建有關(guān)圓角的Drawable,主要在WrappingUtils類中使用,用例構(gòu)建全新的圓角Drawable
RoundedColorDrawable:繼承于Drawable,根據(jù)Color來創(chuàng)建圓角Drawable,主要在WrappingUtils類中使用,用例構(gòu)建全新的圓角Drawable
RoundedCornersDrawable:繼承于ForwardingDrawable,用于設(shè)計overLay覆蓋圖片圓角,主要在WrappingUtils類中使用
ScaleTypeDrawable:繼承于ForwardingDrawable,用于縮放類型的Drawable
End本篇文章主要是分析Fresco中有關(guān)Hierarchy相關(guān)的實現(xiàn)與原理,通過分析發(fā)現(xiàn)Hierarchy中都是對Drawable圖層進行處理,并沒有其它的緩存、請求之類的邏輯。所以如果你使用Fresco的時候只使用Hierarchy的話,就與別的ImageView沒有多大的區(qū)別,真正的圖層操作與緩存控制都在Controller中,所以下篇文章將進入Controller解析,來詳細了解它的實現(xiàn)細節(jié)。
Fresco源碼分析系列Github地址
關(guān)注 RecommendAndroid共享動畫兼容實現(xiàn)
Kotlin最佳實踐
RecyclerView下拉刷新與上拉更多
Android高仿微信之mvp實現(xiàn)(四)
php與android的簡單交互
tensorflow-梯度下降,有這一篇就足夠了
博客
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70792.html
摘要:如果你是第一次看我的的源碼分析系列文章,這里強烈推薦你先閱讀我的前面兩篇文章源碼分析之與源碼分析之。其中是用來管理推遲資源釋放的。發(fā)送預處理請求,獲取相應類型的緩存數(shù)據(jù)。當數(shù)據(jù)源已經(jīng)獲取到時,發(fā)送通知給訂閱者,因此分別回調(diào)訂閱者的方法。 如果你是第一次看我的Fresco的源碼分析系列文章,這里強烈推薦你先閱讀我的前面兩篇文章Fresco源碼分析之DraweeView與Fresco源碼分...
摘要:首先這是對的源碼分析,所以在看這篇文章之前你應該要有使用的基礎(chǔ),如果沒有的強烈推薦看下官方文檔。在中統(tǒng)一由來替代。關(guān)于后續(xù)文章會詳細分析。在其內(nèi)部的,是用來記錄事件的傳遞,方便的調(diào)試。這次主要是分析了中的基本組件與它的子類。 在Android中圖片加載的框架很多,例如:Fresco、Picasso、Glide與Imageloader。它們都有各自的優(yōu)點,但總的來說,使用起來方便簡單、可...
閱讀 2744·2023-04-25 14:21
閱讀 1181·2021-11-23 09:51
閱讀 4026·2021-09-22 15:43
閱讀 614·2019-08-30 15:55
閱讀 1565·2019-08-29 11:28
閱讀 2451·2019-08-26 11:44
閱讀 1688·2019-08-23 18:15
閱讀 2886·2019-08-23 16:42