詳解Android之圖片加載框架Fresco基本使用(二)
PS:最近看到很多人都開始寫年終總結(jié)了,時(shí)間過得飛快,又到年底了,又老了一歲。
學(xué)習(xí)內(nèi)容:
1.進(jìn)度條
2.縮放
3.ControllerBuilder,ControllerListener,PostProcesser,Image Request
4.漸進(jìn)式JPEG與動圖的顯示
最近這兩天把Fresco的官方文檔算是看了個(gè)差不多,就剩下Fresco的基本原理還有結(jié)合okHttp等類庫如何使用的問題,雖然官方文檔給出的功能比較的多,比如說自定義View,縮略圖顯示等等,這些我也基本就看了個(gè)大概,覺得實(shí)際需求應(yīng)該沒有那么高的要求,因此有些東西我這里就不介紹了,詳細(xì)的情況可以參考官方文檔,我這里只是針對一些使用情況比較多的功能進(jìn)行一個(gè)簡單的介紹。
1.進(jìn)度條
進(jìn)度條也算是Fresco的一個(gè)功能,F(xiàn)resco內(nèi)部本身提供了一個(gè)ProgressBarDrawable類,效果其實(shí)就是一個(gè)矩形的藍(lán)色進(jìn)度條,當(dāng)圖片處于加載狀態(tài)時(shí),進(jìn)度條會跟著進(jìn)行加載,當(dāng)圖片加載完畢之后,那么進(jìn)度條也隨之消失,但是這個(gè)進(jìn)度并不是實(shí)時(shí)更新的,如果我們想要精確的加載進(jìn)度,那么我們需要重寫內(nèi)部的方法。
class CustomProgressBar extends Drawable{ @Override protected boolean onLevelChange(int level) { //doSomething return super.onLevelChange(level); } }
自定義進(jìn)度條我們就需要實(shí)現(xiàn)onLevelChange方法,這里只做一個(gè)簡單的介紹,如果大家想自定義進(jìn)度條,那么可以參照ProgressBarDrawable去重寫一個(gè)進(jìn)度條,個(gè)人感覺這個(gè)功能一般般,與其在加載圖片的時(shí)候使用進(jìn)度條,不如使用ProgressBarImage屬性為圖片加載時(shí)設(shè)置一個(gè)進(jìn)度圖片,同時(shí)還支持旋轉(zhuǎn)屬性。
2.縮放
DraweeView的縮放和ImageView的縮放類型基本上是相同的,fitXY,centerCrop,唯一與ImageView有區(qū)別的就是,他不支持matrix屬性,但是追加了一個(gè)focusCrop屬性來替代matrix屬性,這里在設(shè)置屬性的時(shí)候,xml使用fresco:actualScaleType來設(shè)置DraweeView的縮放屬性或者是使用GenericDraweeHierarchy屬性去設(shè)置。
GenericDraweeHierarchyBuilder progressHierarchyBuilder = new GenericDraweeHierarchyBuilder(getResources()); GenericDraweeHierarchy progressHierarchy = progressHierarchyBuilder .setProgressBarImage(new ProgressBarDrawable(), ScalingUtils.ScaleType.CENTER_INSIDE) .build(); progressImageDraweeView.setHierarchy(progressHierarchy);
這里不要使用setScaleType()或者是在xml中android:scaleType設(shè)置縮放屬性,這是無效的。官方給出focusCrop去替代matrix是有一定道理的,雖然說效果會比matrix要好,不過到底效果如何這個(gè)確實(shí)無法評判。先說說這個(gè)屬性的具體作用。
我們在實(shí)際需求中可能會遇到這樣的情況,在顯示人臉圖片的時(shí)候,盡量的將圖片居中顯示,以前我也遇到過這個(gè)需求,不過當(dāng)時(shí)是用matrix屬性實(shí)現(xiàn)的,服務(wù)器會傳遞給我們?nèi)四樀闹匦淖鴺?biāo)位置,然后客戶端需要根據(jù)人臉重心位置將圖像進(jìn)行平移,同時(shí)需要將圖片進(jìn)行縮放,是用matrix的話就需要使用matrix.postScale()和matrix.postTranslate()兩個(gè)方法去實(shí)現(xiàn)。簡單貼一下當(dāng)時(shí)的處理方式。
@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); }
這格式整的真蛋疼,這就是當(dāng)時(shí)我們實(shí)際的項(xiàng)目需求,當(dāng)時(shí)是一個(gè)GridView,其中一行有三張圖片,需要同時(shí)對三張圖片進(jìn)行控制,如果是人像圖片,那么需要將頭像平移到中央,如果不是人像圖片,那么就顯示中央位置就可以了,當(dāng)時(shí)實(shí)現(xiàn)的還是挺麻煩的,對寬度圖和高度圖進(jìn)行了一個(gè)判斷,因?yàn)閳D片分為寬度圖和高度圖,那么在計(jì)算縮放比例的時(shí)候,就需要對二者進(jìn)行判斷,縮放比例的計(jì)算 = 實(shí)際顯示的尺寸 / 圖片真正的尺寸,由于圖片的不同,我們需要取二者的最大值來設(shè)置縮放比例,才不會導(dǎo)致出現(xiàn)縮放過度的問題,同時(shí)平移的尺寸不能過度。
舉個(gè)例子,比如說我們ImageView的實(shí)際顯示寬度是100,我們對圖片進(jìn)行了縮放,縮放之后圖片的寬度是110,那么我們可以平移的最大距離就是10個(gè)單位,不能超過10個(gè)單位,否則顯示的時(shí)候就會出現(xiàn)問題。并且我們需要將圖片盡可能的居中顯示,也就是說盡可能的使圖片損失的單位要小一點(diǎn),那么我們就只能講圖片平移5個(gè)單位,也就是說,左右兩邊各損失5個(gè)單位,這樣的顯示要比完全偏向一邊損失10個(gè)單位要好得多。這里可以看到使用matrix還是挺復(fù)雜的,也是這里的代碼是可以進(jìn)行優(yōu)化的,但是優(yōu)化完之后其實(shí)還是比較的麻煩。
如果我們使用Fresco的focusCrop屬性的話,那么事情就會變得很簡單。
fresco:actualImageScaleType="focusCrop"
xml中設(shè)置縮放類型為focusCrop,然后在Java代碼中設(shè)置:
PointF focusPoint; // your app populates the focus point mSimpleDraweeView .getHierarchy() .setActualImageFocusPoint(focusPoint);
這里focusPoint就是我們中心點(diǎn)的相對位置,float類型,(0.5,0.5)就相當(dāng)于centerCrop中央位置,(1.0,1.0)也就是圖片的最下角位置,這樣如果在顯示人像圖片的時(shí)候問題就非常的輕松了,只需要傳遞頭像重心的相對位置,那么Fresco就會自動的以坐標(biāo)點(diǎn)為顯示中心??雌饋砗孟翊_實(shí)是蠻簡單的。
有時(shí)候現(xiàn)有的 ScaleType 不符合你的需求,我們允許你通過實(shí)現(xiàn) ScalingUtils.ScaleType 來拓展它,這個(gè)接口里面只有一個(gè)方法:getTransform,它會基于以下參數(shù)來計(jì)算轉(zhuǎn)換矩陣,簡單解釋一下官方給出的例子。
官方的例子其實(shí)就是這樣,給了一個(gè)實(shí)際的View顯示尺寸,然后給了一個(gè)圖片的顯示尺寸,如果直接將圖片鋪上去,那么圖片確實(shí)可以顯示完整但是卻損失了一些像素點(diǎn),那么這時(shí)需要對寬度進(jìn)行縮放,將圖片縮放為400,那么這時(shí)橫向就能夠完全的顯示在屏幕上了,但是高度卻變低了圖片從210變成了200,然而實(shí)際顯示的高度為300,這時(shí)使用fitCenter保持寬高比,縮小或者放大,使得圖片完全顯示在顯示邊界內(nèi),且寬或高契合顯示邊界。居中顯示??傮w差不多就是這個(gè)意思。但是自我感覺沒什么意義,高度確實(shí)縮放了,但是寬度也縮放了,那么寬度還是會損失像素的。只不過是放大后的像素而已。
這里就是官方給出的例子,這里min表示的最小才對,官方翻譯為最大的一個(gè)。沒太具體的研究下面的方法,感覺和使用matrix差不多,但是確實(shí)是簡化了不少。
public static abstract class AbstractScaleType implements ScaleType { @Override public Matrix getTransform(Matrix outTransform, Rect parentRect, int childWidth, int childHeight, float focusX, float focusY) { // 取寬度和高度需要縮放的倍數(shù)中最小的一個(gè) final float sX = (float) parentRect.width() / (float) childWidth; final float sY = (float) parentRect.height() / (float) childHeight; float scale = Math.min(scaleX, scaleY); // 計(jì)算為了均分空白區(qū)域,需要偏移的x、y方向的距離 float dx = parentRect.left + (parentRect.width() - childWidth * scale) * 0.5f; float dy = parentRect.top + (parentRect.height() - childHeight * scale) * 0.5f; // 最后我們應(yīng)用它 outTransform.setScale(scale, scale); outTransform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); return outTransform; } }
3.ControllerBuilder,ControllerListener,PostProcesser,Image Request
ControllerBuilder是用來構(gòu)建Controller的,上一次已經(jīng)簡單的介紹過Controller,主要是控制器,設(shè)置圖片的uri,能否重新加載等等,那么ControllerBuilder就是使用build模式來構(gòu)建Controller的。
ControllerListener則是用來控制下載的監(jiān)聽事件的,如果我們需要在圖片下載完成或者之后需要設(shè)置一切屬性,那么ControllerListener可以幫助我們實(shí)現(xiàn)這個(gè)功能。但是這個(gè)監(jiān)聽事件中是無法修改圖片的,如果我們需要修改圖片,那么就需要使用到PostProcesser后處理器去修改圖片。ImageRequest用于配置更多的屬性。也可以設(shè)置相關(guān)的uri,是否支持漸進(jìn)式加載,或者設(shè)置后處理器??梢钥吹竭@幾者是存在必然的聯(lián)系的,因此將這三個(gè)功能放在一起進(jìn)行介紹。
這里我們?yōu)間if圖片設(shè)置了一個(gè)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);
這里為圖片上繪制了一些小圓點(diǎn),同時(shí)這個(gè)屬性的設(shè)置需要使用ImageRequest來進(jìn)行配置后處理器。
ImageRequest的最低請求級別
1.檢查內(nèi)存緩存,有如,立刻返回。這個(gè)操作是實(shí)時(shí)的。
2.檢查未解碼的圖片緩存,如有,解碼并返回。
3.檢查磁盤緩存,如果有加載,解碼,返回。
4.下載或者加載本地文件。調(diào)整大小和旋轉(zhuǎn)(如有),解碼并返回。對于網(wǎng)絡(luò)圖來說,這一套流程下來是最耗時(shí)的。
setLowestPermittedRequestLevel允許設(shè)置一個(gè)最低請求級別,請求級別和上面對應(yīng)地有以下幾個(gè)取值:
- BITMAP_MEMORY_CACHE
- ENCODED_MEMORY_CACHE
- DISK_CACHE
- FULL_FETCH
如果你需要立即取到一個(gè)圖片,或者在相對比較短時(shí)間內(nèi)取到圖片,否則就不顯示的情況下,這非常有用。
4.漸進(jìn)式JPEG的設(shè)置,動圖顯示。
漸進(jìn)式JPEG表示的是當(dāng)我們加載一張圖片的時(shí)候,如果網(wǎng)絡(luò)比較緩慢,那么圖片會從模糊到清晰漸漸呈現(xiàn),這被稱之為漸進(jìn)式JPEG。具體的使用如下:
/** * 設(shè)置漸進(jìn)式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支持漸進(jìn)式JPEG的加載 * */ Fresco.initialize(this,imagePipelineConfig);
初始化的時(shí)候需要做這些配置,否則圖片是不會呈現(xiàn)漸進(jìn)式JPEG的。
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse("http://pooyak.com/p/progjpeg/jpegload.cgi")) .setProgressiveRenderingEnabled(true) //設(shè)置支持漸進(jìn)式JPEG .build(); DraweeController progressiveJPEGController = Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setOldController(progressiveJpegImageView.getController()) .build(); progressiveJpegImageView.setController(progressiveJPEGController);
動圖顯示其實(shí)沒什么可說的,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()設(shè)置為true就可以在加載后自動進(jìn)行播放,如果希望手動進(jìn)行控制,那么就用ControllerListener進(jìn)行控制或者直接用controller訪問animations來完成。
Animatable animatable = mSimpleDraweeView.getController().getAnimatable(); if (animatable != null) { animatable.start(); // later animatable.stop(); }
注意:動圖設(shè)置在高版本的Fresco需要引入gradle,使其支持動畫屬性。
compile 'com.facebook.fresco:animated-gif:0.14.0'
最后放置一個(gè)Demo:Demo下載。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實(shí)現(xiàn)ViewPager無限循環(huán)效果(一)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)ViewPager無限循環(huán)效果的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android ViewPager自定義輪播圖并解決播放沖突
這篇文章主要為大家詳細(xì)介紹了Android ViewPager自定義輪播圖并解決播放沖突,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Android非XML形式動態(tài)生成、調(diào)用頁面的方法
這篇文章主要介紹了Android非XML形式動態(tài)生成、調(diào)用頁面的方法,涉及Android構(gòu)建頁面的相關(guān)技巧,需要的朋友可以參考下2015-04-04Android使用OKhttp3實(shí)現(xiàn)登錄注冊功能+springboot搭建后端的詳細(xì)過程
這篇教程主要實(shí)現(xiàn)Android使用OKhttp3實(shí)現(xiàn)登錄注冊的功能,后端使用SSM框架,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-07-07Android studio gradle環(huán)境變量配置教程
這篇文章主要為大家詳細(xì)介紹了Android studio gradle環(huán)境變量配置教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05微信或手機(jī)瀏覽器在線顯示office文件(已測試ios、android)
這篇文章主要介紹了微信或手機(jī)瀏覽器在線顯示office文件,已測試ios、android,感興趣的小伙伴們可以參考一下2016-06-06Android自定義view實(shí)現(xiàn)水波進(jìn)度條控件
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)水波進(jìn)度條控件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android 中SQLite技術(shù)實(shí)例詳解
這篇文章主要介紹了Android 中SQLite技術(shù)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06