1. 程式人生 > >Android四大圖片快取框架之-Fresco(一)

Android四大圖片快取框架之-Fresco(一)

本文來自於Fresco中文文件,這僅僅是自己的學習筆記!!!大牛繞路,放我我。
關於Fresco的介紹,請檢視連結
關於android圖片快取,這是一個android程式設計師必須瞭解的。關於四大圖片快取框架的特性與對比,請移步MDCC傳送門

首先說明,本文的大多數內容來自於官方文件,請勿噴!!!
那麼今天我們就來了解下Fresco,作為FB出版的開源專案,據說是目前最好的快取框架。

那麼我們就先來了解下Fresco是個什麼。

  • Fresco是一個強大的圖片載入元件
  • Fresco中設計有一個叫做image pipeline 的模組。他負責從網路,從本地檔案系統,本地資源載入圖片。為了最大限度上節省空間和CPU時間,它含有3級快取的設計(額,沒三級能拿出手?)
  • Fresco中設計有一個叫做Drawees模組,方便地顯示loading圖,當圖片不再顯示在螢幕上時,及時地釋放記憶體和空間佔用。
  • Fresco支援Android2.3及以上系統

簡單的看下使用SimpleDraweeView顯示一張佔位圖。在XML檔案中加入

<com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/my_image_view"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
> </com.facebook.drawee.view.SimpleDraweeView>

在程式碼中設定Uri,

draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
        Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");
//        draweeView.setController(draweeController);
draweeView.setImageURI(uri);

最後新增網路許可權,就可以了。我們在來看下這裡的Uri支援什麼格式:

  • 遠端圖片 http:// 或者 https://
  • 本地檔案 file://
  • Content Provider content://
  • asset目錄下的資源 asset://
  • res目錄下的資源 res://包名/+R……

接下來便詳細的介紹Drawee
舉個栗子:

<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/my_image_view"
    android:layout_width="20dp"
    android:layout_height="20dp"
    fresco:fadeDuration="300"
    fresco:actualImageScaleType="focusCrop"
    fresco:placeholderImage="@color/wait_color"
    fresco:placeholderImageScaleType="fitCenter"
    fresco:failureImage="@drawable/error"
    fresco:failureImageScaleType="centerInside"
    fresco:retryImage="@drawable/retrying"
    fresco:retryImageScaleType="centerCrop"
    fresco:progressBarImage="@drawable/progress_bar"
    fresco:progressBarImageScaleType="centerInside"
    fresco:progressBarAutoRotateInterval="1000"
    fresco:backgroundImage="@color/blue"
    fresco:overlayImage="@drawable/watermark"
    fresco:pressedStateOverlayImage="@color/red"
    fresco:roundAsCircle="false"
    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"
  />

上面這個是官方的栗子,上面有所有的屬性,我們就來看看,這些屬性代表什麼意思,

  • layout_width和layout_height 不支援warp_content,但是可以通過setAspectRetio();來設定寬高比
  • fadeDuration() 淡..時間
  • actualImageScaleType 設定圖片縮放,通常使用foucsCrop,該屬性值會通過演算法把人頭像放在中間,關於縮放,請移步黑洞
  • placeholderImage 下載成功之前顯示的圖片
  • placeholderImageScaleType
  • failureImage 載入失敗時顯示的圖片
  • failureImageScaleType
  • retryImage 載入失敗,提示使用者點選重新載入的圖片
  • retryImageScaleType
  • progressBarImage 提示 使用者正在載入,和進度無關
  • progressBarImageScaleType
  • progressBarAutoRotateInterval 圖片自動旋轉的時間間隔
  • backgroundImage 背景
  • overlayImage 疊加圖
  • pressedStateOverlayImage 按下時候的疊加圖
  • roundAsCircle 是否涉及圓圈
  • roundedCornerRadius 圓角
  • roundTopLeft、roundTopRight….. 分別設定4個角不同半徑,設定為true以後可以再程式碼中設定角度。通過RoundingParams的setConnersRadii()方法
public RoundingParams setCornersRadii(float topLeft, float topRight, float bottomRight, float bottomLeft) {
        float[] radii = this.getOrCreateRoundedCornersRadii();
        radii[0] = radii[1] = topLeft;
        radii[2] = radii[3] = topRight;
        radii[4] = radii[5] = bottomRight;
        radii[6] = radii[7] = bottomLeft;
        return this;
    }
  • roundWithOverlayColor,邊框的疊加顏色
  • roundingBorderWidth 邊框寬度
  • roundingBorderColor 邊框顏色

    我們看到,僅僅靠xml檔案的屬性,功能就已經很強大了。我們通過什麼來在程式碼中設定各種效果呢?看程式碼

GenericDraweeHierarchyBuilder builder =
    new GenericDraweeHierarchyBuilder(getResources());
GenericDraweeHierarchy hierarchy = builder
    .setFadeDuration(300)
    .setPlaceholderImage(new MyCustomDrawable())
    .setBackgrounds(backgroundList)
    .setOverlays(overlaysList)
    .build();
mSimpleDraweeView.setHierarchy(hierarchy);

DraweeHierarchy的一些屬性可以在執行時改變。

GenericDraweeHierarchy hierarchy =mSimpleDraweeView.getHierarchy();
hierarchy.setPlaceholderImage(R.drawable.placeholderId);

注意:對於同一個View,不要多次呼叫setHierarchy。

我們看看GenericDraweeHierarchy提供了哪些set方法
看這裡看這裡
看到這裡有相當多的drawable啊。我們在來看看,Fresco原始碼中有哪些Drawable,
drawable
我們在set…(new ..Drawable),就可以輕鬆使用了。我們以ProgressBarDrawable為例,看看他提供了哪些方法。
,看起來還是很強大的。當然,我們可以自定義Drawable,關於自定義Drawable,我這裡就不再說了,網上還是很多的。

我們在來看看ControllerBuilder,從字面上就可以看出,這個可以對圖片做出一些控制。

ControllerListener listener = new BaseControllerListener() {...}

DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setUri(uri)
    .setTapToRetryEnabled(true)
    .setOldController(mSimpleDraweeView.getController())
    .setControllerListener(listener)
    .build();

mSimpleDraweeView.setController(controller);

在指定一個新的controller的時候,使用setOldController,可以節省不必要的記憶體分配。
我們來看看Fresco.newDraweeControllerBuilder()有哪些set方法。

自定義圖片載入請求

Uri uri;
Postprocessor myPostprocessor = new Postprocessor() { ... }
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
    .setPostprocessor(myPostprocessor)
    .build();

DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setImageRequest(request)
    .setOldController(mSimpleDraweeView.getController())
    // 其他設定
    .build();

漸進式JPEG圖

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

  public QualityInfo getQualityInfo(int scanNumber) {
    boolean isGoodEnough = (scanNumber >= 5);
    return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);
  }
}

ImagePipelineConfig config = ImagePipelineConfig.newBuilder()
    .setProgressiveJpegConfig(pjpeg)
    .build();
Uri uri;
ImageRequest request = ImageRequestBuilder
    .newBuilderWithSource(uri)
    .setProgressiveRenderingEnabled(true)
    .build();
PipelineDraweeController controller = Fresco.newControllerBuilder()
    .setImageRequest(requests)
    .setOldController(mSimpleDraweeView.getController())
    .build();

mSimpleDraweeView.setController(controller);

Gif動態圖

Uri uri;
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
    . // other setters
    .build();

DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setImageRequest(request)
    .setAutoPlayAnimations(true)
    . // other setters
    .build();
mSimpleDraweeView.setController(controller);

手動控制動態圖播放

ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
    @Override
    public void onFinalImageSet(
        String id,
        @Nullable ImageInfo imageInfo,
        @Nullable Animatable anim) {
    if (anim != null) {
      // app-specific logic to enable animation starting
      anim.start();
    }
};

Uri uri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setUri(uri)
    .setControllerListener(controllerListener)
    // other setters
    .build();
mSimpleDraweeView.setController(controller);

使用動畫

Animatable animation = mSimpleDraweeView.getController().getAnimatable();
if (animation != null) {
  // 開始播放
  animation.start();
  // 一段時間之後,根據業務邏輯,停止播放
  animation.stop();
}

多圖請求以及圖片複用

Uri lowResUri, highResUri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setLowResImageRequest(ImageRequest.fromUri(lowResUri))
    .setImageRequest(ImageRequest.fromUri(highResUri))
    .setOldController(mSimpleDraweeView.getController())
    .build();
mSimpleDraweeView.setController(controller);

縮圖預覽

Uri uri;
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
    .setLocalThumbnailPreviewsEnabled(true)
    .build();

DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setImageRequest(request)
    .setOldController(mSimpleDraweeView.getController())
    .build();
mSimpleDraweeView.setController(controller);

本地圖片複用

Uri uri1, uri2;
ImageRequest request = ImageRequest.fromUri(uri1);
ImageRequest request2 = ImageRequest.fromUri(uri2);
ImageRequest[] requests = { request1, request2 };

DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setFirstAvailableImageRequests(requests)
    .setOldController(mSimpleDraweeView.getController())
    .build();
mSimpleDraweeView.setController(controller);

監聽下載事件

ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
    @Override
    public void onFinalImageSet(
        String id,
        @Nullable ImageInfo imageInfo,
        @Nullable Animatable anim) {
      if (imageInfo == null) {
        return;
      }
      QualityInfo qualityInfo = imageInfo.getQualityInfo();
      FLog.d("Final image received! " + 
          "Size %d x %d",
          "Quality level %d, good enough: %s, full quality: %s",
          imageInfo.getWidth(),
          imageInfo.getHeight(),
          qualityInfo.getQuality(),
          qualityInfo.isOfGoodEnoughQuality(),
          qualityInfo.isOfFullQuality());
    }

    @Override 
    public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
      FLog.d("Intermediate image received");
    }

    @Override
    public void onFailure(String id, Throwable throwable) {
      FLog.e(getClass(), throwable, "Error loading %s", id)
    }
};

Uri uri;
DraweeController controller = Fresco.newControllerBuilder()
    .setControllerListener(controllerListener)
    .setUri(uri);
    // other setters
    .build();
mSimpleDraweeView.setController(controller);

修改圖片尺寸

Uri uri = "file:///mnt/sdcard/MyApp/myfile.jpg";
int width = 50, height = 50;
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
    .setResizeOptions(new ResizeOptions(width, height))
    .build();
PipelineDraweeController controller = Fresco.newDraweeControllerBuilder()
    .setOldController(mDraweeView.getController())
    .setImageRequest(request)
    .build();
mSimpleDraweeView.setController(controller);

自動旋轉

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

圖片的修改。後處理器

Uri uri;
Postprocessor redMeshPostprocessor = new BasePostprocessor() { 
  @Override
  public String getName() {
    return "redMeshPostprocessor";
  }

  @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.RED);
      }
    }
  }
}

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

PipelineDraweeController controller = (PipelineDraweeController) 
    Fresco.newDraweeControllerBuilder()
    .setImageRequest(request)
    .setOldController(mSimpleDraweeView.getController())
    // other setters as you need
    .build();
mSimpleDraweeView.setController(controller);

圖片請求 Image Requests

Uri uri;

ImageDecodeOptions decodeOptions = ImageDecodeOptions.newBuilder()
    .setBackgroundColor(Color.GREEN)
    .build();

ImageRequest request = ImageRequestBuilder
    .newBuilderWithSource(uri)
    .setAutoRotateEnabled(true)
    .setLocalThumbnailPreviewsEnabled(true)
    .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
    .setProgressiveRenderingEnabled(false)
    .setResizeOptions(new ResizeOptions(width, height))
    .build();

上面我們說了Deawee,下面我們說下Image Pipeline

配置程式碼

ImagePipelineConfig config = ImagePipelineConfig.newBuilder()
    .setBitmapMemoryCacheParamsSupplier(bitmapCacheParamsSupplier)
    .setCacheKeyFactory(cacheKeyFactory)
    .setEncodedMemoryCacheParamsSupplier(encodedCacheParamsSupplier)
    .setExecutorSupplier(executorSupplier)
    .setImageCacheStatsTracker(imageCacheStatsTracker)
    .setMainDiskCacheConfig(mainDiskCacheConfig)
    .setMemoryTrimmableRegistry(memoryTrimmableRegistry) 
    .setNetworkFetchProducer(networkFetchProducer)
    .setPoolFactory(poolFactory)
    .setProgressiveJpegConfig(progressiveJpegConfig)
    .setRequestListeners(requestListeners)
    .setSmallImageDiskCacheConfig(smallImageDiskCacheConfig)
    .build();
Fresco.initialize(context, config);

是在是太多了,我也懶的寫了。

參考資料:中文文件