天天看點

圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

1:簡介

Fresco是Facebook最新推出的一款用于Android應用中展示圖檔的強大圖檔庫,可以從網絡、本地存儲和本地資源中加載圖檔。相對于ImageLoader,擁有更快的圖檔下載下傳速度以及可以加載和顯示gif圖等諸多優勢,是個很好的圖檔架構。

2:特點

2.1:記憶體管理

在5.0以下系統,Fresco将圖檔放到一個特别的記憶體區域。當然,在圖檔不顯示的時候,占用的記憶體會自動被釋放。這會使得APP更加流暢,減少因圖檔記憶體占用而引發的OOM。

記憶體配置設定采用:系統匿名共享記憶體

2.2:漸進式呈現圖檔

漸進式圖檔格式先呈現大緻的圖檔輪廓,然後随着圖檔下載下傳的繼續,呈現逐漸清晰的圖檔,這對于移動裝置,尤其是慢網絡有極大的利好,可帶來更好的使用者體驗。

2.3:支援加載Gif圖,支援WebP格式

2.4:圖像的呈現

  1. 自定義居中焦點(對人臉等圖檔顯示非常有幫助)。
  2. 圓角圖,當然圓圈也行。
  3. 下載下傳失敗之後,點選重新下載下傳。
  4. 自定義占位圖,自定義overlay, 或者進度條。
  5. 指定使用者按壓時的overlay。

2.5:圖像的加載

  1. 為同一個圖檔指定不同的遠端路徑,或者使用已經存在本地緩存中的圖檔。
  2. 先顯示一個低解析度的圖檔,等高清圖下載下傳完之後再顯示高清圖。
  3. 加載完成回調通知。
  4. 對于本地圖,如有EXIF縮略圖,在大圖加載完成之前,可先顯示縮略圖。
  5. 縮放或者旋轉圖檔。
  6. 處理已下載下傳的圖檔。

3:下載下傳位址

Github下載下傳位址是:https://github.com/facebook/fresco

官方使用網址:http://fresco-cn.org/docs/index.html

中文文檔位址:https://www.fresco-cn.org/docs/index.html

4:支援的URI

類型 Scheme 示例
遠端圖檔 http://, https:// HttpURLConnection
本地檔案 file:// FileInputStream
Content provider content:// ContentResolver
asset目錄下的資源 asset:// AssetManager
res目錄下的資源 res:// Resources.openRawResource

需要注意的是:Fresco中所有的URI都必須是絕對路徑,并且帶上該URI的scheme

例如,要顯示res/drawable下的一張圖檔可以使用下面的方式:

上面的額”aaaaa”可以使用随意的字元串,不過還是建議使用包名來書寫。

5:常用API

1:寬度不支援wrap_content, 如果要設定寬高比, 需要在Java代碼中指定setAspectRatio(float ratio);

2:高度不支援wrap_content

3:下載下傳成功之前顯示的圖檔

4:設定圖檔縮放. 通常使用focusCrop,該屬性值會通過算法把人頭像放在中間

5:加載失敗的時候顯示的圖檔

6:加載失敗的時候圖檔的縮放類型

7:加載失敗,提示使用者點選重新加載的圖檔(會覆寫failureImage的圖檔)

8:設定圓形方式顯示圖檔

9:圓角設定

fresco:roundedCornerRadius="1dp"
    fresco:roundTopLeft="true"
    fresco:roundTopRight="false"
    fresco:roundBottomLeft="false"
    fresco:roundBottomRight="true"
    fresco:roundWithOverlayColor="@color/corner_color"
    fresco:roundingBorderWidth="2dp"
    fresco:roundingBorderColor="@color/border_color"
           

6:使用步驟

6.1:添加依賴

dependencies {
  // 在 API < 14 上的機器支援 WebP 時,需要添加
  compile 'com.facebook.fresco:animated-base-support:0.14.1'

  // 支援 GIF 動圖,需要添加
  compile 'com.facebook.fresco:animated-gif:0.14.1'

  // 支援 WebP (靜态圖+動圖),需要添加
  compile 'com.facebook.fresco:animated-webp:0.14.1'
  compile 'com.facebook.fresco:webpsupport:0.14.1'

  // 僅支援 WebP 靜态圖,需要添加
  compile 'com.facebook.fresco:webpsupport:0.14.1'
  // 其他依賴
  compile 'com.facebook.fresco:fresco:0.14.1'
}
           

fresco最新版本是:1.0.0

6.2:在application中初始化Fresco

Fresco.initialize(this);
           

6.3:配置網絡權限

6.4:在xml布局檔案中,加入命名空間

<!-- 其他元素-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:layout_width="match_parent">
           

6.5:在xml中引入SimpleDraweeView

<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/my_image_view"
    android:layout_width="130dp"
    android:layout_height="130dp"
    fresco:placeholderImage="@drawable/my_drawable"
  />
           

6.6:在Java代碼中開始加載圖檔

Uri uri = Uri.parse("hhttp://p1.so.qhmsg.com/bdr/_240_/t01ffd622bffeabb5e1.jpg");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);
           

6.7:注意事項

如果項目中使用了OkHttp需要進行替換,

For OkHttp2:
compile "com.facebook.fresco:imagepipeline-okhttp:0.12.0+"
           
For OkHttp3:
compile "com.facebook.fresco:imagepipeline-okhttp3:0.12.0+"
           

7:例子

7.1:帶進度條的圖檔

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_fresco_spimg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.bruce.chang.testfresco.FrescoSpimgActivity">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco"
        android:layout_width="150dp"
        android:layout_height="150dp"
        fresco:placeholderImage="@mipmap/fbb"
        />
    <Button
        android:id="@+id/bt_load"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="加載"
        />
</LinearLayout>
           
代碼
setTitle("帶進度條的圖檔");
        bt_load.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 設定帶進度條樣式
                GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(getResources());
                GenericDraweeHierarchy hierarchy = builder.setProgressBarImage(new ProgressBarDrawable()).build();
                sdv_fresco.setHierarchy(hierarchy);
                Uri uri = Uri.parse("http://bizhi.zhuoku.com/bizhi/200706/4/20070625/bingbing/012.jpg");
                // 設定顯示圖檔
                sdv_fresco.setImageURI(uri);
            }
        });
           
結果,因為布局中設定了placeholderImage,是以會加載一張預設圖檔
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.2:圖檔的不同裁剪

布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco_crop"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_marginTop="48dp"
        android:background="@android:color/black"
        fresco:placeholderImage="@mipmap/fbb" />
    <!--裁剪方式的描述資訊-->
    <TextView
        android:id="@+id/tv_fresco_explain"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:textSize="16sp" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="48dp"
            android:orientation="vertical">
            <!--9個代表不同裁剪樣式的button,這裡省略-->

        </LinearLayout>
    </ScrollView>

</LinearLayout>
           
代碼設定
  1. GenericDraweeHierarchyBuilder的初始化
  1. imageDisplay(加載圖檔的方法)
//顯示圖檔
    private void (GenericDraweeHierarchy hierarchy) {
        sdv_fresco_crop.setHierarchy(hierarchy);
        Uri uri = Uri.parse("http://bizhi.zhuoku.com/bizhi/200706/4/20070625/bingbing/012.jpg");
        sdv_fresco_crop.setImageURI(uri);
    }
           
  1. 9個按鈕的方法
@Override
    public void onClick(View view) {
        switch (view.getId()) {
            //CENTER
            case R.id.bt_fresco_center:
                tv_fresco_explain.setText("居中,無縮放");
                //樣式設定
                GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.CENTER).build();
                //顯示圖檔
                imageDisplay(hierarchy);
                break;
            //CENTER_CROP
            case R.id.bt_fresco_centercrop:
                tv_fresco_explain.setText("保持寬高比縮小或放大,使得兩邊都大于或等于顯示邊界。居中顯示");
                //樣式設定
                GenericDraweeHierarchy hierarchy1 = builder.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP).build();
                //顯示圖檔
                imageDisplay(hierarchy1);
                break;
            //FOCUS_CROP
            case R.id.bt_fresco_focuscrop:
                tv_fresco_explain.setText("同centerCrop, 但居中點不是中點,而是指定的某個點,這裡我設定為圖檔的左上角那點");
                //設定focusCrop的縮放形式  并指定縮放的中心點在左上角
                PointF point = new PointF(f, f);
                //樣式設定
                GenericDraweeHierarchy hierarchy2 = builder.setActualImageScaleType(ScalingUtils.ScaleType.FOCUS_CROP)
                        .setActualImageFocusPoint(point)
                        .build();
                //顯示圖檔
                imageDisplay(hierarchy2);
                break;
            //CENTER_INSIDE
            case R.id.bt_fresco_centerinside:
                tv_fresco_explain.setText("使兩邊都在顯示邊界内,居中顯示。如果圖尺寸大于顯示邊界,則保持長寬比縮小圖檔");
                GenericDraweeHierarchy hierarchy3 = builder.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_INSIDE).build();
                imageDisplay(hierarchy3);
                break;
            //FIT_CENTER
            case R.id.bt_fresco_fitcenter:
                tv_fresco_explain.setText("保持寬高比,縮小或者放大,使得圖檔完全顯示在顯示邊界内。居中顯示");
                GenericDraweeHierarchy hierarchy4 = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER).build();
                imageDisplay(hierarchy4);
                break;
            //FIT_START
            case R.id.bt_fresco_fitstart:
                tv_fresco_explain.setText("保持寬高比,縮小或者放大,使得圖檔完全顯示在顯示邊界内,不居中,和顯示邊界左上對齊");
                GenericDraweeHierarchy hierarchy5 = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_START).build();
                imageDisplay(hierarchy5);
                break;
            //FIT_END
            case R.id.bt_fresco_fitend:
                tv_fresco_explain.setText("保持寬高比,縮小或者放大,使得圖檔完全顯示在顯示邊界内,不居中,和顯示邊界右下對齊");
                GenericDraweeHierarchy hierarchy6 = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_END).build();
                imageDisplay(hierarchy6);
                break;
            //FIT_XY
            case R.id.bt_fresco_fitxy:
                tv_fresco_explain.setText("不保持寬高比,填充滿顯示邊界");
                GenericDraweeHierarchy hierarchy7 = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_XY).build();
                imageDisplay(hierarchy7);
                break;
            //null
            case R.id.bt_fresco_none:
                tv_fresco_explain.setText("不設定任何樣式");
                GenericDraweeHierarchy hierarchy8 = builder.setActualImageScaleType(null).build();
                imageDisplay(hierarchy8);
                break;
            default:
                break;
        }
    }
           
結果,因為圖檔大小的緣故,沒有完全示範,自己可以動手試試
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.3:圓形和圓角圖檔

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco_circleandcorner"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/bt_fresco_circle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="設定圓形圖檔" />

    <Button
        android:id="@+id/bt_fresco_corner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="設定圓角圖檔" />

</LinearLayout>
           
代碼
uri = Uri.parse("http://bizhi.zhuoku.com/bizhi/200706/4/20070625/bingbing/012.jpg");
         builder = new GenericDraweeHierarchyBuilder(getResources());

        bt_fresco_circle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 參數設定為圓形
                RoundingParams params = RoundingParams.asCircle();
                GenericDraweeHierarchy hierarchy = builder.setRoundingParams(params).build();
                sdv_fresco_circleandcorner.setHierarchy(hierarchy);
                sdv_fresco_circleandcorner.setImageURI(uri);
            }
        });

        bt_fresco_corner.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 配置參數
                RoundingParams params = RoundingParams.fromCornersRadius(f);//設定圓角大小
                params.setOverlayColor(getResources().getColor(android.R.color.holo_blue_light));//覆寫層
                params.setBorder(getResources().getColor(android.R.color.holo_blue_light), );//邊框
//                params.setRoundAsCircle(true);//如果是RoundingParams.fromCornersRadius,這個可以強制進行圓形展示
                // 設定圓形參數
                GenericDraweeHierarchy hierarchy = builder.setRoundingParams(params).build();
                sdv_fresco_circleandcorner.setHierarchy(hierarchy);
                // 加載圖檔
                sdv_fresco_circleandcorner.setImageURI(uri);
            }
        });
           
結果
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.4:漸進式顯示圖檔

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_fresco_jpeg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.facebook.drawee.view.SimpleDraweeView
        android:layout_gravity="center"
        android:id="@+id/sdv_fresco_jpeg"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_marginTop="48dp"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/sdv_fresco_askImg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="請求網絡圖檔" />
</LinearLayout>
           
代碼
builder = new GenericDraweeHierarchyBuilder(getResources());

        sdv_fresco_askImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 加載品質配置
                ProgressiveJpegConfig jpegConfig = new ProgressiveJpegConfig() {
                    @Override
                    public int getNextScanNumberToDecode(int scanNumber) {
                        return scanNumber + ;
                    }

                    @Override
                    public QualityInfo getQualityInfo(int scanNumber) {
                        boolean isGoodEnough = (scanNumber >= );

                        return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);
                    }
                };

                ImagePipelineConfig.newBuilder(FrescoJianJinShiActivity.this).setProgressiveJpegConfig(jpegConfig).build();
                // 擷取圖檔URL
                uri = Uri.parse("http://bizhi.zhuoku.com/bizhi/200706/4/20070625/bingbing/012.jpg");

                // 擷取圖檔請求
                ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).setProgressiveRenderingEnabled(true).build();

                DraweeController draweeController = Fresco.newDraweeControllerBuilder()
                        .setImageRequest(request)
                        .setTapToRetryEnabled(true)
                        .setOldController(sdv_fresco_jpeg.getController())//使用oldController可以節省不必要的記憶體配置設定
                        .build();

                // 設定加載的控制
                sdv_fresco_jpeg.setController(draweeController);
            }
        });
           
結果
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.5:Gif動畫圖檔

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_fresco_gif"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco_gif"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_marginTop="48dp"
        android:layout_gravity="center"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/bt_fresco_askImg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="請求gif圖檔" />

    <Button
        android:id="@+id/bt_fresco_stopAnim"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="動畫停止" />

    <Button
        android:id="@+id/bt_fresco_startAnim"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="動畫開始" />
</LinearLayout>
           
代碼
  1. 請求Gif圖檔
bt_fresco_askImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 圖檔位址
                Uri uri = Uri.parse("http://www.sznews.com/humor/attachement/gif/site3/20140902/4487fcd7fc66156f51db5d.gif");
                DraweeController controller = Fresco.newDraweeControllerBuilder()
                        .setUri(uri)
                        .setAutoPlayAnimations(false)//設定為true将循環播放Gif動畫
                        .setOldController(sdv_fresco_gif.getController())
                        .build();

                // 設定控制器
                sdv_fresco_gif.setController(controller);
            }
        });
           
  1. 開始動畫
bt_fresco_startAnim.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Animatable animation = sdv_fresco_gif.getController().getAnimatable();

                if (animation != null && !animation.isRunning()) {
                    animation.start();
                }
            }
        });
           
  1. 停止動畫
bt_fresco_stopAnim.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Animatable animation = sdv_fresco_gif.getController().getAnimatable();

                if (animation != null && animation.isRunning()) {
                    animation.stop();
                }
            }
        });
           
結果(不要忘了在gradle中添加gif的依賴)
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.6:多圖請求以及圖檔複用

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_fresco_multi"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco_multi"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center"
        android:layout_marginTop="48dp"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/bt_fresco_multiImg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="先顯示低分辨率的圖,然後是高分辨率的圖"
        android:onClick="bt_fresco_multiImg_click"
        />

    <Button
        android:id="@+id/bt_fresco_thumbnailImg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="本地縮略圖預覽"
        android:onClick="bt_fresco_thumbnailImg_click"
        />

    <Button
        android:id="@+id/bt_fresco_multiplexImg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="本地圖檔複用"
        android:onClick="bt_fresco_multiplexImg_click"
        />

</LinearLayout>
           
代碼
  1. 先顯示低分辨率的圖,然後是高分辨率的圖
void bt_fresco_multiImg_click(View view) {
        //同一張圖檔,不同品質的兩個uri
        Uri lowResUri = Uri.parse("http://img1.gamedog.cn/2012/03/11/19-120311133617-50.jpg");
        Uri highResUri = Uri.parse("http://img5.duitang.com/uploads/item/201312/03/20131203153823_Y4y8F.jpeg");
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setLowResImageRequest(ImageRequest.fromUri(lowResUri))
                .setImageRequest(ImageRequest.fromUri(highResUri))
                .setOldController(sdv_fresco_multi.getController())
                .build();
        sdv_fresco_multi.setController(controller);
    }
           
  1. 本地縮略圖預覽
void bt_fresco_thumbnailImg_click(View view){

        //将本地圖檔位址轉換成Uri
        Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory()+"/swj.jpg"));
        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setLocalThumbnailPreviewsEnabled(true)
                .build();
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(request)
                .setOldController(sdv_fresco_multi.getController())
                .build();
        sdv_fresco_multi.setController(controller);
    }
           
  1. 本地圖檔複用
void bt_fresco_multiplexImg_click(View view){

        //本地圖檔的複用
        //在請求之前,還會去記憶體中請求一次圖檔,沒有才會先去本地,最後去網絡uri

        //本地準備複用圖檔的uri  如果本地這個圖檔不存在,會自動去加載下一個uri
        Uri uri1 = Uri.fromFile(new File(Environment.getExternalStorageDirectory()+"/swj.jpg"));
        //圖檔的網絡uri
        Uri uri2 = Uri.parse("http://img5.duitang.com/uploads/item/201312/03/20131203153823_Y4y8F.jpeg");
        ImageRequest request1 = ImageRequest.fromUri(uri1);
        ImageRequest request2 = ImageRequest.fromUri(uri2);
        ImageRequest[] requests = {request1, request2};
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setFirstAvailableImageRequests(requests)
                .setOldController(sdv_fresco_multi.getController())
                .build();
        sdv_fresco_multi.setController(controller);
    }
           
結果
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.7:圖檔加載監聽

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_fresco_listener"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <com.facebook.drawee.view.SimpleDraweeView
        android:layout_gravity="center"
        android:id="@+id/sdv_fresco_listener"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_marginTop="48dp"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/bt_fresco_listener"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="開始加載圖檔"
        android:onClick="bt_fresco_listener_click"
        />

    <TextView
        android:id="@+id/tv_fresco_listener"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="14sp"
        android:text="tv_fresco_listener"
        />

    <TextView
        android:id="@+id/tv_fresco_listener2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="14sp"
        android:text="tv_fresco_listener2"
        />
</LinearLayout>
           
代碼
  1. 自定義BaseControllerListener初始化
//對所有的圖檔加載,onFinalImageSet 或者 onFailure 都會被觸發。前者在成功時,後者在失敗時。
    //如果允許呈現漸進式JPEG,同時圖檔也是漸進式圖檔,onIntermediateImageSet會在每個掃描被解碼後回調。
    // 具體圖檔的那個掃描會被解碼,參見漸進式JPEG圖
    private ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
        @Override
        public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
           //前者在成功時
            super.onFinalImageSet(id, imageInfo, animatable);

            if (imageInfo == null) {
                return;
            }

            QualityInfo qualityInfo = imageInfo.getQualityInfo();

            tv_fresco_listener.setText("Final image received! " +
                    "\nSize: " + imageInfo.getWidth()
                    + "x" + imageInfo.getHeight()
                    + "\nQuality level: " + qualityInfo.getQuality()
                    + "\ngood enough: " + qualityInfo.isOfGoodEnoughQuality()
                    + "\nfull quality: " + qualityInfo.isOfFullQuality());
        }

        @Override
        public void onIntermediateImageSet(String id, ImageInfo imageInfo) {
            super.onIntermediateImageSet(id, imageInfo);

            tv_fresco_listener2.setText("IntermediateImageSet image receiced");
        }
        //後者在失敗時
        @Override
        public void onFailure(String id, Throwable throwable) {
            super.onFailure(id, throwable);

            tv_fresco_listener.setText("Error loading" + id);
        }
    };
           
  1. 開始加載圖檔按鈕方法
void bt_fresco_listener_click(View view) {

        ProgressiveJpegConfig jpegConfig = new ProgressiveJpegConfig() {
            @Override
            public int getNextScanNumberToDecode(int scanNumber) {
                return scanNumber + ;
            }

            @Override
            public QualityInfo getQualityInfo(int scanNumber) {
                boolean isGoodEnough = (scanNumber >= );

                return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);
            }
        };

        ImagePipelineConfig.newBuilder(this).setProgressiveJpegConfig(jpegConfig).build();
        Uri uri = Uri.parse("http://h.hiphotos.baidu.com/zhidao/pic/item/58ee3d6d55fbb2fbac4f2af24f4a20a44723dcee.jpg");
        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).setProgressiveRenderingEnabled(true).build();

        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setOldController(sdv_fresco_listener.getController())
                .setImageRequest(request)
                //設定監聽器監聽圖檔加載狀态
                .setControllerListener(controllerListener)
                .build();

        sdv_fresco_listener.setController(controller);
    }
           
結果,加載成功的時候可以得到圖檔的相關資訊,如Size,在浏覽器中檢視,該圖檔的尺寸就是那麼大的。
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.8:圖檔縮放和旋轉

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_fresco_resize"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco_resize"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center"
        android:layout_marginTop="48dp"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/bt_fresco_resize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:onClick="bt_fresco_resize_click"
        android:text="修記憶體中改圖檔大小" />

    <Button
        android:id="@+id/bt_fresco_rotate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="bt_fresco_rotate_click"
        android:text="旋轉圖檔" />

</LinearLayout>
           
代碼
//縮放
    void bt_fresco_resize_click(View view) {

        int width = ;
        int height = ;
        Uri uri = Uri.parse("http://c.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd21a519680db30f2442a70fa1.jpg");
        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setResizeOptions(new ResizeOptions(width, height)).build();
        PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
                .setOldController(sdv_fresco_resize.getController())
                .setImageRequest(request)
                .build();
        sdv_fresco_resize.setController(controller);
    }

    //旋轉
    void bt_fresco_rotate_click(View view) {
        Uri uri2 = Uri.parse("http://c.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd21a519680db30f2442a70fa1.jpg");
        RotationOptions rotationOptions = RotationOptions.forceRotation(RotationOptions.ROTATE_90);
        ImageRequest request1 = ImageRequestBuilder.newBuilderWithSource(uri2)
                .setRotationOptions(RotationOptions.autoRotate())
                .setRotationOptions(rotationOptions)//旋轉的時候要使用RotationOptions類
                .build();//但貌似Fresco在旋轉的功能上不是很好
        DraweeController controller1 = Fresco.newDraweeControllerBuilder()
                .setImageRequest(request1)
                .setOldController(sdv_fresco_resize.getController()).build();
        sdv_fresco_resize.setController(controller1);
    }
           
結果
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.9:修改圖檔

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_fresco_modify"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv_fresco_modify"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center"
        android:layout_marginTop="48dp"
        fresco:placeholderImage="@mipmap/fbb" />

    <Button
        android:id="@+id/bt_fresco_modify"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:text="為圖檔添加網格"
        android:onClick="bt_fresco_modify_click"
        />
</LinearLayout>
           
代碼
void bt_fresco_modify_click(View view){

        Uri uri = Uri.parse("http://c.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd21a519680db30f2442a70fa1.jpg");

        Postprocessor redMeshPostprocessor = new BasePostprocessor() {
            @Override
            public String getName() {
                return "redMeshPostprocessor";
            }
            //繪制紅色點狀網格
            @Override
            public void process(Bitmap bitmap) {

                for (int x = ; x < bitmap.getWidth(); x += ) {

                    for (int y = ; y < bitmap.getHeight(); y += ) {
                        bitmap.setPixel(x, y, Color.RED);
                    }
                }
            }
        };
        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setPostprocessor(redMeshPostprocessor)
                .build();
        PipelineDraweeController controller = (PipelineDraweeController)
                Fresco.newDraweeControllerBuilder()
                        .setImageRequest(request)
                        .setOldController(sdv_fresco_modify.getController())
                        .build();
        sdv_fresco_modify.setController(controller);
    }
           
結果
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

7.10:

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_fresco_auto_size"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <Button
        android:id="@+id/bt_fresco_loadsmall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="加載圖檔"
        android:onClick="bt_fresco_loadsmall_click"
        />

    <LinearLayout
        android:id="@+id/ll_fresco"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="vertical">

    </LinearLayout>
</LinearLayout>
           
代碼
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fresco_dynamic_display_img);
        ll_fresco = (LinearLayout) findViewById(R.id.ll_fresco);
        setTitle("動态展示圖檔");

        simpleDraweeView = new SimpleDraweeView(this);
        //設定寬高比
        simpleDraweeView.setAspectRatio(f);
    }

    void bt_fresco_loadsmall_click(View view) {

        final Uri uri = Uri.parse("http://img4q.duitang.com/uploads/item/201304/27/20130427043538_wAfHC.jpeg");

        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .build();

        PipelineDraweeController controller = (PipelineDraweeController)
                Fresco.newDraweeControllerBuilder()
                        .setImageRequest(request)
                        .setOldController(simpleDraweeView.getController())
                        .build();
        simpleDraweeView.setController(controller);
        ll_fresco.addView(simpleDraweeView);
    }
           
結果
圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

8:注意事項

8.1:問題處理

  1. 重複的邊界
這是使用圓角的一個缺陷。 參考圓角和圓圈 來擷取更多資訊。
           
  1. 圖檔沒有加載
你可以從 image pipeline 打出的日志來檢視原因。 這裡提供一些通常會導緻問題的原因:
           
  1. 檔案不可用
無效的路徑、連結會導緻這種情況。
判斷網絡連結是否有效,你可以嘗試在浏覽器中打開它,看看是否圖檔會被加載。若圖檔依然加載不出來,那麼這不是Fresco的問題。
判斷本地檔案是否有效,你可以通過下面這段代碼來校驗:

FileInputStream fis = new FileInputStream(new File(localUri.getPath()));

如果這裡抛出了異常,那麼這不是Fresco的問題,可能是你的其他代碼導緻的。有可能是沒有擷取到SD卡讀取權限、路徑不合法、檔案不存在等。
           
  1. OOM - 無法配置設定圖檔空間
加載特别特别大的圖檔時最容易導緻這種情況。
如果你加載的圖檔比承載的View明顯大出太多,那你應該考慮将它Resize一下。
           
  1. Bitmap太大導緻無法繪制
Android 無法繪制長或寬大于2048像素的圖檔。
這是由OpenGL渲染系統限制的,如果它超過了這個界限,Fresco會對它進行Resize。
           
  1. 通過Logcat來判斷原因
在加載圖檔時會出現各種各樣的原因導緻加載失敗。
在使用Fresco的時候,最直接的方式就是檢視 image pipeline 打出的VERBOSE級别日志。
           
  1. 啟動日志
預設情況下Fresco是關閉日志輸出的,你可以配置image pipeline讓它啟動.

Set<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new RequestLoggingListener());
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
   // other setters
   .setRequestListeners(requestListeners)
   .build();
Fresco.initialize(context, config);
FLog.setMinimumLoggingLevel(FLog.VERBOSE);
           
  1. 檢視日志
你可以通過下面這條shell指令來檢視Fresco日志:

adb logcat -v threadtime | grep -iE 'LoggingListener|AbstractDraweeController|BufferedDiskCache'

它的輸出為如下格式:
- ::   V unknown:RequestLoggingListener: time : onProducerStart: {requestId: , producer: EncodedMemoryCacheProducer}
- ::   V unknown:RequestLoggingListener: time : onProducerFinishWithSuccess: {requestId: , producer: EncodedMemoryCacheProducer, elapsedTime:  ms, extraMap: {cached_value_found=false}}
- ::   V unknown:RequestLoggingListener: time : onProducerStart: {requestId: , producer: DiskCacheProducer}
- ::   V unknown:BufferedDiskCache: Did not find image for http://www.example.com/image.jpg in staging area
- ::   V unknown:BufferedDiskCache: Disk cache read for http://www.example.com/image.jpg
- ::   V unknown:BufferedDiskCache: Disk cache miss for http://www.example.com/image.jpg
- ::   V unknown:RequestLoggingListener: time : onProducerFinishWithSuccess: {requestId: , producer: DiskCacheProducer, elapsedTime:  ms, extraMap: {cached_value_found=false}}
- ::   V unknown:RequestLoggingListener: time : onProducerStart: {requestId: , producer: NetworkFetchProducer}
- ::   V unknown:RequestLoggingListener: time : onProducerFinishWithSuccess: {requestId: , producer: NetworkFetchProducer, elapsedTime:  ms, extraMap: null}
- ::   V unknown:BufferedDiskCache: About to write to disk-cache for key http://www.example.com/image.jpg
- ::   V unknown:RequestLoggingListener: time : onProducerStart: {requestId: , producer: DecodeProducer}
- ::   V unknown:BufferedDiskCache: Successful disk-cache write for key http://www.example.com/image.jpg
- ::   V unknown:RequestLoggingListener: time : onProducerFinishWithSuccess: {requestId: , producer: DecodeProducer, elapsedTime:  ms, extraMap: {hasGoodQuality=true, queueTime=, bitmapSize=x400, isFinal=true}}
- ::   V unknown:RequestLoggingListener: time : onRequestSuccess: {requestId: , elapsedTime:  ms}
- ::   V unknown:AbstractDraweeController: controller ebe0eb : set_final_result @ onNewResult: image: CloseableReference fd41bb0

在這個示例中,我們發現名為ebe0eb的 DraweeView 向名為的 DataSource 進行了圖像請求。
首先,圖檔沒有在記憶體緩存中找到,也沒有在磁盤緩存中找到,最後去網絡上下載下傳圖檔。下載下傳成功後,圖檔被解碼,之後請求結束。
最後資料源通知 controller 圖檔就緒,顯示圖檔(set_final_result)。
           

8.2:一些陷阱

  1. 不要使用 ScrollViews界
如果你想要在一個長的圖檔清單中滑動,你應該使用 RecyclerView,ListView,或 GridView。這三者都會在你滑動時不斷重用子視圖。Fresco 的 view 會接收系統事件,使它們能正确管理記憶體。
ScrollView 不會這樣做。是以,Fresco view 不會被告知它們是否在螢幕上顯示,并保持圖檔記憶體占用直到你的 Fragment 或 Activity 停止。
你的 App 将會面臨更大的 OOM 風險。
           
  1. 不要向下轉換
不要試圖把Fresco傳回的一些對象進行向下轉化,這也許會帶來一些對象操作上的便利
,但是也許在後續的版本中,你會遇到一些因為向下轉換特性丢失導緻的難以處理的問題。
           
  1. 不要使用getTopLevelDrawable
DraweeHierarchy.getTopLevelDrawable() 僅僅 應該在DraweeViews中用,除了定義View中,其他應用代碼建議連碰都不要碰這個。

在自定義view中,也千萬不要将傳回值向下轉換,也許下個版本,我們會更改這個傳回值類型。
           
  1. 不要複用 DraweeHierarchies
永遠不要把 DraweeHierarchy 通過 DraweeView.setHierarchy 設定給不同的View。
DraweeHierarchy 是由一系列 Drawable 組成的。在 Android 中, Drawable 不能被多個 View 共享。
           
  1. 不要在多個DraweeHierarchy中使用同一個Drawable
原因同上。不過你可以在占位圖、重試圖、錯誤圖中使用相同的資源ID,Android 實際會建立不同的 Drawable。

如果你使用GenericDraweeHierarchyBuilder,那麼需要調用Resources.getDrawable來通過資源擷取圖檔。

不過請不要隻調用一次然後将結果傳給不同的Hierarchy!
           
  1. 不要直接控制 hierarchy
不要直接使用 SettableDraweeHierarchy 方法(reset,setImage,…)。

它們應該僅由 controller 使用。

不要使用setControllerOverlay來設定一個覆寫圖,這個方法隻能給 controller 調用。
如果你需要顯示覆寫圖,可以參考Drawee的各種效果配置
           
  1. 不要直接給 DraweeView 設定圖檔
目前 DraweeView 直接繼承于 ImageView,是以它有 setImageBitmap,
setImageDrawable 等方法。
如果利用這些方法直接設定一張圖檔,内部的 DraweeHierarchy 就會丢失,也就無法取到image
pipeline 的任何圖像了。
           
  1. 使用 DraweeView 時,請不要使用任何 ImageView 的屬性
在後續的版本中,DraweeView 會直接從 View 派生。
任何屬于 ImageView 但是不屬于 View 的方法都會被移除。
           

8.3:為什麼不支援wrap_content

人們經常會問,為什麼Fresco中不可以使用wrap_content?

主要的原因是,Drawee永遠會在getIntrinsicHeight/getIntrinsicWidth中傳回-。
這麼做的原因是 Drawee 不像ImageView一樣。它同一時刻可能會顯示多個元素。比如在從占位圖漸變到目标圖
時,兩張圖會有同時顯示的時候。再比如可能有多張目标圖檔(低清晰度、高清晰度兩張)。如果這些圖像都是不同的尺寸,那麼很難定義”intrinsic”尺寸。

如果我們要先用占位圖的尺寸,等加載完成後再使用真實圖的尺寸,那麼圖檔很可能顯
示錯誤。它可能會被根據占位圖的尺寸來縮放、裁剪。
唯一防止這種事情的方式就隻有在圖檔加載完成後強制觸發一次layout。這樣的話不僅
會影響性能,而且會讓應用的界面突變,很影響使用者體驗!
如果使用者正在讀一篇文章,然後在圖檔加載完成後整篇文章突然向下移動,這是非常不好的。

是以你必須指定尺寸或者用match_parent來布局。

你如果從服務端請求圖檔,服務端可以做到傳回圖檔尺寸。然後你拿到之後通過setLayoutParams 來給View設定寬高。

當然如果你必須要使用wrap_content,那麼你可以參考StackOverflow上的一個回答。
但是我們以後會移除這個功能,Ugly things should look ugly。
           

8.4:共享元素動畫

使用 ChangeBounds,而不是ChangeImageTransform

Android  (Lollipop) 引入了 共享元素動畫,允許在多個Activity切換時共享相同的View!

你可以在XML中定義這個變換。有個ChangeImageTransform變換可以在共享元素切換時對ImageView進行變換,可惜Fresco暫時不支援它,因為Drawee維護着自己的轉換Matrix。

幸運的是你可以有另一種做法:使用ChangeBounds。你可以改變layout的邊界,這樣Fresco會根據它進行自适應,也能夠達到你想要的功能。
           

9:總結

Fresco的使用在一定程度上要比Picasso、GLide要複雜,但是在強大的Fresco面前,複雜并不是理由,隻要靈活的使用Fresco,一定會給我們的開發帶來便利。

有關Fresco的任何問題可以檢視Fresco的中文API網址來尋求解決方案。

https://www.fresco-cn.org/docs/index.html

歡迎通路201216323.tech來檢視我的CSDN部落格。

歡迎關注我的個人技術公衆号,快速檢視我的最新文章。

圖檔加載架構之Fresco1:簡介2:特點3:下載下傳位址4:支援的URI5:常用API6:使用步驟7:例子8:注意事項9:總結

繼續閱讀