Compose UI:Button

投稿日:  更新日:

Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。

そのライブラリ中のButtonについて、構成や使用方法などをまとめます。

※環境:Android Studio Flamingo | 2022.2.1
   :androidx.compose.material3:material3:1.1.1
   :androidx.compose.ui:ui:1.4.3

スポンサーリンク

UIの概要

Buttonはクリック可能なボタンを表現するUI要素です。

Buttonの例

クリックすることでイベントが発生し、引数onClickで指定された関数オブジェクト(ラムダ式)が実行されます。

@Preview(showBackground = true)
@Composable
fun ButtonPreview() {
    Button(onClick = { /*ラムダ式*/ }) {
        Text(text = "OK")
    }
}
スポンサーリンク

関数の引数

Buttonの状態(コンテンツ、装飾、演出など)は、引数により指定できます。

@Composable
fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = ButtonDefaults.shape,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
    border: BorderStroke? = null,
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
)
引数概要
onClick( )->Unitクリックで実行する関数オブジェクト(ラムダ式)
modifierModifierUI全般のCompose修飾子
enabledBooleanUIの有効:true/無効:false
※有効⇒クリック可能、無効⇒クリック不可
shapeShape角の形状
colorsButtonColors有効/無効(enabled)に合わせた色の情報
elevationButtonElevation状態(Press/Focus/...)に合わせた高さの情報
borderBorderStroke輪郭
contentPaddingPaddingValuescontent周りの余白
interactionSourceMutableInteractionSourceユーザが行ったUI操作の観測と報告
contentRowScopw.( )->Unitボタンに表示する文字・アイコン
スポンサーリンク

関数の構成

ButtonはSurfaceとRowを重ね合わせた構成になっています。Rowはインライン関数なので展開されて階層は残りません。ですので、Buttonの実態はSurfaceであると言えます。

Buttonの構成

参考:Button関数全体(Button.ktより抜粋)
@Composable
fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = ButtonDefaults.shape,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
    border: BorderStroke? = null,
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
) {
    val containerColor = colors.containerColor(enabled).value
    val contentColor = colors.contentColor(enabled).value
    val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
    val tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp
    Surface(
        onClick = onClick,
        modifier = modifier,
        enabled = enabled,
        shape = shape,
        color = containerColor,
        contentColor = contentColor,
        tonalElevation = tonalElevation,
        shadowElevation = shadowElevation,
        border = border,
        interactionSource = interactionSource
    ) {
        CompositionLocalProvider(LocalContentColor provides contentColor) {
            ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
                Row(
                    Modifier
                        .defaultMinSize(
                            minWidth = ButtonDefaults.MinWidth,
                            minHeight = ButtonDefaults.MinHeight
                        )
                        .padding(contentPadding),
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically,
                    content = content
                )
            }
        }
    }
}

content(ボタンに表示する文字・アイコン)はRowに入るので、横並びの配置になります。

スポンサーリンク

クリック処理

ボタンなのでクリックすることでイベントが発生します。

イベントに反応して、引数onClickで指定された関数オブジェクト(ラムダ式)が実行されます。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(...) {
                    ButtonPanel(
                        onClickOK = ::clickOK,            // 関数オブジェクト
                        onClickNG = { Log.i(TAG, "Clicked NG!") } // ラムダ式
                    )
                }
            }
        }
    }

    fun clickOK() { Log.i(TAG, "Clicked OK!") }
}

@Composable
fun ButtonPanel(onClickOK: () -> Unit, onClickNG: () -> Unit,) {
    Row(...) {
        Button(onClick = onClickOK, ...) {
            Text(text = "OK")
        }
        Button(onClick = onClickNG, ...) {
            Text(text = "NG")
        }
    }
}
11:03:29.374   Clicked OK!
11:03:30.927   Clicked NG!
11:03:32.030   Clicked NG!
11:03:33.253   Clicked OK!

LongClick(長押し)はサポートされていません。

スポンサーリンク

インタラクション処理

ユーザのUI操作で発生したイベントがシステムに送られ、システムは見返りとしてエフェクトをユーザに返します。

このような相互作用(インタラクション)によって、ユーザとシステムはコミュニケーションを取っています。

インタラクション

これを「インタラクション処理」といいます。

リップルエフェクト

クリックによるボタンの状態変化(Release>Press>Release)でリップルエフェクト(Ripple Effect)が表現され、薄いハイライトが広がります。クリック感をユーザへ示すための演出です。

Buttonのインタラクション処理はリップルエフェクトに固定化されています。

リップル(波紋)に変更(色を変えるなど)を加えたり、リップル自体を別のエフェクトへ置き換えたりできません。

ユーザエフェクト

InteractionSourceはユーザの操作によるUI要素の状態変化を観測し、非同期に報告してくれます。

次のような状態変化が観測可能です。

状態変化関数(InterractionSourceの拡張関数)
PressInteractionSource.collectIsPressedAsState()
FocusInteractionSource.collectIsFocusedAsState()
DraggeInteractionSource.collectIsDraggedAsState()
HovereInteractionSource.collectIsHoveredAsState()

この報告に合わせたユーザ定義のエフェクトを追加できます。

例えば、Buttonの押下(Press)で文字色を赤に変えるエフェクトは、次のように記述できます。

            val interactionSource = remember { MutableInteractionSource() }

            val isPressed by interactionSource.collectIsPressedAsState()
            Button(
                onClick = { /* do something */ },
                interactionSource = interactionSource) {
                Text(
                    text = "OK",
                    color = if(isPressed) Color.Red else Color.White
                )
            }

変数isPressedはState<Boolean>型であり、UI要素の状態の追跡対象です。

isPressが非同期に変化すれば変化に応じてButtonが再コンポーズとなり、「isPress==true」で文字”OK”が赤(Color.Red)へ変化し、「isPress==false」で白(Color.White)へ変化します。

参考:collectIsPressedAsState()関数
@Composable
fun InteractionSource.collectIsPressedAsState(): State<Boolean> {
    val isPressed = remember { mutableStateOf(false) }
    LaunchedEffect(this) {
        val pressInteractions = mutableListOf<PressInteraction.Press>()
        interactions.collect { interaction ->
            when (interaction) {
                is PressInteraction.Press -> pressInteractions.add(interaction)
                is PressInteraction.Release -> pressInteractions.remove(interaction.press)
                is PressInteraction.Cancel -> pressInteractions.remove(interaction.press)
            }
            isPressed.value = pressInteractions.isNotEmpty()
        }
    }
    return isPressed
}

InteractionSourceによるUI要素の状態変化の観測と報告はFlowにより行われています。

※Flowについては「Coroutine:コルーチン間でメッセージの送受信」を参照

スポンサーリンク

派生タイプ

Button関数をベースにした派生タイプが定義されています。

どれも、Button関数の引数の初期値を上書きし、装飾と演出を変更したものです。構成に手を加えたものではありません。

Button(ベース)

塗りつぶされたボタンです。色はマテリアルデザインのPrimaryColorが使用されます。

Button(FilledButton)

Buttonは派生タイプのベースになるUIです。この記事中に使用されているサンプルは、Button関数を使っています。

マテリアルデザインのButtonの「FilledButton」に相当します。

FilledTonalButton

塗りつぶされたボタンです。色はマテリアルデザインのSecondaryColorが使用されます。

FilledTonalButton

@Composable
fun FilledTonalButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = ButtonDefaults.filledTonalShape,
    colors: ButtonColors = ButtonDefaults.filledTonalButtonColors(),
    elevation: ButtonElevation? = ButtonDefaults.filledTonalButtonElevation(),
    border: BorderStroke? = null,
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
)

ElevatedButton

画面から突出しているような形状を演出したボタンです。デフォルトはあまり突出しているように見えませんが…

ElevatedButton

@Composable
fun ElevatedButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = ButtonDefaults.elevatedShape,
    colors: ButtonColors = ButtonDefaults.elevatedButtonColors(),
    elevation: ButtonElevation? = ButtonDefaults.elevatedButtonElevation(),
    border: BorderStroke? = null,
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
)

OutlinedButton

輪郭を持ったボタンです。

OutlinedButton

@Composable
fun OutlinedButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = ButtonDefaults.outlinedShape,
    colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
    elevation: ButtonElevation? = null,
    border: BorderStroke? = ButtonDefaults.outlinedButtonBorder,
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
)

TextButton

テキストのみのボタンです。

TextButton

@Composable
fun TextButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = ButtonDefaults.textShape,
    colors: ButtonColors = ButtonDefaults.textButtonColors(),
    elevation: ButtonElevation? = null,
    border: BorderStroke? = null,
    contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable RowScope.() -> Unit
)
スポンサーリンク

関連記事:

Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のSurfaceについて、構成や使用方法などをまとめます。 ※環境:Android Studio Flamingo | 2022.2.1    :androidx.compose.material3:material3:1.1.1    :androidx.compose.ui:ui:1.4.3 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のRadioButtonについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中にUI要素を格納する機能をもつコンテナ型のUIがあります。 Column/Row/Boxがコンテナ型のUIです。構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のImageについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.foundation 1.5.0 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のDividerについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.material3:material3 1.1.1 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のSpacerについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.foundation 1.5.0 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のSliderについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.material3:material3 1.1.1 ...
スポンサーリンク