PS:最近看到不少人都開始寫年終總結了,時間過得飛快,又到年末了,又老了一歲。android
學習內容:緩存
1.進度條服務器
2.縮放網絡
3.ControllerBuilder,ControllerListener,PostProcesser,Image Requestapp
4.漸進式JPEG與動圖的顯示ide
最近這兩天把Fresco的官方文檔算是看了個差很少,就剩下Fresco的基本原理還有結合okHttp等類庫如何使用的問題,雖然官方文檔給出的功能比較的多,好比說自定義View,縮略圖顯示等等,這些我也基本就看了個大概,以爲實際需求應該沒有那麼高的要求,所以有些東西我這裏就不介紹了,詳細的狀況能夠參考官方文檔,我這裏只是針對一些使用狀況比較多的功能進行一個簡單的介紹。post
1.進度條學習
進度條也算是Fresco的一個功能,Fresco內部自己提供了一個ProgressBarDrawable類,效果其實就是一個矩形的藍色進度條,當圖片處於加載狀態時,進度條會跟着進行加載,當圖片加載完畢以後,那麼進度條也隨之消失,可是這個進度並非實時更新的,若是咱們想要精確的加載進度,那麼咱們須要重寫內部的方法。gradle
class CustomProgressBar extends Drawable{ @Override protected boolean onLevelChange(int level) { //doSomething return super.onLevelChange(level); } }
自定義進度條咱們就須要實現onLevelChange方法,這裏只作一個簡單的介紹,若是你們想自定義進度條,那麼能夠參照ProgressBarDrawable去重寫一個進度條,我的感受這個功能通常般,與其在加載圖片的時候使用進度條,不如使用ProgressBarImage屬性爲圖片加載時設置一個進度圖片,同時還支持旋轉屬性。
優化
2.縮放
DraweeView的縮放和ImageView的縮放類型基本上是相同的,fitXY,centerCrop,惟一與ImageView有區別的就是,他不支持matrix屬性,可是追加了一個focusCrop屬性來替代matrix屬性,這裏在設置屬性的時候,xml使用fresco:actualScaleType來設置DraweeView的縮放屬性或者是使用GenericDraweeHierarchy屬性去設置。
GenericDraweeHierarchyBuilder progressHierarchyBuilder = new GenericDraweeHierarchyBuilder(getResources()); GenericDraweeHierarchy progressHierarchy = progressHierarchyBuilder .setProgressBarImage(new ProgressBarDrawable(), ScalingUtils.ScaleType.CENTER_INSIDE) .build(); progressImageDraweeView.setHierarchy(progressHierarchy);
這裏不要使用setScaleType()或者是在xml中android:scaleType設置縮放屬性,這是無效的。官方給出focusCrop去替代matrix是有必定道理的,雖說效果會比matrix要好,不過到底效果如何這個確實沒法評判。先說說這個屬性的具體做用。
咱們在實際需求中可能會遇到這樣的狀況,在顯示人臉圖片的時候,儘可能的將圖片居中顯示,之前我也遇到過這個需求,不過當時是用matrix屬性實現的,服務器會傳遞給咱們人臉的重心座標位置,而後客戶端須要根據人臉重心位置將圖像進行平移,同時須要將圖片進行縮放,是用matrix的話就須要使用matrix.postScale()和matrix.postTranslate()兩個方法去實現。簡單貼一下當時的處理方式。
@Override public void onLoadingComplete(String s, View view, Bitmap bitmap) { float bitmapWidth = bitmap.getWidth(); float bitmapHeight = bitmap.getHeight(); Scale[o] = (params[o].height / bitmapHeight >= params[o].width / bitmapWidth) ? params[o].height / bitmapHeight : params[o].width / bitmapWidth; float scaleBitmapWidth = Scale[o] * bitmapWidth; float scaleBitmapHeight = Scale[o] * bitmapHeight; Matrix matrix = new Matrix(); matrix.postScale(Scale[o], Scale[o]); if (scaleBitmapWidth > scaleBitmapHeight) { //寬度圖 if (imagedata.get(o).getFace_center_x() == 0 && imagedata.get(o).getFace_center_y() == 0) { if(scaleBitmapWidth - params[o].width < 0.5 * scaleBitmapWidth - params[o].width / 2){ matrix.postTranslate( params[o].width - scaleBitmapWidth ,0); }else{ matrix.postTranslate(-(0.5f * scaleBitmapWidth - params[o].width / 2), 0); } } else { if(scaleBitmapWidth - params[o].width < scaleBitmapWidth * imagedata.get(o).getFace_center_x() - params[o].width / 2) { matrix.postTranslate(params[o].width - scaleBitmapWidth, 0); } else { if (scaleBitmapWidth * imagedata.get(o).getFace_center_x() - params[o].width / 2 < 0) { matrix.postTranslate(0, 0); } else { matrix.postTranslate(-(scaleBitmapWidth * imagedata.get(o).getFace_center_x() - params[o].width / 2), 0); } } } } else { //高度圖 if (imagedata.get(o).getFace_center_x() == 0 && imagedata.get(o).getFace_center_y() == 0) { if(scaleBitmapHeight - params[o].height < 0.5 * scaleBitmapHeight - params[o].height / 2){ matrix.postTranslate(0, params[o].height - scaleBitmapHeight); }else{ matrix.postTranslate(0,-(0.5f * scaleBitmapHeight - params[o].height / 2)); } } else { if (scaleBitmapHeight - params[o].height < scaleBitmapHeight * imagedata.get(o).getFace_center_y() - params[o].height / 2) { matrix.postTranslate(0, params[o].height - scaleBitmapHeight); } else { if (scaleBitmapHeight * imagedata.get(o).getFace_center_y() - params[o].height / 2 < 0) { matrix.postTranslate(0, 0); } else { matrix.postTranslate(0, -(scaleBitmapHeight * imagedata.get(o).getFace_center_y() - params[o].height / 2)); } } } } vh.image[o].setImageMatrix(matrix);
}
這格式整的真蛋疼,這就是當時咱們實際的項目需求,當時是一個GridView,其中一行有三張圖片,須要同時對三張圖片進行控制,若是是人像圖片,那麼須要將頭像平移到中央,若是不是人像圖片,那麼就顯示中央位置就能夠了,當時實現的仍是挺麻煩的,對寬度圖和高度圖進行了一個判斷,由於圖片分爲寬度圖和高度圖,那麼在計算縮放比例的時候,就須要對兩者進行判斷,縮放比例的計算 = 實際顯示的尺寸 / 圖片真正的尺寸,因爲圖片的不一樣,咱們須要取兩者的最大值來設置縮放比例,纔不會致使出現縮放過分的問題,同時平移的尺寸不能過分。
舉個例子,好比說咱們ImageView的實際顯示寬度是100,咱們對圖片進行了縮放,縮放以後圖片的寬度是110,那麼咱們能夠平移的最大距離就是10個單位,不能超過10個單位,不然顯示的時候就會出現問題。而且咱們須要將圖片儘量的居中顯示,也就是說盡量的使圖片損失的單位要小一點,那麼咱們就只能講圖片平移5個單位,也就是說,左右兩邊各損失5個單位,這樣的顯示要比徹底偏向一邊損失10個單位要好得多。這裏能夠看到使用matrix仍是挺複雜的,也是這裏的代碼是能夠進行優化的,可是優化完以後其實仍是比較的麻煩。
若是咱們使用Fresco的focusCrop屬性的話,那麼事情就會變得很簡單。
fresco:actualImageScaleType="focusCrop"
xml中設置縮放類型爲focusCrop,而後在Java代碼中設置:
PointF focusPoint; // your app populates the focus point mSimpleDraweeView .getHierarchy() .setActualImageFocusPoint(focusPoint);
這裏focusPoint就是咱們中心點的相對位置,float類型,(0.5,0.5)就至關於centerCrop中央位置,(1.0,1.0)也就是圖片的最下角位置,這樣若是在顯示人像圖片的時候問題就很是的輕鬆了,只須要傳遞頭像重心的相對位置,那麼Fresco就會自動的以座標點爲顯示中心。看起來好像確實是蠻簡單的。
有時候現有的 ScaleType 不符合你的需求,咱們容許你經過實現 ScalingUtils.ScaleType
來拓展它,這個接口裏面只有一個方法:getTransform
,它會基於如下參數來計算轉換矩陣,簡單解釋一下官方給出的例子。
官方的例子其實就是這樣,給了一個實際的View顯示尺寸,而後給了一個圖片的顯示尺寸,若是直接將圖片鋪上去,那麼圖片確實能夠顯示完整可是卻損失了一些像素點,那麼這時須要對寬度進行縮放,將圖片縮放爲400,那麼這時橫向就可以徹底的顯示在屏幕上了,可是高度卻變低了圖片從210變成了200,然而實際顯示的高度爲300,這時使用fitCenter保持寬高比,縮小或者放大,使得圖片徹底顯示在顯示邊界內,且寬或高契合顯示邊界。居中顯示。整體差很少就是這個意思。可是自我感受沒什麼意義,高度確實縮放了,可是寬度也縮放了,那麼寬度仍是會損失像素的。只不過是放大後的像素而已。
這裏就是官方給出的例子,這裏min表示的最小纔對,官方翻譯爲最大的一個。沒太具體的研究下面的方法,感受和使用matrix差很少,可是確實是簡化了很多。
public static abstract class AbstractScaleType implements ScaleType { @Override public Matrix getTransform(Matrix outTransform, Rect parentRect, int childWidth, int childHeight, float focusX, float focusY) { // 取寬度和高度須要縮放的倍數中最小的一個 final float sX = (float) parentRect.width() / (float) childWidth; final float sY = (float) parentRect.height() / (float) childHeight; float scale = Math.min(scaleX, scaleY); // 計算爲了均分空白區域,須要偏移的x、y方向的距離 float dx = parentRect.left + (parentRect.width() - childWidth * scale) * 0.5f; float dy = parentRect.top + (parentRect.height() - childHeight * scale) * 0.5f; // 最後咱們應用它 outTransform.setScale(scale, scale); outTransform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); return outTransform; } }
3.ControllerBuilder,ControllerListener,PostProcesser,Image Request
ControllerBuilder是用來構建Controller的,上一次已經簡單的介紹過Controller,主要是控制器,設置圖片的uri,可否從新加載等等,那麼ControllerBuilder就是使用build模式來構建Controller的。
ControllerListener則是用來控制下載的監聽事件的,若是咱們須要在圖片下載完成或者以後須要設置一切屬性,那麼ControllerListener能夠幫助咱們實現這個功能。可是這個監聽事件中是沒法修改圖片的,若是咱們須要修改圖片,那麼就須要使用到PostProcesser後處理器去修改圖片。ImageRequest用於配置更多的屬性。也能夠設置相關的uri,是否支持漸進式加載,或者設置後處理器。能夠看到這幾者是存在必然的聯繫的,所以將這三個功能放在一塊兒進行介紹。
這裏咱們爲gif圖片設置了一個ControllerListener,若是圖片獲取成功,而且圖片存在動畫效果,那麼播放動畫效果,不然toast消息。
ControllerListener controllerListener = new BaseControllerListener(){ @Override public void onFinalImageSet(String id, Object imageInfo, Animatable animatable) { if(animatable!=null){ animatable.start(); } } @Override public void onFailure(String id, Throwable throwable) { Toast.makeText(context,"圖片加載失敗",Toast.LENGTH_SHORT).show(); } }; DraweeController gifController = Fresco.newDraweeControllerBuilder() .setUri(Uri.parse("http://img.huofar.com/data/jiankangrenwu/shizi.gif")) .setOldController(gifImageView.getController()) .setControllerListener(controllerListener) .build(); gifImageView.setController(gifController);
理解起來都比較的簡單。那麼這裏再使用後處理器簡單處理一下。
Postprocessor redMeshPostProcessor = new BasePostprocessor() { @Override public void process(Bitmap bitmap) { for (int x = 0; x < bitmap.getWidth(); x+=2) { for (int y = 0; y < bitmap.getHeight(); y+=2) { bitmap.setPixel(x, y, Color.TRANSPARENT); } } } @Override public String getName() { return super.getName(); } }; ImageRequest processorImageRequest = ImageRequestBuilder .newBuilderWithSource(Uri.parse("http://avatar.csdn.net/4/E/8/1_y1scp.jpg")) .setPostprocessor(redMeshPostProcessor) .build(); DraweeController processorController = Fresco.newDraweeControllerBuilder() .setImageRequest(processorImageRequest) .setOldController(processImageView.getController()) .build(); processImageView.setController(processorController);
這裏爲圖片上繪製了一些小圓點,同時這個屬性的設置須要使用ImageRequest來進行配置後處理器。
ImageRequest的最低請求級別
setLowestPermittedRequestLevel
容許設置一個最低請求級別,請求級別和上面對應地有如下幾個取值:
BITMAP_MEMORY_CACHE
ENCODED_MEMORY_CACHE
DISK_CACHE
FULL_FETCH
若是你須要當即取到一個圖片,或者在相對比較短期內取到圖片,不然就不顯示的狀況下,這很是有用。
4.漸進式JPEG的設置,動圖顯示。
漸進式JPEG表示的是當咱們加載一張圖片的時候,若是網絡比較緩慢,那麼圖片會從模糊到清晰漸漸呈現,這被稱之爲漸進式JPEG。具體的使用以下:
/** * 設置漸進式JPEG Config * */ ProgressiveJpegConfig config = new ProgressiveJpegConfig() { @Override public int getNextScanNumberToDecode(int scanNumber) { return 0; } @Override public QualityInfo getQualityInfo(int scanNumber) { return null; } }; /** * 直接控制ImagePipeline Config * */ ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig.newBuilder(context) .setProgressiveJpegConfig(config) .setDownsampleEnabled(true) .build(); /** * 初始化使得Fresco支持漸進式JPEG的加載 * */ Fresco.initialize(this,imagePipelineConfig);
初始化的時候須要作這些配置,不然圖片是不會呈現漸進式JPEG的。
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse("http://pooyak.com/p/progjpeg/jpegload.cgi")) .setProgressiveRenderingEnabled(true) //設置支持漸進式JPEG .build(); DraweeController progressiveJPEGController = Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setOldController(progressiveJpegImageView.getController()) .build(); progressiveJpegImageView.setController(progressiveJPEGController);
動圖顯示其實沒什麼可說的,Controller也不須要作過多的配置。
DraweeController gifController = Fresco.newDraweeControllerBuilder() .setUri(Uri.parse("http://img.huofar.com/data/jiankangrenwu/shizi.gif")) .setAutoPlayAnimations(true) //使動畫自動播放 .setOldController(gifImageView.getController()) .build(); gifImageView.setController(gifController);
只須要setAutoPlayAnimations()設置爲true就能夠在加載後自動進行播放,若是但願手動進行控制,那麼就用ControllerListener進行控制或者直接用controller訪問animations來完成。
Animatable animatable = mSimpleDraweeView.getController().getAnimatable(); if (animatable != null) { animatable.start(); // later animatable.stop(); }
注意:動圖設置在高版本的Fresco須要引入gradle,使其支持動畫屬性。
compile 'com.facebook.fresco:animated-gif:0.14.0'
最後放置一個Demo:Demo下載。