FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

Jetpack Compose Modifierについて

はじめに

こんにちは。 ピクトリンク事業部でwebアプリケーション開発を担当している西村です🧑‍💻
今回はJetpack ComposeのModifieについてまとめてみようと思います!

Jetpack Composeは、Android開発においてAndroid Viewに変わる新たなUIツールキットです。
その中でもModifierComposable関数の外観や動作をカスタマイズするための重要な要素になります。
本記事では、様々な種類のModifierとその使用例について解説しようと思います🙋‍♂️

Modifierとは

Modifierは、Composable関数に対して順序付けられた組み合わせ可能な要素で、
UIコンポーネントのレイアウト、描画、ジェスチャーなどをカスタマイズするために使用されます。

以下の例ではBoxのModifierに2つの設定をしています。

Box(
    modifier = Modifier
    .padding(16.dp)
    .background(Color.Blue)
) {
    Text(
        text = "Hello Jetpack Compose",
    )
}

.padding(16.dp)はBoxに16dpのPaddingを設定
.background(Color.Blue)はBoxの背景色に青色を設定

Modifierオブジェクトにチェーンしながら設定を追加していくことでUIに様々な設定を行うことが可能です!
以降は様々な種類のModifierの中でもよく使用しそうなものをピックアップしてご紹介します。

サンプルコード

以降様々なModifierの設定を説明する際に使用するコードです。
もし不明な箇所が出てきた場合はこちらをご確認下さい。

@Composable
fun ProfileCard() {
    Card(
        shape = MaterialTheme.shapes.medium,
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.primaryContainer
        ),
        elevation = CardDefaults.cardElevation(
            defaultElevation = 4.dp
        )
    ) {
        Row(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            // ユーザー情報
            Column {
                // 氏名
                Text(
                    text = "Yoshihiro Nishimura",
                    style = MaterialTheme.typography.titleMedium,
                    color = MaterialTheme.colorScheme.onPrimaryContainer
                )
                // 職業
                Text(
                    text = "Android Developer",
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.onPrimaryContainer
                )
                Spacer(modifier = Modifier.height(8.dp))
                // ボタン
                OutlinedButton(
                    onClick = { /* TODO: アクションを定義 */ },
                    colors = ButtonDefaults.outlinedButtonColors(
                        contentColor = MaterialTheme.colorScheme.primary
                    )
                ) {
                    // ボタンのアイコン画像
                    Icon(
                        imageVector = Icons.Default.Email,
                        contentDescription = stringResource(id = R.string.connect),
                        modifier = Modifier.size(18.dp)
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    // ボタンのテキスト
                    Text("Contact")
                }
            }

            // プロフィールアイコン
            Image(
                painter = painterResource(id = R.drawable.profile_icon),
                contentDescription = "Profile Image",
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .size(72.dp)
                    .clip(CircleShape)
                    .border(2.dp, MaterialTheme.colorScheme.primary, CircleShape)
            )
        }
    }
}

レイアウト系Modifier

レイアウト系Modifierは、コンポーネントのサイズや位置、余白などを制御するために使用します。

padding

modifier = Modifier.padding(16.dp)

説明
コンポーネントの内側または外側に余白を追加します。
今回はサンプルコードのRow要素に設定してあげることで、親のCard要素に対して16dpの余白が生まれています。

以下のように一部のみ設定することや数値を調整することも可能です。

modifier = Modifier.padding(start = 4.dp, end = 8.dp, bottom = 8.dp)

fillMaxWidth

modifier = Modifier.fillMaxWidth()

説明
コンポーネントの幅を親コンポーネントの最大幅に合わせて設定します。
今回はサンプルコードのRow要素に設定してあげることで、親のCard要素に対して16dpの余白を守りつつ、横幅を最大まで広げています。
fillMaxWidthに類似している設定としてfillMaxHeight、fillMaxSizeも同じような考え方です。

heightとwidth

Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.width(8.dp))

説明
heightは高さを指定し、widthは横幅を設定します。
今回はサンプルコードのSpacer要素に設定してあげることで、8dpの余白が生まれるようにしています。

size

modifier = Modifier.size(72.dp)

説明
コンポーネントの縦横サイズを同時に設定します。
今回はサンプルコードのImage要素に設定してあげることで、72dpのプロフィール画像を表示しています。

描画系Modifier

描画系Modifierは、コンポーネントの見た目を変更するために使用します。

clip

modifier = Modifier.clip(CircleShape)

説明
コンポーネントを特定の形状に切り抜きします。
今回はサンプルコードのImage要素に設定しており、CircleShapeを引数に渡しているため、画像が円形に切り抜きされます。
注意点としては 複雑な形状で大きな画像やコンポーネントを切り抜きしようとすると、描画パフォーマンスに影響が出る場合があります。
そのため必要に応じて適切なサイズや形状を選択する必要があります。

border

modifier = Modifier.border(2.dp, MaterialTheme.colorScheme.primary, CircleShape)

説明
コンポーネントに境界線を追加します。
今回はサンプルコードのImage要素に設定しているため、円形で切り抜かれた箇所に境界線を描いています。

ジェスチャー系Modifier

ジェスチャー系Modifierは、ユーザーのタッチ操作などのインタラクションを処理するために使用します。

clickable

modifier = Modifier.clickable { /* クリック時の処理 */ }

説明
コンポーネントをクリック可能にし、クリック時に処理を実行することができます。

Scrollable

val state = rememberScrollState()
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .verticalScroll(state),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        repeat(10){
            ProfileCard()
        }
    }

説明
スクロール対象のコンテンツのサイズが親コンテンツのサイズを超える場合にスクロールすることができます。
verticalScrollは縦方向、horizontalScrollは横方向にスクロールすることができます。

Modifierの順序の重要性

Modifierはチェーンしている設定の順序によって結果が変わるため、注意が必要です。
clickableとpaddingの順序を例にしてみます。

パターン①

Box(
        modifier = Modifier
            .clickable { /* クリック時の処理 */ }
            .padding(16.dp)
            .background(color = Color.White)
    ) {
        Text(
            text = "Hello Jetpack Compose",
        )
    }

パターン②

Box(
        modifier = Modifier
            .padding(16.dp)
            .clickable { /* クリック時の処理 */ }
            .background(color = Color.White)
    ) {
        Text(
            text = "Hello Jetpack Compose",
        )
    }

2つのパターンの差はclickableとpaddingの順序です。
パターン①の場合は白背景の部分しかクリックできませんが、
パターン②の場合はマージンの範囲含めてクリックすることができます。

パターン①のクリック範囲

パターン②のクリック範囲

これはModifierの設定の反映がチェーンで繋いだ順番になるからです。
ただ単に設定するだけでは期待した動作にならないことが多いので、設定順も考慮するようにしましょう

カスタムModifierの作成

頻繁に使用するModifierの組み合わせは、カスタムModifierとして定義すると便利です。
以下のようにModifier.名称 で定義することで様々な箇所で使用することができます。

@Composable
fun Modifier.profileIconStyle() = this
    .size(72.dp)
    .clip(CircleShape)
    .border(2.dp, MaterialTheme.colorScheme.primary, CircleShape)
Image(
    painter = painterResource(id = R.drawable.profile_icon),
    contentDescription = "Profile Image",
    contentScale = ContentScale.Crop,
    modifier = Modifier.profileIconStyle()
)

最後に

今回はJetpack ComposeのModifierについてまとめました!
今回記事を書いたおかげで理解が深まり頭の中を整理できた気がします。
今後も適切にModifierを設定して、様々なUIを構築してみようと思います!