🐻‍❄️

[Compose Multiplatform]Libresで画像リソースをアプリに組み込んで表示する方法

2023/07/20に公開

はじめに

調べたところどうやらCompose Multiplatform(v1.4.1)で画像リソースを組み込む際はLibresやmoko-resourcesというサードパーティ製のライブラリを利用するのが一般的な手法らしいです。今回はLibresというライブラリを利用して画像リソースをアプリに組み込む方法について調べたのでやり方をまとめて解説していきます。

https://github.com/Skeptick/libres

https://github.com/icerockdev/moko-resources

Libresとは?

Libresは画像リソースや文字列リソースからAndroid・iOS・MacOSの各アプリで取り扱えるリソースクラスを作成してくれるライブラリです。

例えば以下のようにプロジェクトフォルダに画像リソースや文字列リソースを格納しておくと、この画像リソースや文字列リソースから以下のようなリソースクラスを生成してくれるらしいです。

プロジェクトフォルダ

.
└── commonMain
    ├── kotlin
    └── libres
        ├── images
        │   └── star_(orig).svg
        └── strings
            ├── strings_en.xml
            └── strings_ja.xml

リソースクラス

public object MainRes {
  public val string: MainResStrings = MainResStrings

  public val image: MainResImages = MainResImages
}

@OptIn(ExperimentalObjCName::class)
public object MainResStrings {
  private val baseLocale: StringsJa = StringsJa

  private val locales: Map<String, Strings> = mapOf("en" to StringsEn, "ja" to StringsJa)

  @ObjCName(name = "helloWorld")
  public val hello_world: String
    get() = locales[getCurrentLanguageCode()]?.hello_world ?: baseLocale.hello_world

  @ObjCName(name = "helloUsername")
  public val hello_username: HelloUsername
    get() = locales[getCurrentLanguageCode()]?.hello_username ?: baseLocale.hello_username
}

public expect object MainResImages {
  public val star: Image
}

生成されたリソースクラスはImageTextのコンポーザブル関数と連携できるように生成されるので、以下のようなコードで画像リソースや文字列リソースをImageTextに渡して表示すること可能になります。

サンプルコード

@Composable
internal fun App() = AppTheme {
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.wrapContentSize().align(Alignment.Center)) {
            Image(
                painter = MainRes.image.star.painterResource(),
                contentDescription = "star",
                modifier = Modifier.size(32.dp)
            )
            Text(text = MainRes.string.hello_world)
            Text(text = MainRes.string.hello_username.format("Katz"))
        }
    }
}

Libresを使ってみる

今回はLibresを使って画像リソースをアプリに組み込んでいきます。Libresを利用して画像リソースをアプリに組み込むためには以下の3ステップで作業を進めていきます。

  1. セットアップ
  2. Libresが生成するリソースクラスに画像リソースを含める
  3. Libresが生成したリソースクラスを使って画像を表示する

1. セットアップ

まずはLibresの依存関係のセットアップから始めていきます。Compose Multiplatformのプロジェクトを作成した後に、以下のコードを各ファイルに記述してLibresの依存関係をセットアップします。

libs.version.toml

// VersionCatalogにLibresのプラグイン・ライブラリの依存関係の情報を登録していく。
[versions]
 ︙ 省略
libres = "1.1.8"
 ︙ 省略

[libraries]
 ︙ 省略
libres = { module = "io.github.skeptick.libres:libres-compose", version.ref = "libres" }
 ︙ 省略

[plugins]
 ︙ 省略
libres = { id = "io.github.skeptick.libres", version.ref = "libres" }
 ︙ 省略

build.gradle.kts(AppName)

// プロジェクトのルートの`build.gradle.kts(AppName)`にLibresのプラグインの依存関係を登録する。
plugins {
    alias(libs.plugins.multiplatform).apply(false)
    alias(libs.plugins.compose).apply(false)
    alias(libs.plugins.cocoapods).apply(false)
    alias(libs.plugins.android.application).apply(false)
    alias(libs.plugins.libres).apply(false)
}

build.gradle.kts(:composeApp)

// プロジェクトのアプリの`build.gradle.kts(:composeApp)`にLibresのプラグインとライブラリの依存関係を追加する。
plugins {
    alias(libs.plugins.libres)
}

sourceSets {
    val commonMain by getting {
        dependencies {
            implementation(libs.libres)
        }
    }
}

// 更に`build.gradle.kts(:composeApp)`にはLibresのデータリソースの生成の設定を記述しておく。`libres`の`generatedClassName`で生成するリソースクラス名を決められるようになっているので、`MainRes`と指定して`MainRes`でリソースクラスを参照できるようにしておく。
libres {
    generatedClassName = "MainRes"
}

2. Libresが生成するリソースクラスに画像リソースを含める

次にリソースクラスに画像リソースを含める作業をしていきます。リソースクラスに画像リソースを含める作業は簡単でComposeAppcommonMainlibres/imagesフォルダを作成して、そこに画像リソースを格納してあげるだけです。これでLibresがリソースクラスに画像リソースを含めてくれるようになります。

// 今回はスター画像(SVG)を画像リソースとして用意して、star_(org).svgというファイル名称で`libres/iamges`フォルダへ格納することにしました。
└── commonMain
    ├── kotlin
    └── libres
        └──  images
             └── star_(orig).svg

3. Libresが生成したリソースクラスを使って画像を表示する

次に生成されたリソースクラスを利用して画像リソースをImageで表示するための作業をしていきます。手順2の作業が終わってビルドをするとMainResというクラス名でリソースクラスが生成されているはずです。

public object MainRes {
  public val string: MainResStrings = MainResStrings

  public val image: MainResImages = MainResImages
}

@OptIn(ExperimentalObjCName::class)
public object MainResStrings

public expect object MainResImages {
  public val star: Image
}

この生成されたMainResクラスを利用し、以下のようなコードでImageに画像リソースを渡してあげれば、画像リソースを表示できます。

@Composable
internal fun App() = AppTheme {
    Box(modifier = Modifier.fillMaxSize()) {
        Image(
            painter = MainRes.image.star.painterResource(),
            contentDescription = "star",
            modifier = Modifier.size(128.dp).align(Alignment.Center)
        )
    }
}

動作確認

最後にこのコードをビルドして動作確認してみましょう。このようにAndroid・iOSのアプリのそれぞれでアプリでビルドして起動すると、組み込んだ画像リソースが無事表示できていますね。

おわりに

このようにLibresを利用すると画像リソースを簡単に組み込むことが可能です。本記事のコードを実装したプロジェクトは以下にありますので興味がある方は御覧ください。

https://github.com/kaleidot725/ComposeMultiplatformLab/tree/main/sample/libres

Discussion