1. 程式人生 > >Fresco的封裝和使用說明以及獲取快取中的Bitmap物件

Fresco的封裝和使用說明以及獲取快取中的Bitmap物件

Fresco介紹

Fresco是facebook開源的圖片載入框架。

關於 Fresco Fresco 是一個強大的圖片載入元件。

Fresco 中設計有一個叫做 image pipeline
的模組。它負責從網路,從本地檔案系統,本地資源載入圖片。為了最大限度節省空間和CPU時間,它含有3級快取設計(2級記憶體,1級檔案)。

Fresco 中設計有一個叫做 Drawees 模組,方便地顯示loading圖,當圖片不再顯示在螢幕上時,及時地釋放記憶體和空間佔用。

Fresco 支援 Android2.3(API level 9) 及其以上系統。

相比其他的圖片載入框架功能強大得多,如果還不瞭解同學可以點選上面的連結學習瞭解。

它的整個框架設計涉及到很多的設計模式,作為開發者如果不瞭解足夠多的設計模式對開源框架分析理解是在有點困難,所以我的部落格會經常更新一些有關設計模式的內容和大家一起學習。

封裝理由

Fresco功能強大。如果只是普通的使用很簡單。
如果需要使用它真正強大的功能就很複雜,這是因為它的框架中包含了太多的功能類和設計模式,導致使用起來非常麻煩,相比其他的一行程式碼就可以解決圖片載入,Fresco就麻煩多了。所以我封裝了一個ImageLoadFresco類方便我在專案中使用,現在開源出來給作為使用大家參考。

為什麼Fresco配置這麼複雜?
因為對整個Fresco圖片載入框架 使用了MVC模式
M層上有很多的圖片資源,比如佔位圖,loading圖,error圖
C層決定呼叫M層上哪些圖
V層對應檢視View層,最後繫結M/C層。所以在使用時需要配置M/C層需要很多程式碼。

使用說明

封裝之後呼叫圖片載入就相對簡單,簡單的使用是沒有問題的,只是在Adapter中使用存在控制元件重複配置的問題,會對記憶體有些影響。這個等我有時間再深究解決。
問題提示在這裡

在指定一個新的controller的時候,使用setOldController,這可節省不必要的記憶體分配。

使用示例程式碼

ImageLoadBuilder.Start(getContext(),mImageView,url)
                .setIsCircle(true)
                .build();

封裝程式碼

說明

針對這個配置屬性比較多的類,我採用

Builder生成器模式,簡化使用時候的程式碼量。類裡面配置很多預設屬性。如果需要新增新的屬性也不會影響現有程式碼編譯。同時在使用的時候不涉及具體的Fresco程式碼,後期如果有需求替換圖片載入框架,也會方便。

程式碼

最新的程式碼點這裡,這個封裝類我自己也在專案中使用,會根據需求新增新的功能。

更新記錄

抽象出兩個類

Builder類

設定預設配置以及修改配置的方法等


/**
 * Created by 李可樂 on 2017/2/17 0017.
 * 圖片載入的封裝builder類
 * 使用示例
 * <p>
 * ImageLoadBuilder.Start(getContext(),mImageUser,url_head)
 * .setIsCircle(true)
 * .build();
 * </p>
 */

public class ImageLoadBuilder {
    //必要引數
    Context mContext;
    SimpleDraweeView mSimpleDraweeView;
    String mUrl;

    //非必要引數
    String mUrlLow;//低分率圖地址

    Drawable mPlaceHolderImage;//佔位圖
    Drawable mProgressBarImage;//loading圖
    Drawable mRetryImage;//重試圖
    Drawable mFailureImage;//失敗圖
    Drawable mBackgroundImage;//背景圖

    ScalingUtils.ScaleType mActualImageScaleType = ScalingUtils.ScaleType.CENTER_CROP;
    boolean mIsCircle = false;//是否圓形圖片
    boolean mIsRadius = false;//是否圓角
    boolean mIsBorder = false;//是否有包邊
    float mRadius = 10;//圓角度數 預設10
    ResizeOptions mResizeOptions = new ResizeOptions(3000, 3000);//圖片的大小限制

    ControllerListener mControllerListener;//圖片載入的回撥

    BaseBitmapDataSubscriber mBitmapDataSubscriber;

    /**
     * 構造器的構造方法 傳入必要引數
     *
     * @param mContext
     * @param mSimpleDraweeView
     * @param mUrl
     */
    public ImageLoadBuilder(Context mContext, SimpleDraweeView mSimpleDraweeView, String mUrl) {
        this.mContext = mContext;
        this.mSimpleDraweeView = mSimpleDraweeView;
        this.mUrl = mUrl;
    }

    public static ImageLoadBuilder Start(Context mContext, SimpleDraweeView mSimpleDraweeView, String mUrl) {
        return new ImageLoadBuilder(mContext, mSimpleDraweeView, mUrl);
    }

    /**
     * 構造器的build方法 構造真正的物件 並返回
     * 構造之前需要檢查
     *
     * @return
     */
    public ImageLoadFresco build() {
        Logger.d("圖片開始載入 viewId=" + this.mSimpleDraweeView.getId() + " url" + this.mUrl);
//            if (TextUtils.isEmpty(mUrl)) {
//                throw new IllegalArgumentException("URL不能為空");
//            }

        //不能同時設定 圓形圓角
        if (mIsCircle && mIsRadius) {
            throw new IllegalArgumentException("圖片不能同時設定圓角和圓形");
        }

        return new ImageLoadFresco(this);
    }

    public ImageLoadBuilder setBitmapDataSubscriber(BaseBitmapDataSubscriber mBitmapDataSubscriber) {
        this.mBitmapDataSubscriber = mBitmapDataSubscriber;
        return this;
    }

    public ImageLoadBuilder setUrlLow(String urlLow) {
        this.mUrlLow = urlLow;
        return this;
    }

    public ImageLoadBuilder setActualImageScaleType(ScalingUtils.ScaleType mActualImageScaleType) {
        this.mActualImageScaleType = mActualImageScaleType;
        return this;
    }

    public ImageLoadBuilder setPlaceHolderImage(Drawable mPlaceHolderImage) {
        this.mPlaceHolderImage = mPlaceHolderImage;
        return this;
    }

    public ImageLoadBuilder setProgressBarImage(Drawable mProgressBarImage) {
        this.mProgressBarImage = mProgressBarImage;
        return this;
    }

    public ImageLoadBuilder setRetryImage(Drawable mRetryImage) {
        this.mRetryImage = mRetryImage;
        return this;
    }

    public ImageLoadBuilder setFailureImage(Drawable mFailureImage) {
        this.mFailureImage = mFailureImage;
        return this;
    }

    public ImageLoadBuilder setBackgroundImage(Drawable mBackgroundImage) {
        this.mBackgroundImage = mBackgroundImage;
        return this;
    }

    public ImageLoadBuilder setBackgroupImageColor(int colorId) {
        Drawable color = ContextCompat.getDrawable(mContext, colorId);
        this.mBackgroundImage = color;
        return this;
    }

    public ImageLoadBuilder setIsCircle(boolean mIsCircle) {
        this.mIsCircle = mIsCircle;
        return this;
    }

    public ImageLoadBuilder setIsCircle(boolean mIsCircle, boolean mIsBorder) {
        this.mIsBorder = mIsBorder;
        this.mIsCircle = mIsCircle;
        return this;
    }

    public ImageLoadBuilder setIsRadius(boolean mIsRadius) {
        this.mIsRadius = mIsRadius;
        return this;
    }

    public ImageLoadBuilder setIsRadius(boolean mIsRadius, float mRadius) {
        this.mRadius = mRadius;
        return setIsRadius(mIsRadius);
    }

    public ImageLoadBuilder setRadius(float mRadius) {
        this.mRadius = mRadius;
        return this;
    }

    public ImageLoadBuilder setResizeOptions(ResizeOptions mResizeOptions) {
        this.mResizeOptions = mResizeOptions;
        return this;
    }

    public ImageLoadBuilder setControllerListener(ControllerListener mControllerListener) {
        this.mControllerListener = mControllerListener;
        return this;
    }
}

Fresco載入實現類

把真正圖片載入實現類抽象成一個類。

/**
 * Created by LiCola on  2016/01/16  15:26
 * 封裝的Fresco圖片載入類
 * 使用構造器傳入配置好的值
 */
public class ImageLoadFresco {
    private static final String TAG = "ImageLoadFresco";

    //必要引數
    private SimpleDraweeView mSimpleDraweeView;
    private Context mContext;

    /**
     * 私有化的建構函式 得到builder的引數 構造物件
     *
     * @param frescoBuilder 構造器
     */
    ImageLoadFresco(ImageLoadBuilder frescoBuilder) {
        this.mContext = frescoBuilder.mContext;
        this.mSimpleDraweeView = frescoBuilder.mSimpleDraweeView;

        //初始化M層 用於初始化圖片中包含的資料
        GenericDraweeHierarchyBuilder builderM = new GenericDraweeHierarchyBuilder(mContext.getResources());

        //請求引數 主要配置url 和C層相關
        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(frescoBuilder.mUrl))
                .setResizeOptions(frescoBuilder.mResizeOptions)
                .build();
        //初始化C層 用於控制圖片的載入 是主要的實現控制類
        PipelineDraweeControllerBuilder builderC = Fresco.newDraweeControllerBuilder()
//                .setOldController(mSimpleDraweeView.getController())
                ;

        if (frescoBuilder.mUrlLow != null) {
            builderC.setLowResImageRequest(ImageRequest.fromUri(frescoBuilder.mUrlLow));
        }

        builderC.setImageRequest(request);

        setViewPerformance(frescoBuilder, builderM, builderC);

        if (frescoBuilder.mControllerListener != null) {
            builderC.setControllerListener(frescoBuilder.mControllerListener);
        }

        DraweeController draweeController = builderC.build();

        if (frescoBuilder.mBitmapDataSubscriber != null) {
            ImagePipeline imagePipeline = Fresco.getImagePipeline();

            DataSource<CloseableReference<CloseableImage>> dataSource =
                    imagePipeline.fetchDecodedImage(request, mSimpleDraweeView.getContext());
            dataSource.subscribe(frescoBuilder.mBitmapDataSubscriber, CallerThreadExecutor.getInstance());
        }

        mSimpleDraweeView.setHierarchy(builderM.build());
        mSimpleDraweeView.setController(draweeController);
    }

    /**
     * 配置DraweeView的各種表現效果
     * 如 失敗圖 重試圖 圓角或圓形
     *
     * @param frescoBuilder
     * @param builderM
     * @param builderC
     */
    private void setViewPerformance(ImageLoadBuilder frescoBuilder, GenericDraweeHierarchyBuilder builderM, PipelineDraweeControllerBuilder builderC) {

        //設定圖片的縮放形式
        builderM.setActualImageScaleType(frescoBuilder.mActualImageScaleType);
        if (frescoBuilder.mActualImageScaleType == ScalingUtils.ScaleType.FOCUS_CROP) {
            builderM.setActualImageFocusPoint(new PointF(0f, 0f));
        }
        ;
        if (frescoBuilder.mPlaceHolderImage != null) {
//            builderM.setPlaceholderImage(ContextCompat.getDrawable(mContext, R.drawable.ic_account_circle_gray_48dp), ScalingUtils.ScaleType.CENTER);
            builderM.setPlaceholderImage(frescoBuilder.mPlaceHolderImage, ScalingUtils.ScaleType.CENTER);
        }

        if (frescoBuilder.mProgressBarImage != null) {
            Drawable progressBarDrawable = new AutoRotateDrawable(frescoBuilder.mProgressBarImage, 2000);
            builderM.setProgressBarImage(progressBarDrawable);
            //// TODO: 2016/3/18 0018 直接設定無效 是自定義Drawable setColor知識為了類裡面的取值
//            MyProgressBarDrawable progressBarDrawable=new MyProgressBarDrawable();
//            builderM.setProgressBarImage(progressBarDrawable);
        }

        //設定重試圖 同時需要C層支援點選控制
        if (frescoBuilder.mRetryImage != null) {
            builderC.setTapToRetryEnabled(true);
            builderM.setRetryImage(frescoBuilder.mRetryImage);
        }

        if (frescoBuilder.mFailureImage != null) {
            builderM.setFailureImage(frescoBuilder.mFailureImage);
        }

        if (frescoBuilder.mBackgroundImage != null) {
            builderM.setBackground(frescoBuilder.mBackgroundImage);
        }

        if (frescoBuilder.mIsCircle) {

            if (frescoBuilder.mIsBorder) {
                //預設白色包邊
                builderM.setRoundingParams(RoundingParams.asCircle().setBorder(0xFFFFFFFF, 2));
            } else {
                builderM.setRoundingParams(RoundingParams.asCircle());
            }
//            builderM.setRoundingParams(RoundingParams.asCircle());
        }

        //如果圓角取預設值10 或者是已經修改過的mRadius值
        if (frescoBuilder.mIsRadius) {
            builderM.setRoundingParams(RoundingParams.fromCornersRadius(frescoBuilder.mRadius));
        }

    }

}

得到Bitmap物件

在使用Fresco存在這樣一個問題,把圖片快取封裝得太好,導致很難直接得到快取中的Bitmap物件,當然也不是不能。
根據:資料來源和資料訂閱者文中說明的程式碼。
我也在上面的封裝程式碼中加入了獲取Bitmap物件的回撥功能。使用如下在回撥中得到物件。

 ImageLoadBuilder.Start(getApplicationContext(), mImageUser, url)
                    .setBitmapDataSubscriber(new BaseBitmapDataSubscriber() {
                        @Override
                        protected void onNewResultImpl(Bitmap bitmap) {
                          if (bitmap == null) {
                                Logger.d("bitmap is null");
                            } else {
                                Logger.d("bitmap is not null");
                                Drawable backDrawable = new BitmapDrawable(getResources(), FastBlurUtil.doBlur(bitmap, 25, false));
                                if (Looper.getMainLooper() != Looper.myLooper()) {
                                    mAppBar.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            mAppBar.setBackground(backDrawable);
                                        }
                                    });
                                } else {
                                    mAppBar.setBackground(backDrawable);
                                }
                            }
                        }

                        @Override
                        protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {

                        }
                    })
                    .build();

提示:

千萬不要把bitmap複製給onNewResultImpl函式範圍之外的任何變數。訂閱者執行完操作之後,image pipeline 會回收這個bitmap,釋放記憶體。在這個函式範圍內再次使用這個Bitmap物件進行繪製將會導致IllegalStateException。

Bitmap物件物件回撥執行緒有可能是在UI主執行緒回撥,也有可能在子執行緒中回撥,如果需要更新UI,需要做判斷進行不同的邏輯處理。

我上面的程式碼,用bitmap物件構造Drawable物件使用是不會發生異常,已經足夠我的開發使用,以後想到什麼再更新。

總結

  • 本文主要是介紹Fresco和封裝理由
  • 封裝採用構造器模式,我之後會寫有關構造器模式的博文,幫助大家理解參考。
  • 最後介紹Fresco的複雜使用,經過實際執行是滿足功能需求,提供大家在使用Fresco的需要獲取Bitmap時的參考。

相關推薦

Fresco封裝使用說明以及獲取快取Bitmap物件

Fresco介紹 Fresco是facebook開源的圖片載入框架。 關於 Fresco Fresco 是一個強大的圖片載入元件。 Fresco 中設計有一個叫做 image pipeline 的模組。它負責從網路,從本地檔案系統,本地

android獲取友盟渠道名以及獲取application的meta-data

獲取友盟渠道名 /** * 獲取渠道名 * @param ctx 此處習慣性的設定為activity,實際上context就可以 * @return 如果沒有獲取成功,那麼返回值為空 */ public static St

session清理快取的理解如何清空快取的資料

尊重版權:http://blog.sina.com.cn/s/blog_62a151be0100nf28.html Session執行一些sql語句把記憶體中的物件的狀態同步到資料庫,這個過程被稱為session清理. 在預設情況下,Session會在下面的時間點清理

【JAVA學習】session 清理快取的理解 如何 清空快取的資料

尊重版權:http://blog.sina.com.cn/s/blog_62a151be0100nf28.html Session執行一些sql語句把記憶體中的物件的狀態同步到資料庫,這個過程被稱為session清理. 在預設情況下,Session會在下面的時間點清理

在activity載入fragment的兩個方式以及獲取fragment的控制元件的方式

public class MainActivity extends Activity { Button show ,show2; FragmentManager fragmentManager; FragmentTransaction fragmentTransac

通過指令碼EL表示式獲取資料

<%@ page import="net.wen.bean.User" %> <%@ page import="java.util.List" %> <%@ page

在jsp使用js獲取session物件的例子。

如下: <%     String platform = Config.getString("platform.division");String webPath = request.getContextPath();String basePath = request

powerdesigner獲取pdm所有物件並修改的vbscript例子

powerdesigner是個好東西,設計工作中經常有些工作需要批量執行修改,採用其自帶的指令碼語言是個不錯的選則,下面這段指令碼把pdm中所有物件並修改,其中包含對多層package中物件的呼叫,很方便 Option ExplicitValidationMode = Tr

【Python學習日記】dicset 以及什麼是 不可變物件

轉載自 http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143167793538255 dict Python內建了字典:dict的支援,dict全稱

安卓 ListView中點選事件獲取Item物件

註明: ListView的物件 : mListView 繼承於BaseAdapter自定義的adapter ResultItem:每個Item資料的類private ListView mListVie

struts2的s:iterator標籤在jsp頁面獲取action陣列物件

在昨天的開發過程中,需要在jsp頁面獲取action中的陣列物件的值,技術水平有效,開始覺得無從下手,後來想寫入java程式片,抱著試試看的心態打出了<s:property value='action中的陣列名'/>  發現頁面顯示出了陣列中的每一個元素,想逐個

RootFramework框架實現文件讀取上傳以及使用JavaScript獲取fields的值

roo 讀取 frame get .get 轉換 script 輸入 tor 1、項目中使用的相對路徑需要轉換成絕對路徑,(Python方法實現 os.path.abspath(‘path‘)) 2、choose file [locators,file_pat

Map四種獲取keyvalue值的方法,以及對map的元素排序(轉)

compareto map.entry 排序 區別 sta hashmap 得到 package log 獲取map的值主要有四種方法,這四種方法又分為兩類,一類是調用map.keySet()方法來獲取key和value的值,另一類則是通過map.entrySet()方法來

總結Jquery獲取自定義屬性使用.attr().data()以及.prop()的區別

p.p1 { margin: 0.0px 0.0px 2.0px 0.0px; font: 14.0px ".PingFang SC" } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC" } p.p3 { margin:

命令列使用SVN以及指令獲取SVN的差異列表檔案其中的錯誤處理

當我們需要獲取SVN管理檔案中的差異列表的檔案,用來進行其他任務的時候: 我們需要使用的指令: svn diff -r COMMITTED:HEAD --summarize [需要獲取的庫的路徑] > [生成的差異列表檔案

jsstyle,currentStyle,getComputedStylegetBoundingClientRect的區別以及獲取css操作方法

js中style,currentStyle,getComputedStyle和getBoundingClientRect的區別以及獲取css操作方法 在js中,之前我們獲取屬性大多用的都是ele.style.border這種形式的方法,但是這種方法是有侷限性的,該方法只能獲取到行內樣式,獲取不

認識Javascript的navigator物件以及檢測瀏覽器型別版本,獲取瀏覽器版本號,檢測客戶端作業系統

Navigator物件包含了Web瀏覽器的基本資訊(如名稱,版本,作業系統等) 通過window.navigator方式可以引用該物件,並利用它的屬性讀取客戶端基本資訊 Navigator的5個主要屬性: appName:Web瀏覽器的名稱 appVersion:瀏覽器的

JqueryJS獲取ulli標籤的值以及賦值問題

通過點選li標籤,拿到li值,在把值賦給一個文字框 以下是HTML,以及input標籤 <input type="hidden" id="orderBy" value="${orderBy}" > <input type="hidd

javadate時間戳相互轉換以及獲取前一個小時的時間

1、時間戳是指檔案屬性裡的建立、修改、訪問時間。  數字時間戳技術是數字簽名技術一種變種的應用。在電子商務交易檔案中,時間是十分重要的資訊。在書面合同中,檔案簽署的日期和簽名一樣均是十分重要的防止檔案被偽造和篡改的關鍵性內容。數字時間戳服務(DTS:digital t

如何在EOS運算邏輯獲取sessionrequest以及application物件

在EOS的頁面流中是沒法獲取到這些物件,只能在呼叫的邏輯流中的運算邏輯中獲取到這些物件 @Bizlet("") public static void method() { IMapContextFactory contextFactory = com.primeton.ext.common.muo.MUODa