借鑑Glide思想二次封裝Fresco
最近封裝了個 Fresco 的元件庫:DFresco,就順便來講講。
背景
Fresco 圖片庫很強大,我們專案中就是使用的 Fresco,但有一點就是,不怎麼好使用,略麻煩。不同專案中,多多少少都需要對 Fresco 進行一層封裝才能在 ui 裡快速使用。
這就導致了,不同專案都根據自己的業務需求場景來進行封裝,每次有新專案,複製貼上時又得解決好多業務耦合的錯誤,麻煩,是真的麻煩~
而且,首次接觸 Fresco,接入上手的成本相比其他圖片庫,如 Glide,成本都要大很多。
舉個例子,假如你有這麼個需求:載入一張網路上的 gif 圖片,為了防止記憶體佔用過多,需要設定解析度壓縮,最後顯示到圓形控制元件上,同時,需要設定佔位圖,錯誤圖,拉伸方式等。
那麼此時,你的程式碼可能就是這樣的:
ImageDecodeOptions imageDecodeOptions = ImageDecodeOptions.newBuilder() .setDecodePreviewFrame(true).build(); ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(mUri) .setProgressiveRenderingEnabled(true) .setImageDecodeOptions(imageDecodeOptions); if (mWidth > 0 && mHeight > 0) { builder.setResizeOptions(new ResizeOptions(mWidth, mHeight)); } ImageRequest request = builder.build(); AbstractDraweeController controller = Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setControllerListener(listener) .setOldController(draweeView.getController()) .setAutoPlayAnimations(true).build(); draweeView.setController(controller);
同時,你可能還需要在 xml 中對 SimpleDrawwView 控制元件進行佔位圖等等的配置:
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/sdv_fresco" android:layout_width="500dp" android:layout_height="500dp" fresco:actualImageScaleType="centerCrop" fresco:fadeDuration="3000" fresco:failureImage="@mipmap/ic_launcher" fresco:failureImageScaleType="centerCrop" fresco:placeholderImage="@mipmap/ic_launcher" fresco:placeholderImageScaleType="centerCrop" fresco:progressBarAutoRotateInterval="1000" fresco:progressBarImage="@drawable/ani_rotate" fresco:progressBarImageScaleType="centerCrop" fresco:retryImage="@mipmap/ic_launcher" fresco:retryImageScaleType="centerCrop" fresco:backgroundImage="@mipmap/ic_launcher" fresco:overlayImage="@mipmap/ic_launcher" fresco:pressedStateOverlayImage="@mipmap/ic_launcher" fresco:roundAsCircle="false" fresco:roundingBorderWidth="2dip" fresco:roundingBorderColor="@color/colorPrimary"/>
如果忘記了某個自定義屬性名是什麼的時候,還得到網上搜索下資料,是吧。
小結一下,使用 Fresco,你的接入學習成本至少需要知道 Fresco 的如下資訊:
- SimpleDraweeView 的自定義屬性
- ImageRequestBuilder 用法及大概用途
- AbstractDraweeController 用法及大概用途
- GenericDraweeHierarchy 用法及大概用途
如果涉及到一些網路下載監聽,快取之類的,那麼你還要了解:
- Imagepipeline 用法及大概用途
總之,Fresco 強大是強大,但使用起來不方便,不得不封裝一層。
既然要封裝,那麼就直接借鑑 Glide 的使用思想來進行封裝好了,如果有使用過 Glide 的應該很清楚,要實現以上功能,全程一個呼叫鏈即可。
二次封裝
封裝要達到的目的有兩點:
- 使用簡潔、方便
- 其他人接入直接上手的成本儘可能少,最好不用去看文件,去看原始碼
第一點可以參考 Glide 的使用方式來設計,那麼第二點我的想法是藉助 AndroidStudio 的程式碼提示功能來實現。
比如,你只需知道,元件的入口是 DFresco 即可,其他都通過 AndroidStudio 來給你提示,如:
當你在 AndroidStudio 上輸入 DFresco.
後,介面上會彈出你可用 api,這些就是我開放給你的入口,我將這個使用過程劃分成幾個步驟,每個步驟能做什麼,該做什麼,我都給你規定好了,你參照著提示,直接從方法命名上就能夠知道該如何使用了,AndroidStudio 會一步步引導你。
這裡就兩個入口,一個是用來初始化 Fresco 的:
init(Context)
這個內部封裝了一些預設的初始化配置,比如記憶體大小配置,內部日誌配置等等。
init(Context, ImagePipelineConfig)
這個是開放給你的自定義配置,如果你不想使用預設的配置的話。
source(String url)
:載入網路上的圖片source(File localFile)
:載入磁碟上的圖片source(Context context, int resId)
:載入 res 內的 drawable 資源圖片source(Uri uri)
:通用的載入方式
我將常用的幾種圖片來源單獨封裝出來使用,方便。
當呼叫了 source()
後就進入了第二個步驟,這個步驟中,我將圖片相關的配置設計到另外一個步驟中去,否則連同圖片配置的 api 也都在這裡的話,會搞得蠻亂的,可能讓使用者到這裡後不清楚該呼叫哪些介面了。
所以,我會把控每個步驟的 api,儘量讓每個步驟的 api 做的事都比較相近,比如這裡:
intoTarget(SimpleDraweeView)
載入圖片顯示到控制元件上intoTarget(SimpleDraweeView,ControllerListener)
載入圖片顯示到控制元件上,允許監聽這個過程intoTarget(BaseBitmapDataSubscriber)
只加載圖片到記憶體中,以 Bitmap 形式存在
我的需求場景大概就是直接載入圖片顯示到控制元件上,或者有時候只是需要將圖片載入到記憶體中,但不用顯示到某個控制元件上,反而要取得圖片的 Bitmap 物件,所以我將這些都封裝起來了。
resize(int width, int height)
這個實際上就是對 Fresco 中的 ResizeOptions 的一層封裝而已,簡化使用,不至於像以前那麼麻煩。
enterImageConfig()
如果你都使用預設配置的話,那麼是不用再去呼叫那些各種配置的介面的,所以我才將圖片配置封裝到另外一個步驟中,這個步驟你可進,可不進,如果有需求,那麼通過這個方法進入圖片配置步驟:
這裡的配置項很多,也是因為這個原因,所以才不想讓這些介面跟上一個步驟放一起,不然很容易讓使用者懵掉。而進入了圖片配置這個步驟後,這裡提供的 api 其實就是對 GenericDraweeHierarchy 的用途進行了一層封裝,或者說對 SimpleDraweeView 的自定義屬性進行了一層封裝。
如果你不熟悉,沒關係,其實就是一些常用的功能,如設定控制元件為圓形、圓角、邊框,設定佔位圖、失敗圖、進度圖、圖片拉伸方式、淡入淡出動畫時長等等。
這樣封裝的目的在於,你可以通過一條呼叫鏈的形式就設定完所有的配置,就像 Glide 的使用一樣,而不用再去 new 很多 Fresco 的類,再去拼接。
進入圖片配置步驟只是一個可選的步驟,進來之後當然就要出去,所以當完成了你的配置後,需要呼叫:finishImageConfig()
,如:
這樣就完成了圖片配置,將流程切回主線了,就可以繼續根據你的需要設定圖片顯示的目標了。
當然,為了防止再次進入圖片配置步驟這樣造成之前的配置項失效的場景,我借鑑了 Fresco 的 init
處理方法,即,如果一次使用過程中,重複進入圖片配置步驟,那麼程式會拋異常來提醒你不能這麼做。
以上,就是 DFresco 元件的封裝思想,歡迎指點一下哈~
另外,參考了 Glide 的一些處理,當你的 intoTarget 是傳入了 SimpleDraweeView 控制元件時,DFresco 內部會自動根據控制元件的大小對圖片進行一次解析度壓縮,降低圖片佔用記憶體,當然,如果你有手動呼叫了 resize()
,那麼以你的為主。
使用示例
compile 'com.dasu.image:fresco:0.0.1'
使用之前,需先初始化,建議在 Application 中進行:
DFresco.init(this);
//載入 res 中的 drawable 圖片到 SimpleDraweeView 控制元件上(預設支援 gif 圖,並且會自動根據控制元件寬高進行解析度壓縮,降低記憶體佔用
DFresco.source(mContext, R.drawable.weixin).intoTarget(mSimpleDraweeView);
//載入磁碟中的圖片,手動設定解析度的壓縮,並獲取 bitmap 物件,監聽回撥,手動顯示到 ImageView 控制元件上
DFresco.source(new File("/mnt/sdcard/weixin.jpg"))
.resize(500, 500)
.intoTarget(new BaseBitmapDataSubscriber() {
@Override
protected void onNewResultImpl(Bitmap bitmap) {
Log.w("!!!!!!", "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount());
Log.w("!!!!!!", "width:" + bitmap.getWidth() + ":::height:" + bitmap.getHeight());
mImageView.setImageBitmap(bitmap);
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
Log.e("!!!!!!", "onFailureImpl");
}
});
//載入網路圖片,進行各種配置,如縮放方式,佔位圖,圓形,圓角,動畫時長等等,最後自動顯示到 SimpleDraweeView 控制元件上
DFresco.source("https://upload-images.jianshu.io/upload_images/1924341-9e528ee638e837a5.png")
.enterImageConfig() //進入配置步驟
.allFitXY() //所有圖片,包括佔位圖等等的拉伸方式
.animFade(3000) //淡入淡出動畫時長
.placeholderScaleType(ScalingUtils.ScaleType.CENTER_INSIDE) //設定佔位圖的拉伸方式,後面設定的會覆蓋前面的
.actualScaleType(ScalingUtils.ScaleType.CENTER)
// .asRound(50) //設定圓角,(圓角和圓形不能同時設定)
.asCircle() //設定控制元件顯示為圓形控制元件
.roundBorderColor(Color.RED) //設定圓角或圓形的邊框顏色
.roundBorderWidth(20) //設定圓角或圓形的邊框寬度
.failure(R.drawable.timg) //設定失敗圖
.progressBar(R.drawable.aaaa) //設定載入進度圖
.retry(R.drawable.weixin) //設定重試時的圖
.placeholder(R.drawable.image) //設定佔位圖
.finishImageConfig() //退出配置步驟
.intoTarget(mSimpleDraweeView);
大家好,我是 dasu,歡迎關注我的公眾號(dasuAndroidTv),如果你覺得本篇內容有幫助到你,可以轉載但記得要關注,要標明原文哦,謝謝支援~