スポンサーリンク

【まとめ】Jetpack Compose

スポンサーリンク

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に委譲されます.コードが大変シンプルになるのが魅力ですが,初見では理解しにくい気がします.

Delegated properties | Kotlin

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関数とはなんでしょうね?関数変数のインスタンス化によるオーバーヘッドを避けるためにコンパイル時に実際にコードに埋め込んでくれる機能です.従って,機能だけを把握したいときは読み飛ばして問題ありません.

Inline functions | Kotlin

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という機能を使用した宣言になります.

Destructuring declarations | Kotlin

すなわち,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"
Material Symbols and Icons - Google Fonts
Material Symbols are our newest icons consolidating over 2,500 glyphs in a single font file with a wide range of design variants.

Spacer

余白を追加できます.sizeとpaddingを使うことが多い.

Coilを使用したUrlによる画像読み込み

androidの公式はここ.
coilの公式はここ

composeで使用するためにはcompose用のライブラリも必要.バージョンはホームページを見て最新のものをご使用ください

implementation("io.coil-kt:coil:1.4.0")
implementation("io.coil-kt:coil-compose:1.4.0")

MaterialTheme

Colors

Material Design
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experienc...

typography

Material Design
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experienc...

@Preview

showBackground

最背面まで表示するかどうか

@Preview(showBackground = true, widthDp = 320, heightDp = 320)

widthDp, heightDp

dpで横幅,縦幅を指定.Scaffoldでテーマを指定すると全範囲となってしまい描画に時間を要するので,widthDp/heightDpで現実的な横幅で設定しておいたほうが描画処理が軽い

uiMode = UI_MODE_NIGHT_YES

ダークモードで表示する

Jetpackの各ライブラリの最新バージョン一覧表

AndroidX releases  |  Jetpack  |  Android Developers

Jetpack ComposeとKotlinのバージョン対応表

基本ビルドエラーが指示されますので不要ですが,以下のような対応表となっています.今のStableリリース1.0.5に対応しているKotlinは1.5.31です.Kotlin1.6を使用したければ2022/2で最新リリース候補の1.1.0-rc03を使用するのが良さそうですね.

Compose to Kotlin Compatibility Map  |  Android Developers

Test

Cheat Sheet

androidx.compose.ui.test  |  Android Developers
タイトルとURLをコピーしました