Compose Button:リップルエフェクト(Indication)の変更

投稿日:  更新日:

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)
    }
}

動作の変更が引数で出来ます。

引数概要
boundedBooleanリップルをUIの境界でクリップする/しない
  true:クリップする、 波紋はタッチ位置から発生
  false:クリップしない、波紋は中心から発生
radiusDpリップルのサイズ
  Dp.Unspecified:UIのサイズ
colorColorリップルの色、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")
        }
スポンサーリンク

関連記事:

Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のButtonについて、構成や使用方法などをまとめます。 ※環境:Android Studio Flamingo | 2022.2.1    :androidx.compose.material3:material3:1.1.1    :androidx.compose.ui:ui:1.4.3 ...
Jetpack Composeのライブラリで提供されるButtonは、ロングクリック(長押し)に対応していません。 ロングクリックは重宝します。 例えば、ミスタッチで起動して欲しくない機能に使ったり、裏技を仕込んだり、などです。なくても困らないけど、あった方が断然便利です。 ロングクリックが無ければ、有るButtonを作るしかありません。 というわけで... Single/Long/Doubleクリックに対応したButtonを作成してみました。 ※環境:Android Studio Giraffe | 2022.3.1    :androidx.compose.material3:material3:1.1.1    :androidx.compose.ui:ui:1.4.3 ...
スポンサーリンク