Edited at

画像ライブラリFresco

More than 3 years have passed since last update.


Fresco

logo.png

FrescoはFacebookが開発している画像ライブラリです。

PicassoGlideなど、Androidの画像ライブラリにはいくつか選択肢がありますが、最近その選択肢の一つとして挙げられることもあるような…でもまだ実際に使ってる人見たことない…そんな新進気鋭の画像ライブラリです。

主に公式ドキュメントをかいつまんだような内容になるので、詳しい使い方を知りたい方はそちらを参照してください(中国語と韓国語がサポートされたのでそのうち日本語もサポートされるかも…)


コンセプト


Drawees

Frescoでは画像が表示される場所をDraweesという言葉で表現しています。このDraweesはMVC(Model-View-Controller)の思想で構成されています。


DraweeView

MVCのViewに該当する部分で、Frescoでは普通のImageViewではなく、このDraweeViewを継承したクラスを使うことが必要になります。ほとんどの場合はSimpleDraweeViewを使えば対応できると思います。


DraweeHierarchy

MVCのModelに該当する部分で、フェードインやplaceholderの設定など、主に画像のレンダリングに関する部分を処理しています。


DraweeController

MVCのControllerに該当する部分で、画像のリクエストなど、主に画像の読み込みに関する部分を処理しています。


導入

dependencies {

compile 'com.facebook.fresco:fresco:0.5.0+'
}


基本的な使い方

Frescoを初期化します。ApplicationクラスのonCreate()のタイミングなどで処理すれば問題ないと思います。Activityクラスで行う場合はsetContentView()の前に呼ぶ必要があります。

Fresco.initialize(context);

xml上でSimpleDraweeViewを設定します。

<com.facebook.drawee.view.SimpleDraweeView

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/my_image_view"
android:layout_width="130dp"
android:layout_height="130dp"
fresco:placeholderImage="@drawable/my_drawable"
/>

コード上で読み込むUriを指定します。

Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");

SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);


対応URI

Type
Scheme

File on network
http://, https://

File on device
file://

Content provider
content://

Asset in app
asset://

Resource in app
res://


xml上でいろいろ設定する

ImageViewではなく専用のViewを使わなければいけないのは導入時のデメリットの一つでもありますが、xml上に設定を記述できるというのはメリットの一つになり得ると思います。それにしても設定が多い…

<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"
/>

個人的にいいなと思ったのはfresco:pressedStateOverlayImage="@color/red"で、画像を押した時にオーバーレイさせる色や画像を指定することができます。普段FrameLayoutでwrapしてforegroundImageを使ったりしてたのですごく助かる。

ここで注意が必要な点が一つ。DraweeViewでは基本的にwrap_contentが使えません。ちなみにmatch_parentは使えます。


Height and width mandatory

You must declare both android:layout_width and android:layout_height. Without both of these two, the view will not be able to lay the image out correctly.

wrap_content

Drawees do not support the wrap_content value for the layout_width and layout_height attributes.

The reason for this is that the content's size changes. The size of your downloaded image can be different from your placeholder - and the failure and retry images, if any, can be still different.

Use of wrap_content would force Android to do another layout pass when your image comes in - and for the layout to change before users' eyes, creating a jarring effect.


wrap_contentが使えないんじゃ使いどころがないじゃん…と思いましたが、唯一アスペクト比を指定すればwrap_contentの指定でも問題なく表示されるようです。

mSimpleDraweeView.setAspectRatio(1.33f);

widthをdpもしくはmatch_parentで指定して、heightはwrap_contentにして適宜アスペクト比を指定するのがよさそうです。画像URLと同時にwidthとheightを返すようなAPIなどと合わせて使うことを主に想定しているような気がします。


コード上で設定する場合

DraweeHierarchyを用います。

List<Drawable> backgroundsList;

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


リスナーの設定

ControllerListenerをDraweeControllerに設定します。BaseControllListenerを使うと必要なメソッドだけOverrideすることができるのでそちらの方がよさそうです。

ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {

@Override
public void onFinalImageSet(
String id,
@Nullable ImageInfo imageInfo,
@Nullable Animatable anim) {
}

@Override
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
}

@Override
public void onFailure(String id, Throwable throwable) {
}
};

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


複数URIへの同時リクエスト

解像度の違う画像URIに同時にリクエストし、解像度の高い画像の読み込みが終わるまで解像度の低い方を先に表示させておくといったことが可能です。

Glideのサムネイル表示とはまた違ったアプローチで、画像サイズが異なるURIが複数返されるようなAPIと合わせて使うことを想定しているようです。

Uri lowResUri, highResUri;

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


ImageRequestの設定

FrescoではDraweeControllerBuilderでsetUri()を用いる以外にsetImageRequest()を使用することができます。単純なURIでのリクエストの際にはImageRequest.fromUri()を用いることができますが、その他にもImageRequestには様々なオプションを設定することが可能です。

Uri uri;

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

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

setAutoRotateEnabled(true)

画像のmeta情報から画像を自動で回転

setLocalThumbnailPreviewsEnabled(true)

ローカル画像のみ読み込み時にサムネイル表示

setResizeOptions(new ResizeOptions(width, height))

リサイズ時のサイズ指定

個人的に一番気になったのはsetLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)です。

Frescoでは基本的に画像リクエスト時に以下のような動作を行います。

1.Bitmapのキャッシュをチェックして画像があれば返す

2.エンコード済のメモリキャッシュをチェックして画像があれば返す

3.ディスクキャッシュをチェックして画像があれば返す

4.キャッシュに存在しないのでURI先の画像を読み込み

setLowestPermittedRequestLevel()ではこの画像読み込み時の一連の動作をどこまで行うか、つまりどのキャッシュまで画像を探しに行くかを設定することができます。


アニメーションの再生

FrescoではGIFとWebPのアニメーション再生をサポートしています。その他の画像ライブラリではGlideがアニメーションGIFの再生をサポートしています。

Uri uri;

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

GiphyAPIがGIFとWebP両方に対応しているので試すのにちょうどよいです。


Progressive JPEG読み込みの有効化

FrescoではProgressive JPEGの読み込みに対応しています。最初にぼやけたかんじで表示されて読み込みに応じてだんだんと鮮明になって表示されます。もちろん読み込み元の画像がProgressive形式の場合のみ有効です。

Uri uri;

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setProgressiveRenderingEnabled(true)
.build();
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setOldController(mSimpleDraweeView.getController())
.build();
mSimpleDraweeView.setController(controller);


キャッシュなどの設定

ImagePipelineConfig上で設定した値を初期化のタイミングで読み込むことで設定できます。他にも細かく設定できるらしいですが設定が多すぎて全て理解できていません、すいません。。。

DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder()

.set....
.set....
.build();
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
.setEncodedMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
public MemoryCacheParams get() {
return new MemoryCacheParams(maxCacheSize, maxCacheEntries,
maxEvictionQueueSize, maxEvictionQueueEntries, maxCacheEntrySize);
}
})
.setMainDiskCacheConfig(diskCacheConfig)
.setSmallImageDiskCacheConfig(smallImageDiskCacheConfig)
.build();
Fresco.initialize(context, config);

おもしろいなと思ったのがsetMainDiskCacheConfig()setSmallImageDiskCacheConfig()で、ディスクキャッシュを大きい画像用と小さい画像用といった風に分けることができるそうです。

ImageRequest request = ImageRequest.newBuilderWithSourceUri(uri)

.setImageType(ImageType.SMALL)

ここでsetImageType(ImageType.SMALL)を指定した画像がSmallImageDiskCacheの方にキャッシュされます。詳細画像とサムネイル画像でキャッシュを分けておくことでサムネイル画像が詳細画像のキャッシュに押されて削除されるといったことを防ぐことができます。


所感

ドキュメントを読んでいて一番感じたのは、汎用的な画像ライブラリというよりも、APIでの画像情報取得などを前提とした実践的な要素の強いライブラリだということです。

少しクセがありますが型にはまれば使いやすいといったライブラリになるのでしょうか。ライブラリ選定時には少し注意が必要だと思います。


リンク

Fresco | An image management library.

Github

Introducing Fresco: A new image library for Android