hiro99ma blog

何か技術的なこと

UI Layer(Jetpack Compose)

2024/10/31

Android Studio の新規プロジェクト作成で作るか、archtecture-templates/base を元にするのがよい。
View ベースアプリの場合は GUI で XML ファイルを更新することができたが Composable 関数の場合にはそれが無い。

Composable関数

レイアウト

Columnなどを組み合わせる。
これらも Composable 関数なのでTextなどの部品と組み合わせることができる。

レイアウトは難しい。
あまりにうまく行かない場合は、画像でレイアウトを作って Gemini などにサンプルコードを書いてもらうと良いだろう。

ちなみにブラウザで日本語表示にしているとメニューに「ポケベル」という項目があるが、 これは “pager(無線呼び出し - wikipedia)” を直訳したものである。

部品

これを書いている時点ではマテリアルデザイン3(M3 と略されることもある)である。

Textのような基本部品はマテリアルデザインのバージョンごとに定義されているので注意しよう ( M2のText, M3のText )。
androidx.compose.material3 は M3、数字が付かない androidx.compose.material は M2 である。

Image は別のパッケージである。

Modifier

だいたいの Composable関数で引数に指定できる Modifierは調整に使う。
同じModifierでも指定する関数によってパラメータにできないものもある。
また、Modifierでも Composable関数でも同じような設定ができるものもある。
一概にどちらがどうということは言えないので、必要になったときに調べると良いだろう。

テーマ

新規プロジェクトで作成した場合、ui/theme/Theme.ktプロジェクト名Themeという Composable関数 が作られている。
デフォルトではダイナミックカラーが有効になっているので、実機で動作させるとエミュレータと色が異なるかもしれない。
ダイナミックカラーを無効にしたり実機が未対応だったりするとデフォルト(baseline)のテーマで表示される。
アプリでのテーマを組み込むこともできる。
自作するのは大変なのでいくつかテーマを作ってくれるサイトがある。
アプリ名がないためTheme.ktのテーマ定義名が違っているくらいである。

Codelabs では Surface を使う例が多い。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)
        setContent {
            AppTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    MainScreen()
                }
            }
        }
    }
}

Android Studio でアプリを作ると Scaffold が使われている。
topBarbottomBarを実装できるので、それだけで普通のアプリの見た目になる。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            AppTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    MainScreen(modifier = Modifier.padding(innerPadding))
                }
            }
        }
    }
}

scaffold

Preview

Composable関数は Android Studio でプレビュー画面を見ることができる。
@Preview のパラメータで調整もできる。

いくつかの Composable関数を組み合わせて 1つの画面にすることもあるので、 それぞれをプレビューで

画面遷移

画面の遷移もアプリ内とアプリ外があるが、ここではアプリ内について記載する。

概要

ここでは画面遷移を固定で実装しておく方式について概要を記載する。
Android Studio でプロジェクトを作った場合は画面遷移のコードはない。
architecture-templates を使った場合は NavHost()は用意してあるが画面が 1枚しかないので遷移しない。
codelabsで一通りやって見るのがよいと思う。

画面遷移をするのにrememberNavController()のインスタンスがいるので、 それをどこで持ってどうするのがよいのかがまだよくわかっていない(2024/10/30現在)。
architecture-samples では画面の Composable関数にボタン押下イベント関数として渡しているようだ。

依存するものは引数に取る

ボタン押下などのイベントが発生したときに実行する内容は直接実装せず、 自作 Composable関数の引数でもらうようにしておくと良い。
そうするとテストするときにやりやすくなる。

また、状態についても UI elements では持たないようにする。
そうすると UI elements としてはステートレスにすることができる。

ViewModel

UI elements がステートレスになった分、ViewModel がステートフルになるという感じか。

概念的な ViewModel があるのかもしれないが、 少なくとも Android アプリでは androidx.lifecycle.ViewModelクラスを継承する。

UI 状態生成パイプラインAndroid アーキテクチャに関する推奨事項を見るとビジネスロジックを UI Layer に置いてもよさそうではあるが、 アプリの推奨アーキテクチャ でビジネスロジックは Data Layer 側であることを書いているのでその方がよいだろう。

UI state

UI elements で表示するのに使うデータは data class にまとめておき、ViewModel でインスタンスを生成して持っておく。
データを提供する場合はインスタンスそのものではなく、その Read Only 版を提供するようにしておく。

具体的には Kotlin の MutableStateFlow でインスタンスを作り、.asStateFlow() で Read Only 版を提供する。
Read Only 版はプロパティとして提供するのが普通なようだ。

State系のインスタンスを UI elements が描画に使っていると、 インスタンスに更新が行われたときに自動で再composeが行われて描画に反映されるしくみになっている。

更新はMutableStateFlowのインスタンスを .update{ it.copy(...) } のように実行する。

< Top page