Skip to content

takusan23/ComposeIconShop

Repository files navigation

ComposeIconShop

Jetpack Composeのアイコンを見るアプリ
アウトラインとかツートーンとか

仕組み

マテリアルアイコンのソースコードからアイコン一覧みたいなやつを見つけてきたので
これを使ってアイコン一覧テキストをJSON配列に変換します。これはNode.jsでやってる(download/src/index.js)

生成したJSONファイルをAndroidのassetsに配置します。

あとはJSONファイルを解析し、リフレクションを使ってアイコンを呼び出しています。

実行方法

必要なもの

  • Android アプリ開発環境
    • Android StudioはCanary版を利用してください。
    • Android Studio Arctic Fox | 2020.3.1 Canary 6
  • Node.js
    • アプリの実行には使いませんが、アイコンの名前を取得する際に利用します
    • まあ無くても動く

アイコンデータ取得

download内にNode.jsで実行できるプログラムが入っています。
プログラムを実行すると、Androidのsrc/main/assets/icon.jsonが更新されます。

$ cd download
$ npm run dev

トラブルシューティング

  • Unresolved reference ~
    • あんのに無いって言われる場合
    • Build > Clean Project を実行
    • 名前が悪い?
  • Scanning files to indexが進む気配すらない
    • .ideaフォルダを消して再度起動
  • 'padding(Dp): Modifier' is only available since Kotlin 1.4.30 and cannot be used in Kotlin 1.4
    • エラーに見えるけど実行できる。
    • 設定を開いて、Languages & Frameworksへ進みKotlinを押して、Update ChannelEarly Access Preview 1.4.xにしてInstallを押せばいいらしい。

技術的な話

画面遷移

ViewModelに置いたLiveDataを利用して画面遷移をしています。どうやるのが正解なのかは知らない

とりあえずデータクラスを用意して

/**
 * 画面遷移で使うデータクラス
 *
 * @param screenName 遷移先画面の名前
 * */
open class NavigationData(val screenName: String)

/**
 * アイコン詳細画面へ切り替える際に利用するデータクラス
 *
 * @param iconName アイコンの名前。
 * @param screenName 遷移先画面の名前
 * */
class IconDetailNavigationData(screenName: String, val iconName: String) :
    NavigationData(screenName)

あとはデータクラスをLiveDataへ

/**
 * GoTo家キャンペーン
 *
 * [screenLiveData]へ[NavigationNames.HOME]を送信します
 * */
fun gotoHome() {
    screenLiveData.postValue(NavigationData(NavigationNames.HOME))
}

Jetpack Compose側はこうなってる
画面もFragmentではなくJetpack Composeで作成しています。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Jetpack Composeでレイアウト作成
    setContent {
        // 画面遷移
        val screenLiveData = viewModel.screenLiveData.observeAsState()
        when (screenLiveData.value?.screenName) {
            NavigationNames.HOME -> HomeScreen(viewModel = viewModel,iconSearch = searchWord.value,iconType = iconType.value)
            NavigationNames.DETAIL -> DetailScreen(iconName = (screenLiveData.value!! as IconDetailNavigationData).iconName)
        }
    }
}

アイコン取得のためにリフレクション

アイコン一覧の配列等は(多分)ない。

ちなみに本家はPythonでアイコン一覧を取得している模様。本家README参照( https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/icons/README.md
ソースコードを見に行けばアイコン一覧が見ようと思えば見れる( https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/icons/generator/raw-icons/filled/
ただし、Kotlinファイルはライブラリ生成時に生成されるらしく?見てもない。

まず、アイコンを返す関数があるクラスを取得するために、クラスの名前からクラスを取得するClass.forName()を利用します。

  • filledなところはアイコンの種類。アウトラインなアイコンが欲しい場合はoutlinedを代わりに書く
  • AndroidKtはアイコン名。アイコン名+Ktを付ける。
val iconClass = Class.forName("androidx.compose.material.icons.filled.AndroidKt")

その次に、Staticなアイコンを返すメソッドがあるので取得します。
先ほど用意したクラスはインスタンス化しなくていいはずです。Staticなメソッドを呼ぶだけなので

メソッド名は get+アイコン名 だと思います。第二引数は戻り値の型。アウトラインを取得するようにした場合はIcons.Outlined::class.javaになると思う

val getIconFunction = iconClass.getMethod("getAndroid", Icons.Filled::class.java)

最後に取得したメソッドを呼びます。

val icon = getIconFunction.invoke(null, Icons.Filled::class.java) as ImageVector

あとはこれをIconに入れてあげれば完成

Icon(imageVector = icon)

詳しくは各Iconのクラスを見てKotlinをJavaに変換して見てみてください。パスがそのまま書いてある

仕様

  • 一部のアイコンが欠けている
    • Jetpack Composeのアイコン一覧どっかにねえかな
    • JSON生成でミスってんのかわからん