今回のJetpack ComposeサンプルはSwitchコンポーザブル( androidx.compose.material.switch)です。単一のアイテム、機能の有効/無効やON/OFFを示します。表示例のようにTextコンポーザブルと一緒につかうことが多いでしょう。このスクリーンショットでは有効状態を示してます。
これまでTextそしてButtonコンポーザブルと紹介してきましたが今回は、ついに状態を持つコンポーザブルの登場です。androidx.compose.materialパッケージが提供しているコンポーザブルのなかで状態を表現できるものはSwitch以外に、CheckboxやIconToggleButtonコンポーザブルがあります。
Switchを表示する
Switchコンポーザブルの引数ではチェックされているかどうか、つまりON/OFFを示すcheckedと変化を受け取るコールバックonCheckedChangeの2つが必須となっています(その他は省略可能)。
@Composable fun SwitchSample() { val checkedState = remember { mutableStateOf(true) } Switch( checked = checkedState.value, onCheckedChange = { checkedState.value = it } ) }
サンプルコードではSwitchの引数checkedに有効/無効の状態を表すcheckedState.valueを設定し、onCheckedChangeでは変更された値itを受け取り、checkedState.valueを新しい値(it)で書き換えています。
この部分はKotlinに慣れていないと不思議な書き方に見えると思うので少しだけ解説しておきます。Switchコンポーザブルの引数、onCheckedChangeの定義はonCheckedChange: ((Boolean) -> Unit)?となっています。これは次のように書き換えられます。
Switch( checked = checkedState.value, onCheckedChange = { changedValue -> checkedState.value = changedValue } )
((Boolean) -> Unit)?のBooleanは、このサンプルのようにchangedValueという名前で受け取ってもいいのですが、冗長であるためKotlin言語では省略できます。省略した場合はitで受け取る、いわゆる糖衣構文(シンタックスシュガー)です。
状態を記憶する
サンプルコードででてきたmutableStateOfはオブザーバブルなデータホルダーです。MutableStateクラスを返却します。
val checkedState = remember { mutableStateOf(true) }
さらにrememberで受け取ってコンポーザブルで状態を覚えておきます。
もうちょっと詳しく(読み飛ばし可)
※ここは読み飛ばしても死にません。詳しく知りたいひとはComposeの公式ガイドライン「Lifecycle of composables」がわかりやすいです。
前述のmutableStateOfは引数で初期化したMutableStateを生成しています。このMutableStateクラスは、ミュータブル(可変)という名前のとおり変更可能です。Composeのシステムによって読み取りと書き込みがオブザーブ(監視)されていますが(詳細は別記事で解説したいと考えているので)ここでは「変更がわかる便利なデータホルダーだな」という理解で大丈夫です。
rememberは引数(calculationとよばれます。このサンプルではMutableStateが渡されています)を覚えてくれる便利なインライン関数です。引数で受け取った値はCompositionでのみ評価されます…というと何をいっているのかわからないかもしれません。Compositionはコンポーザブルのツリー構造を表現する概念で、Composeで記述したUIの変更を検出します。ツリー構造をもっているCompositionは特定のキッカケで再構成するのですが、そのキッカケは再描画が必要なとき、つまりSwitchコンポーザブルなどUIの状態が変わったとき(=MutableStateなどのState<T>の変更)をトリガーとします。
Compositionを再構成(これをRecomposition、再Compositionなどと呼びます)するタイミングを伝える関数がrememberとも言えます。再描画のタイミングを状態変更のタイミングのみに制約することは一見、わかりにくいかもしれませんが、描画の効率性(変更があったUIツリーのみ更新すればいい)かつ、データフローがわかりやすくなるメリットがあります。
閑話休題。
Switchを変更する
Switchコンポーザブルの引数checkedがfalse、つまりなにかがOFFであることを示す場合は次のようにします。
@Preview(showBackground = true) @Composable fun NonchekedSwitch() { Switch( checked = false, onCheckedChange = null ) }
Switchコンポーザブルが無効のとき
Switchコンポーザブルそのものが操作不能であるなどコンポーザブルが無効である場合はenabled引数をfalseにすれば表現できます。
@Preview(showBackground = true) @Composable fun DisabledSwitch() { Switch( checked = true, onCheckedChange = null, enabled = false ) }
Switchのスタイルを変える
ボタン等と同じく、SwitchコンポーザブルのスタイルはSwitchDefaultsにまとまっています。次の図はチェック状態を赤色に変更したものです。
@Preview(showBackground = true) @Composable fun ColoredSwitch() { Switch( checked = true, onCheckedChange = null, colors = SwitchDefaults.colors( checkedThumbColor = Color.Red ) ) }
SwitchDefaults.colorsにはcheckedThumbColorをいれて10のスタイル情報が含まれています。enabledとcheckedの組み合わせに応じて色や透明度を指定できるようになっています。
- checkedThumbColor:enabledかつcheckedでの●部分の色。同様にuncheckedThumbColor、disabledCheckedThumbColor 、disabledUncheckedThumbColorの合計4つがある
- checkedTrackColor:enabledかつcheckedでの横棒=Trackの色。同様にuncheckedTrackColor、disabledCheckedTrackColor、disabledUncheckedTrackColorの合計4つがある
- checkedTrackAlpha:checkedTrackColorとdisabledCheckedTrackColorで使う透明度
- uncheckedTrackAlpha:uncheckedTrackColorとdisabledUncheckedTrackColorで使う透明度
Switchをレイアウトする
マテリアルデザインガイドラインに則ってSwitchコンポーザブルを表示する場合、いろんな実現方法がありますが、手っ取り早くというのであれば次のようなレイアウトが手軽です。
次のようにRowコンポーザブルを使ってTextとSwitchを左右に配置します。このレイアウトのポイントはSpacerコンポーザブルをつかった調整です。
@Preview(showBackground = true) @Composable fun SwitchWithText() { Row( Modifier .width(320.dp) .padding(horizontal = 16.dp)) { Text( text = "Jetpack Compose Switch", modifier = Modifier .align(Alignment.CenterVertically) .padding(16.dp), style = MaterialTheme.typography.subtitle1 ) Spacer(Modifier.weight(1f)) val checkedState = remember { mutableStateOf(true) } Switch( checked = checkedState.value, onCheckedChange = { checkedState.value = it }, modifier = Modifier.align(Alignment.CenterVertically) ) } }
Switchコンポーザブルの引数
@Composable @OptIn(ExperimentalMaterialApi::class) fun Switch( checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, modifier: Modifier = Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, colors: SwitchColors = SwitchDefaults.colors() ) {
- checked:チェックされているかどうか
- onCheckedChange:スイッチの変化を受け取るコールバック
- modifier:レイアウトへ反映するmodifier
- enabled:スイッチの有効/無効(操作できる/できない)
- interactionSource:インタラクションに合わせてスタイルや振る舞いを変えたいときはソースとなるストリームを設定する
今回はSwitchコンポーザブルを紹介しました。途中、状態の保存を解説したので少し長くなりましたが、それでもJetpack Composeの根幹をなす部分ですので説明が足りてないところもあります。状態管理の面白さについては技術書典11の書籍化の際や別の記事で触れられれば、と考えています。
Switchコンポーザブルは単体で使うことはまずないため、レイアウトのサンプルもいっしょに紹介しました。ちょっとしたテクニックはJetpack Compose Sampleなど公式のJetpack Composeサンプル集でも見つけることが出来ます。気になるUIがあれば、どのような実装か探してみると面白いはずです。
実務ではSwitchだけでなく冒頭で説明したようなCheckboxやIconToggleButtonコンポーザブルも候補にあがりますので、どこかでこのあたりのコンポーザブルも解説できればと考えています。
- Switchコンポーザブルのリファレンス
- マテリアルデザインガイドライン(マテリアルスイッチ)
- スタイルを変更するSwitchDefaultsのリファレンス
- レイアウトの参考にしたcompose-samplesの実装
- State and Jetpack Compose – Composeの状態管理ドキュメント
- Lifecycle of composables – Composableの描画や更新について
今回の記事はこれでおしまいです。お疲れさまでした。