見出し画像

画像読み込みライブラリCoil導入ガイド

こんにちは。
note Androidアプリで、画像読み込みライブラリCoilを導入しましたので、それについて書いていこうと思います。

※ この記事はnote株式会社のアプリチーム1weekアドベントカレンダーの4日目の記事です


Coilとは

画像読み込みライブラリCoilは、Android用の画像読み込みライブラリです。
Coilの名称は「Coroutine Image Loader」の略です。
主な特徴はこちら。

高速: Coil は、メモリとディスクのキャッシング、メモリ内の画像のダウンサンプリング、リクエストの一時停止/キャンセルの自動化など、多くの最適化を実行します。
軽量: Coil は ~2000 のメソッドを APK に追加します (すでに OkHttp と Coroutines を使用しているアプリの場合)。これは Picasso に匹敵し、Glide や Fresco よりも大幅に少ない数です。
使いやすい: Coil の API は、ボイラープレートの最小化とシンプルさのために Kotlin の言語機能を活用しています。
現代的: Coil は Kotlin ファーストで、Coroutines、OkHttp、Okio、AndroidX Lifecycles などの最新のライブラリを使用します。

https://coil-kt.github.io/coil/README-ja/

導入

mavenCentralで導入できます。

implementation("io.coil-kt:coil:2.6.0")

バージョンはこちらから参照してください。(Release)

画像取得

CoilはImageViewの拡張関数のloadを使用して、画像を取得します。

// URL
imageView.load("https://example.com/image.jpg")

// File
imageView.load(File("/path/to/image.jpg"))

画像を読み込むだけだと上記のみですので、コード量が少なくて実装が楽な印象ですね。

各種設定

画像を読み込む際に、各種設定を行うことができます。
設定については、ラムダ式を使用して行います。
ここでは、よく使用した設定について解説していこうと思います。

imageView.load("https://example.com/image.jpg") {
    crossfade(true)
    placeholder(R.drawable.placeholder)
    error(R.drawable.error)
    scale(Scale.FILL)
    size(width, height)
    transformations(CircleCropTransformation())
}

crossfade: クロスフェード アニメーションを有効かどうか。デフォルトはfalse。
placeholder: プレイスホルダーの画像を設定。Drawableと @DrawableRes Intが設定可能。
error: 画像を読み込みに失敗した際に表示する画像を設定。Drawableと @DrawableRes Intが設定可能。
scale: 画像を指定されたサイズに合わせるために使用されるスケーリングを設定。Scaleは FILLとFIT。設定されていないとImageViewに対して自動的に計算されます。
size: 画像のサイズを設定。@Px Intを設定可能。
transformations: 返される画像の形を設定。CircleCropTransformation()で円画像。RoundedCornersTransformation()で丸角画像に変換でき、radiusの設定も可能です。

Jetpack Compose

CoilはJetpack Composeにも正式対応しています。
ただ、別途mavenCentralで導入する必要があります。

implementation("io.coil-kt:coil-compose:2.6.0")

バージョンは上記のCoilと同様。

AsyncImageを使用して、画像読み込みを行えます。

AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://example.com/image.jpg")
        .crossfade(true)
        .build(),
    placeholder = painterResource(R.drawable.placeholder),
    contentDescription = stringResource(R.string.description),
    contentScale = ContentScale.Crop,
    modifier = Modifier.clip(CircleShape)
)

各画像フォーマットに対応する

対応フォーマット

Coilを導入することで下記のフォーマットは標準で対応しています。

BMP
JPEG
PNG
WebP
HEIF (Android 8.0+)
AVIF (Android 12.0+)

https://coil-kt.github.io/coil/getting_started/#supported-image-formats

また、追加で下記フォーマットも設定することで使用できます。

coil-gif: GIF, animated WebP (Android 9.0+), animated HEIF (Android 11.0+)
coil-svg: SVG
coil-video: Static video frames from any video codec supported by Android

アディショナルのフォーマットを導入

mavenCentralから追加で導入。

implementation("io.coil-kt:coil-gif:2.6.0")
implementation("io.coil-kt:coil-svg:2.6.0")
implementation("io.coil-kt:coil-video:2.6.0")

ImageLoaderにcomponentsを追加。

val imageLoader = ImageLoader.Builder(context)         
            .components {
                add(ImageDecoderDecoder.Factory())
                add(SvgDecoder.Factory())
                add(VideoFrameDecoder.Factory())
            }
            .build()

上記の設定を行うことで、自動で各フォーマットを認識して対応してくれます。

ヘッダー設定

画像を読み込む際に、カスタムヘッダーを追加したい場合もあると思います。
Coilでは、Okhttpを使用しているのでOkhttpと同じように設定することが出来ます。

1. Interceptorを設置

class CustomHeaderInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        val newRequest = request.newBuilder()
            .addHeader("header", "header")
            .build()
        return chain.proceed(newRequest)
    }
}

2. ImageLoaderに設定

val imageLoader = ImageLoader.Builder(context)
            .okHttpClient {
                OkHttpClient.Builder()
                    .addInterceptor(CustomHeaderInterceptor())
                    .build()
            }
            .build()

画像取得後に処理を行う

画像をプリロードしたり、取得した後に何か処理をしたい場合にも対応できます。
targetを設定することで、成功時、失敗時の処理を行えます。

val request = ImageRequest.Builder(context)
            .data("https://example.com/image.jpg")
            .target(
                onSuccess = { result ->
                    // 成功時の処理
                },
                onError = {
                    // 失敗時の処理
                },
            ).build()
        imageLoader.enqueue(request)

共通設定

今までの上記設定は個別に設定できますが、アプリ内で共通の設定を行うことでコードを簡略化することができ、とても便利です。

1. ApplicationクラスにImageLoaderFactoryを継承

class App : Application(), ImageLoaderFactory

2.newImageLoader()内に共通化したい処理を記述する

 override fun newImageLoader(): ImageLoader {
        val imageLoader = ImageLoader.Builder(applicationContext)
            .okHttpClient {
                OkHttpClient.Builder()
                    .addInterceptor(CustomHeaderInterceptor())
                    .build()
            }
            .components {
                add(ImageDecoderDecoder.Factory())
                add(SvgDecoder.Factory())
                add(VideoFrameDecoder.Factory())
            }
            .crossfade(true)
            .build()
        return imageLoader
    }

導入してみて

導入してみて、とても実装が楽だなと感じました。
ラムダ式で設定を書くのは現状の実装では馴染みがあり、すぐに順応できたと思います。
各種カスタマイズもやりやすく、ヘッダー設定や共通処理は簡単にできたと思っています。
また、ImageViewの拡張関数として書けるので、他のライブラリに比べてコード記述量が削減できていました。
パフォーマンスに関しても、今のところ申し分なく感じています。
note AndroidアプリはJetpack Composeへ徐々に移行していっているのですが、Jetpack Composeに正式対応しているところもいいところだなっと思います。
今、一番ホットな画像読み込みライブラリだと思うので、この記事を読んで導入するきっかけになればと思います。

参考

最後に

現在、noteではAndroidエンジニアを絶賛募集中です!
カジュアル面談もやっていますので、
どんなことをやっているのか、どんな開発環境でやっているか等の気になることがありましたら、ぜひお気軽にお問い合わせください。


この記事が気に入ったらサポートをしてみませんか?