Compose Multiplatform : ネットワーク経由で画像を読み込む ① Kamel編

Compose Multiplatformでネットワーク経由で画像を読み込みたいときはKamelを利用すると良いらしい。本記事ではKamelの機能を調べ、どのような感じで画像読み込みを制御できるかまとめます。

目次

前提

この記事では以下の環境で実装を進めています。

名称 バージョン 備考
Fleet(IDE) Fleet version: build 1.26.104 OS: Mac OS X (14.1.1, aarch64)
Kotlin 1.9.20
Compose Multiplatform 1.5.4

※もし不明点がある場合はこちらのGitHubのソースコードにて詳細を確認ください。

https://github.com/kaleidot725/Compose-Multiplatform-Playground/tree/main/projects/ImageLoader

セットアップ

toml
[versions]
kamel = "0.9.0"

[libraries]
kamel = { module = "media.kamel:kamel-image", version.ref = "kamel" }\

build.gradle.kts(app)
   sourceSets {
        commonMain.dependencies {
            implementation(libs.kamel)
     }
    }

画像読み込み機能

Kamelに備わる画像読み込み機能として、以下のような機能がサポートされている。

機能名称 説明
画像読み込み ・Bitmap(JPG・PNG)やVector(XML)、SVGの画像をダウンロードして表示できる
キャッシュ ・ダウンロードした画像をキャッシュに保存できる
・キャッシュにはメモリキャッシュとディスクキャッシュがある
・キャッシュではBitmap・Vector・SVGごとにキャッシュサイズを決められる
クロスフェード ・Compose MultiplatformのanimationSpecを指定してクロスフェードを実行できる
エラーハンドリング ・読み込み中・成功・エラーと状態を取得できるため、状態に応じてComposable関数を出し分けることができる
ロギング ・内部的にKtorクライアントを利用しているのでKtorのロギングの仕組みを利用してログを表示できる
リトライ制御 ・画像のダウンロードの失敗したときに何回までリトライするか設定できる’

画像読み込み表示

Kamelに備わる画像読み込み機能を利用して、下記のような表示制御を実装してみた。

名称 内容
画像を表示する 画像を読み込み表示する
読み込み中を表示する 画像の読み込み中に独自のUIを表示する
エラー画像を表示する 画像の読み込みに失敗したらエラー画像を表示する
プレースホルダー画像を表示する 画像の読み込み中にプレースホルダー画像を表示する
クロスフェードする 画像の読み込みが終わったらクロスフェードして画像を切り替える
エラーハンドリングする 画像の読み込み処理をハンドリングし、読み込み中・成功・失敗で表示を出し分ける

画像を表示する

動作内容

サンプルコード

Kotlin
@Composable
fun KamelImageDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        KamelImage(
            resource = asyncPainterResource(ImageResource.sampleImage),
            contentDescription = "sample",
            contentScale = ContentScale.Crop,
            modifier = Modifier.size(200.dp),
        )
    }
}

読み込み中を表示する

動作内容

サンプルコード

Kotlin
@Composable
fun KamelLoadingDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        KamelImage(
            resource = asyncPainterResource(data = ImageResource.sampleImage),
            contentDescription = "sample",
            contentScale = ContentScale.Crop,
            modifier = Modifier.size(200.dp),
            onLoading = { CircularProgressIndicator(modifier = Modifier.size(128.dp)) }
        )
    }
}

エラー画像を表示する

動作内容

サンプルコード

Kotlin
@OptIn(ExperimentalResourceApi::class)
@Composable
fun KamelErrorDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        KamelImage(
            resource = asyncPainterResource(data = ImageResource.sampleErrorImage),
            contentDescription = "sample",
            contentScale = ContentScale.Crop,
            modifier = Modifier.size(200.dp),
            onLoading = { CircularProgressIndicator(modifier = Modifier.size(128.dp)) },
            onFailure = {
                Image(
                    painter = painterResource(ImageResource.error),
                    contentDescription = null,
                    modifier = Modifier.size(200.dp),
                )
            }
        )
    }
}

プレースホルダー画像を表示する

動作内容

サンプルコード

Kotlin
@OptIn(ExperimentalResourceApi::class)
@Composable
fun KamelPlaceHolderDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        KamelImage(
            resource = asyncPainterResource(data = ImageResource.sampleImage),
            contentDescription = "sample",
            contentScale = ContentScale.Crop,
            modifier = Modifier.size(200.dp),
            onLoading = {
                Image(
                    painter = painterResource(ImageResource.placeholder),
                    contentDescription = null,
                    modifier = Modifier.size(200.dp),
                )
            }
        )
    }
}

クロスフェードする

動作内容

サンプルコード

Kotlin
@OptIn(ExperimentalResourceApi::class)
@Composable
fun KamelCrossfadeDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        KamelImage(
            resource = asyncPainterResource(data = ImageResource.sampleImage),
            contentDescription = "sample",
            contentScale = ContentScale.Crop,
            modifier = Modifier.size(200.dp),
            animationSpec = tween(durationMillis = 2000),
            onLoading = {
                Image(
                    painter = painterResource(ImageResource.placeholder),
                    contentDescription = null,
                    modifier = Modifier.size(200.dp)
                )
            },
            onFailure = {
                Image(
                    painter = painterResource(ImageResource.error),
                    contentDescription = null,
                    modifier = Modifier.size(200.dp)
                )
            }
        )
    }
}

エラーハンドリングする

動作内容

サンプルコード

Kotlin
@OptIn(ExperimentalResourceApi::class)
@Composable
fun KamelErrorHandlingDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Black)
    ) {
        when (val resource = asyncPainterResource(data = ImageResource.sampleImage)) {
            is Resource.Success -> {
                Image(
                    painter = resource.value,
                    contentDescription = "sample",
                    modifier = Modifier.size(200.dp)
                )
            }

            is Resource.Loading -> {
                Image(
                    painter = painterResource(ImageResource.placeholder),
                    contentDescription = "placeholder",
                    modifier = Modifier.size(200.dp)
                )
            }

            is Resource.Failure -> {
                Image(
                    painter = painterResource(ImageResource.error),
                    contentDescription = "placeholder",
                    modifier = Modifier.size(200.dp)
                )
            }
        }
    }
}

使ってみて

Kamelは画像読み込みの基本的な制御が簡単にできるライブラリなのでおすすめです。今回は紹介していないのですがKamelではビルダー設定もサポートしていて、簡単にキャッシュの有効化・無効化を変更することもできます。Compose Multiplatformのアプリを作る際にはKamelを導入しておけばほとんど問題なく画像読み込みができそうです。

参考文献