Jetpack ComposeとKotlinの両方が初めての人用に記載します.Kotlin特有の記載方法が満載で,Javaから移行した身としては一気に理解するのがしんどいため,学習を兼ねてまとめていきます.
State
Compose内でStateを利用すると,Composeは自動でStateの状態を監視し,更新を検知すると自動的にComposeをrecomposeします.
すなわち,今までLiveDataを用いて状態を監視し,変更が検知された場合に通知され,UIに反映するというロジックを実装していましたが,それの代替となるものです.observeを暗黙的に行ってくれるのでコード量が少なく,UIとの紐づきが一目でわかるのが魅力です.
なお,既存のCompose以外のUIに対しては依然としてLiveDataによる仕組みを使用することを推奨されています.
LiveDataをStateとして利用する
既存のViewModelのLiveDataを手っ取り早くComposeで利用したい場合には,LiveDataをStateとして認識させて利用します.
val hogeValue: Hoge by hogeViewModel.hogeLiveData.observeAsState(hoge)
なお,Composeを一から使い始める,他のViewにも通知したい等の理由がない限りこれを使用する必要はなくダイレクトにStateを使用するべきです.
また,Kotlinのbyはdelegate(委譲)property機能です.すなわち,hogeValueへの値の代入はState#setValueに委譲され,hogeValueの値はState#getValueに委譲されます.コードが大変シンプルになるのが魅力ですが,初見では理解しにくい気がします.
remember
Composeは関連しているStateの状態が変化するごとにRecomposeされ値が破棄され初期化されます.しかし,Recompose後でも一貫した値を保持したい場合がもちろんあります.その際に使用するのがrememberというdelegation propertyです.
// key initial calculation
remember(hoge.value) { func }
一度目はfuncを実行した値が使用され,その後はkeyが同一であれば,rememberされた値が返されます.
なんとなくrememberのjavadocを見てみます.
@Composable
public inline fun <T> remember(
calculation: @DisallowComposableCalls () -> T
): T
inline関数とはなんでしょうね?関数変数のインスタンス化によるオーバーヘッドを避けるためにコンパイル時に実際にコードに埋め込んでくれる機能です.従って,機能だけを把握したいときは読み飛ばして問題ありません.
Stateをrememberする
StateもRecomposeの対象であり,何もしなければStateの値も初期化されてしまいます.従って,Stateもrememberする必要がありますが,原則として以下のような手法となります.
val state = remember { mutableStateOf(default) } //1
var value by remember { mutableStateOf(default) } //2
val (value, setValue) = remember { mutableStateOf(default) } //3
ぱっと見たときなんだこりゃと思いましたが以下のような意味になります.
1は上記したLiveDataのrememberと同様で,rememberされた値を返します.
2はrememberされたstateにdelegateしています.なお,value自体はsetValueされるべき値ですので,置き換えられる必要があります.したがってこれだけvarです.
3はdestructuring declarationsという機能を使用した宣言になります.
すなわち,value = MutableState#component1(), setValue = MutableState#component2()の値が入っていることになります.名前通りの値,関数変数が与えられているか確認してみます.MutableStateと実装が以下になっています.
@Stable
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
override operator fun component1(): T = value
override operator fun component2(): (T) -> Unit = { value = it }
よって,確かにvalueには値が,setValueには値をセットするために関数変数が与えられています.
ViewModel内でのState表記のベストプラクティス
以下で紹介されているのが多い気がしますが,いくらviewmodel以外での変更を防ぎたいからって冗長じゃない?似たような名前の変数があるのも紛らわしいし,しかも呼び出すときにわざわざvalueをつけないといけないし,コードが見にくくなってしまいます.
val _variable = mutableStateOf(0)
val variable: State<Int> = _variable
delegation propertyを使った以下の表記が一番簡潔になると思います.これなら呼び出し時にvalueもいらないし,viewmodel外部での変更も防止できます.
var variable by mutableStateOf(0)
private set
戻るボタンの取り扱い
Compose内にBackHandlerを定義し,バックを押されたときの処理を記載すればよい.enableをtrueとした時にのみ処理される.なお,backstackの順番に伝播していくため,呼び出し順序には注意する必要がある.
BackHandler(viewModel.lastCategory != Category.ROOT) {
viewModel.apply {
updateCategory(lastCategory)
}
}
Scaffold
topBar, content, bottomBarなどLayout全体を指定できます.
Javadocが読めない
fun Scaffold(
//...
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
//...
content: @Composable (PaddingValues) -> Unit
)
@Composableを追加された関数式はSlotsと呼ばれるComposable
Slots are parameters to a composable that allow the caller to describe a section of the screen. You’ll find examples of slots throughout the built-in composable APIs.
https://developer.android.com/codelabs/jetpack-compose-state?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fcompose%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-state#11
() -> Unit = {}
これは引数なしで返り値Unit,引数で指定されなければデフォルトで空関数{}を呼び出す関数型.Unitはkotlinでは有用な返り値を返さない場合に使用します(javaで言えばvoidと考えて問題ない).また,Kotlinではデフォルト引数を利用できるためこのような表記となります.
content: @Composable (PaddingValues) -> Unit
PaddingValuesには,topとbottomに適用されるpadding値が格納されています.もし子composableがscroll型の場合,topとbottom分のpaddingを明示的に与えて,表示がかぶらないように調整する必要があります.
Modifier
UIデザインはとにもかくにもModifierを学べば,複雑な形状でない限り実現できます.
padding
パディングの指定
.padding(8.dp) //全方向に
.padding(vertical = 4.dp, horizontal = 8.dp) //垂直・水平方向にそれぞれ両サイドに
.padding(start = 4.dp, end = 2.dp) //startとend方向にそれぞれ4.dpと2.dp
.padding(top= 4.dp, bottom = 2.dp) //topとbottom方向にそれぞれ4.dpと2.dp
clipping
指定の形状でクリップ可能
.clip(RoundedCornerShape(8.dp))
background
背景色の指定が可能
background(MaterialTheme.colors.primary)
clickable
クリック時の判定
.clickable(onClick = {})
fillMaxWidth
余白をどう埋めるか.float 0-1で指定
.fillMaxWidth(1.0f) //全部埋める
fillMaxSize
余白をどう埋めるか.float 0-1で指定.デフォルトは1.
.fillMaxSize()
align
レイアウト内のアライメントを指定することが可能
.align(Alignment.CenterVertically) //垂直方向で中央に配置
.align(Alighment.CenterHorizontally) //水平方向で中央に配置
alignmentとarrangementの違い
並び方向=Row{}であれば横,Column{}であれば縦
alignmentは並び方向と垂直の方向にどのように配置するか.Rowでいえば上詰めするか下詰めするか.
arrangementは並び方向にどのように配置するか.Rowでいえば左詰めするか右詰するか.
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
)
weight
レイアウト内での重みを決定可能です.Row内で使用することが想定されており,Row内で一方に1.0f(最大値)を指定された場合,その他の要素は最大限端に寄せられる結果となります.したがって,これを利用する場合,fillMaxWidthは不要な場合があります.
.weight(1.0f)
size
sizeを指定できます.それぞれのComponentにはDefaultでいい感じのサイズが定数として用意されていますので,名前を書いてみるといいです.ボタン内に表示するIconサイズはButtonDefaults.IconSizeです.
.size(ButtonDefaults.IconSize)
Text
style
どのスタイルを使用するかが印象的に非常に重要
Text(
style = MaterialTheme.typography.body1
)
一部の値だけ変えて使用したい場合は以下のようにcopyを使用する.
Text(
style = MaterialTheme.typography.body1.copy(
fontWeight = FontWeight.ExtraBold
)
)
Button
colors
指定方法が少し特殊ですが,型がButtonColorsなので,ButtonDefaults.buttonColors, ButtonDefaults.textColors, ButtonDefaults.outlinedColorsを用いて指定します.全てデフォルトでMaterialThemeから与えられた値が入っているので,必要な値のみオーバーライドして使用します.
背景色をprimaryからsecondaryに変えたい場合は以下のようになります.
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondary
)
Icon
画像やアイコンを配置可能.
Icon(
Icons.Filled.Recommend,
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Iconsでは下記サイトに記載のあるマテリアルデザインの一部にデフォルトで使用可能です.全てを使用するためには,material-icons-extendedへの依存を追加する必要があります.ただし,公式にも記載があるようにapkサイズが大きくなる原因となるので,不要なリソースは含めない処理が必要です.
implementation "androidx.compose.material:material-icons-extended:$compose_version"
Spacer
余白を追加できます.sizeとpaddingを使うことが多い.
Coilを使用したUrlによる画像読み込み
composeで使用するためにはcompose用のライブラリも必要.バージョンはホームページを見て最新のものをご使用ください
implementation("io.coil-kt:coil:1.4.0") implementation("io.coil-kt:coil-compose:1.4.0")
MaterialTheme
Colors
typography
@Preview
showBackground
最背面まで表示するかどうか
@Preview(showBackground = true, widthDp = 320, heightDp = 320)
widthDp, heightDp
dpで横幅,縦幅を指定.Scaffoldでテーマを指定すると全範囲となってしまい描画に時間を要するので,widthDp/heightDpで現実的な横幅で設定しておいたほうが描画処理が軽い
uiMode = UI_MODE_NIGHT_YES
ダークモードで表示する
Jetpackの各ライブラリの最新バージョン一覧表
Jetpack ComposeとKotlinのバージョン対応表
基本ビルドエラーが指示されますので不要ですが,以下のような対応表となっています.今のStableリリース1.0.5に対応しているKotlinは1.5.31です.Kotlin1.6を使用したければ2022/2で最新リリース候補の1.1.0-rc03を使用するのが良さそうですね.