Jetpack Composeのライブラリで提供されるButtonは、クリックした時にリップルエフェクト(波紋が広がる)を表示します。クリック感をユーザへ伝える演出です。
このリップルエフェクトですが、デフォルトの動作で固定化されています。動作の変更や置き換えが出来ません。
アプリにボタンは多用されます。より見栄えの良いエフェクトへ変更できれば、アプリの差別化に役立つと思います。なぜ、固定化したのでしょう?!Button開発者の意図が分からないです…
変更できなければ、できるButtonを作るしかありません。
というわけで…
リップルエフェクトの動作の変更や置き換えが可能なButtonを作成してみました。
※環境:Android Studio Giraffe | 2022.3.1
:androidx.compose.material3:material3:1.1.1
:androidx.compose.ui:ui:1.4.3
目次
Buttonのリップルエフェクト
Jetpack Composeのライブラリで提供されるButton(既存Button)のリップルエフェクトを確認しましょう。
リップルエフェクトはButton関数の記述に登場しません。下位階層のSurface内で宣言されています。
@Composable fun Button( onClick: () -> Unit, modifier: Modifier = Modifier, ... ) { ... Surface( onClick = onClick, modifier = modifier.semantics { role = Role.Button }, ... ) { ... } }
Surfaceは4タイプあり、Buttonで使用されているのは引数onClickを持つタイプ(Type2)です。※Surfaceの詳細は「Compose UI:Surface」を参照
@Composable @NonRestartableComposable fun Surface( onClick: () -> Unit, modifier: Modifier = Modifier, ... ) { val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box( modifier = modifier .minimumInteractiveComponentSize() .surface(...) .clickable( interactionSource = interactionSource, indication = rememberRipple(), enabled = enabled, onClick = onClick ), propagateMinConstraints = true ) { content() } } }
Surface内でクリック処理は、Boxの引数modifierで行われていて、Modifier#clickable( )が使われています。ここで、クリック処理は必ず上書きされます。
リップルエフェクトはこのクリック処理内で宣言されていることがわかります。
クリック処理済みのmodifierは関数外部から参照できないので、クリック処理へ変更を加えることは不可能です。ですので、このSurface(Type2)は既存Button向けの特注品と言えます。
リップルエフェクトの動作を変更可能なButtonを実現したければ、専用のSurfaceが必要になります。
rememberRipple( )の引数
関数rememberRipple( )は、リップルを演出するIndicationインスタンスを返す関数です。
@Composable public fun rememberRipple( bounded: Boolean = true, radius: Dp = Dp.Unspecified, color: Color = Color.Unspecified ): Indication { val colorState = rememberUpdatedState(color) return remember(bounded, radius) { PlatformRipple(bounded, radius, colorState) } }
動作の変更が引数で出来ます。
引数 | 概要 | |
---|---|---|
bounded | Boolean | リップルをUIの境界でクリップする/しない true:クリップする、 波紋はタッチ位置から発生 false:クリップしない、波紋は中心から発生 |
radius | Dp | リップルのサイズ Dp.Unspecified:UIのサイズ |
color | Color | リップルの色、RippleTheme.rippleAlphaを適用 Color.Unspecified:RippleTheme.defaultColor |
Column(...) { Text( text = "Ripple Effect ( )", modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(), onClick = {}, ) ) Spacer(modifier = Modifier.size(24.dp)) Text( text = "Ripple Effect (bounded = false)", modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(bounded = false), onClick = {}, ) ) Spacer(modifier = Modifier.size(24.dp)) Text( text = "Ripple Effect (radius = 30.dp)", modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(radius = 30.dp), onClick = {}, ) ) Spacer(modifier = Modifier.size(24.dp)) Text( text = "Ripple Effect (color = Color.Red)", modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(color = Color.Red), onClick = {}, ) ) }
CombinedButtonの作成(引数indicationの取り出し)
リップルエフェクトの宣言(引数indication)を、Surfaceのクリック処理内からButtonの引数へ取り出して、外部から注入できるようしたCombinedButtonを作成します。
これにより、リップルエフェクトの動作を変更可能にします。
作成の詳細は「Compose Button:Single/Long/Double Click Buttonの作成」と同じです。
リップルエフェクトのサンプル:無効化
引数indicationへNullを指定すれば、エフェクトは無効化されます。
CombinedButton(indication = rememberRipple()) { Text(text = "indication = rememberRipple()") } CombinedButton(indication = null) { Text(text = "indication = null") }
リップルエフェクトのサンプル:Bounded
falseを指定してもUIの形状でクリップされてしまいます。しかし、リップルの広がりは中心からです。
CombinedButton(indication = rememberRipple(bounded = true)) { Text(text = "bounded = true") } CombinedButton(indication = rememberRipple(bounded = false)) { Text(text = "bounded = false") }
リップルエフェクトのサンプル:Color
リップルの色(サンプルは赤)が変わります。
CombinedButton(indication = rememberRipple(color = Color.Unspecified)) { Text(text = "color = Color.Unspecified") } CombinedButton(indication = rememberRipple(color = Color.Red)) { Text(text = "color = Color.Red") }
リップルエフェクトのサンプル:Radius
リップルのサイズが変わります。
CombinedButton(indication = rememberRipple(radius = Dp.Unspecified)) { Text(text = "radius = Dp.Unspecified") } CombinedButton(indication = rememberRipple(radius = 30.dp)) { Text(text = "radius = 30.dp") }
関連記事: