Modifier — Composeにおける修飾子チェーンとパフォーマンス

著者: IT Sectr 公開日: 2026-06-28 読了時間: 8 分

ModifierはJetpack Composeにおける不変オブジェクトで、UIコンポーネントのプロパティ(サイズ、パディング、背景、ジェスチャー処理、動作)を定義します。修飾子は逐次呼び出しによってチェーンに結合され、その適用順序が結果に決定的な影響を与えます。Google Android Developers, 2026によると、Modifierの正しい使用は宣言的UIにおける柔軟で高性能なインターフェース構築の基盤です。

重要なポイント

  • ModifierはUIコンポーネントの外観と動作を記述する不変オブジェクトです
  • チェーンは逐次的に構築され、順序が表示に影響します
  • 順序が重要:padding → size と size → padding は異なります
  • Modifier.composedでカスタム複合修飾子を作成できます
  • 最適化:再コンポジションのたびにModifierを再作成しないでください

Jetpack ComposeにおけるModifierとは

Modifierはandroidx.compose.uiパッケージのインターフェースで、Compositeパターンを実装しています。各修飾子はチェーン要素であり、前の要素をラップして独自の動作を追加します。Modifierは不変です。変更があると、新しい要素をチェーンに追加したコピーとして新しいオブジェクトが作成されます。これにより、単一のModifierを複数のコンポーネント間で安全に共有できます。

基本的な修飾子関数はコンパニオンオブジェクトModifierを介して呼び出されます(例:Modifier.padding()、Modifier.fillMaxWidth())。各関数は追加された要素を含む新しいModifierを返します。複数の修飾子がある場合、それらはチェーンに結合されます:Modifier.padding(16.dp).fillMaxWidth().background(Color.Blue)。順序はUI要素に対して外側から内側へと進みます。

従来のViewではプロパティがセッターを介して設定されていましたが(view.setPadding(...)、view.setBackground(...))、ComposeではModifierは宣言的な記述です。コンポーネントは実行時に修飾子を「適用」しません。LayoutNodeがコンポジション中にModifierチェーンを走査し、Modifier.Elementのリストを構築し、これらが測定およびレイアウトフェーズで処理されます。

修飾子チェーンと適用順序

修飾子の順序はComposeで最もよくある間違いの一つです。各修飾子は前のものをラップし、操作は外側から内側へ適用されます。例えば、padding(16.dp).clickable { }:最初に要素の周りにパディングが追加され、次にクリック領域にパディングが含まれます。clickable { }.padding(16.dp):クリック領域は最初に要素のサイズと同じになり、その後周りにパディングが追加されます。パディングをクリックしても機能しません。

覚え方のルール:チェーンを左から右に読み、外側から内側に適用する。最初の修飾子は最も外側で、要素の周囲の領域に適用されます。最後の修飾子は最も内側で、コンテンツに直接適用されます。サイズ修飾子(size、fillMaxWidth)は、親からのパディングが必要な場合はパディングの後に、コンテンツを先に制約してから中央揃えにする必要がある場合はパディングの前に配置します。

例:size(100.dp).padding(10.dp) — 固定サイズ100dpの要素、その後外側に10dpのパディング(最終サイズ120dp)。padding(10.dp).size(100.dp) — 10dpのパディングが利用可能スペースを(親 - 20dp)に減らし、その後size(100dp)が親からはみ出す可能性があります。結果を検証するために表示テストを使用して、常に順序を意識的に検討してください。

順序結果
padding → clickableパディング領域でもクリックが機能する
clickable → paddingクリックはコンテンツのみで機能し、パディングはデッドゾーン
size → padding要素size(100)、外側にパディング → 100+2*pad
padding → sizeパディングがスペースを減らし、sizeが境界を超える可能性
background → padding背景が外部領域を含む要素全体を塗りつぶす
padding → background背景はパディングの内側のみ(外部領域は透明)

修飾子の種類:サイズ、パディング、装飾、動作

標準Composeライブラリには約50以上の修飾子があり、カテゴリに分類されています。サイズと配置:Modifier.size()、width()、height()、fillMaxSize()、fillMaxWidth()、fillMaxHeight()、defaultMinSize()、requiredSize()。パディングと境界:padding()、offset()、margin(親のパディングまたはLayoutを介して設定)。装飾:background()、border()、clip()、alpha()、shadow()、blur()。

動作とジェスチャー:clickable()、combinedClickable()、pointerInput()、draggable()、swipeable()。コンテナ内のレイアウト:weight()(Row/Column用)、align()、alignBy()、matchParentSize()。セマンティクスとアクセシビリティ:semantics()、testTag()、clearAndSetSemantics()。描画:drawBehind()、drawWithContent()、drawModifier() — キャンバス上でのカスタム描画を可能にする修飾子。

セマンティック修飾子は特別なカテゴリです。Modifier.semantics {}は、要素がアクセシビリティツリーでどのように表現されるかを定義します。Composeはテキストから自動的にセマンティクスを設定しますが、カスタムコンポーネントでは役割、状態、アクションを手動で設定する必要があります。これはWCAG 2.2への準拠とTalkBack(Android)およびVoiceOver(iOS)の正しい動作にとって重要です。

kotlin
@Composable
fun ModifierDemo() {
    // 正しい順序の修飾子チェーン
    Box(
        modifier = Modifier
            .size(150.dp)
            .padding(8.dp)
            .border(2.dp, Color.Gray)
            .background(Color(0xFFE3F2FD))
            .clickable { /* handle click */ }
            .semantics {
                contentDescription = "Demo card with click action"
                role = Role.Button
            }
    ) {
        Text("触ってください")
    }
}

Modifier.composedによるカスタム修飾子の作成

Modifier.composedはファクトリメソッドで、他の修飾子、LocalComposition、ローカル状態を使用できる複合修飾子を作成できます。通常の拡張関数とは異なり、composedは適用されるたびにインスタンスを作成するため、修飾子が独自の状態を持つことができます。

composedを使用するタイミング:繰り返し使用される修飾子の組み合わせ(例:標準カードスタイル:padding + background + border + clickable);状態を持つ修飾子(押下時のアニメーション背景変更);CompositionLocalsへのアクセス(MaterialThemeの配色、ピクセル密度)。通常のケースでは、composedなしの通常の拡張関数で十分です。

composedのパフォーマンス:各呼び出しが新しい修飾子オブジェクトを作成するため、再コンポジション中に余分な割り当てが発生する可能性があります。これを防ぐには、composedをrememberでラップします。Googleは、内部で実際に状態またはCompositionLocalが必要な場合にのみcomposedを使用することを推奨しています。静的な組み合わせには、通常の拡張関数を使用してください。

kotlin
// 状態を持つcomposedによるカスタム修飾子
fun Modifier.cardStyle(
    elevation: Dp = 4.dp,
    isSelected: Boolean = false
): Modifier = this.composed {
    val backgroundColor = if (isSelected)
        MaterialTheme.colorScheme.primaryContainer
    else
        MaterialTheme.colorScheme.surface

    this
        .fillMaxWidth()
        .padding(12.dp)
        .background(backgroundColor, RoundedCornerShape(8.dp))
        .shadow(elevation, RoundedCornerShape(8.dp))
}

// 使用例
@Composable
fun CardList() {
    Column {
        Box(Modifier.cardStyle()) { Text("項目1") }
        Box(Modifier.cardStyle(isSelected = true)) { Text("選択済み") }
    }
}

// 静的バージョン(composedなし)— より高速
fun Modifier.simpleCardStyle(): Modifier =
    this.fillMaxWidth().padding(8.dp).clip(RoundedCornerShape(4.dp))

Modifierのパフォーマンスとベストプラクティス

再コンポジションのたびにModifierを再作成しないでください。修飾子が可変データに依存しない場合は、定数またはrememberに移動してください。Modifier.padding().background()を呼び出すたびに新しいModifier.Elementオブジェクトが作成されます。孤立したコンポーネントでは無視できますが、数百の要素を持つLazyColumnでは、余分な割り当てがスクロール時に顕著な遅延を引き起こします。

ルール:修飾子チェーンがComposable関数のパラメータに依存しない場合は、関数の外側(ファイルレベルまたはCompanion)でvalとして宣言してください。依存する場合は、remember(依存関係) { ... }を使用してください。常に同じ修飾子の場合、Composableの外側のvalが最も効率的です。そのようなオブジェクトはアプリケーションの全ライフタイムで一度だけ作成されます。

Modifier順序のベストプラクティス:修飾子を論理的な順序で配置します。最初にサイズ/パディング(レイアウト)、次に装飾(background、border)、最後に動作(clickable、pointerInput)。これにより可読性が向上するだけでなく、Compose Runtimeが測定中にチェーンを最適化するのに役立ちます。また、異なるModifierを持つ過剰にネストされたBoxを避けてください。多くの場合、親コンテナ上の単一のModifierで2〜3のネストを置き換えることができます。

kotlin
// ✅ 良い:Composable外の定数
private val cardModifier = Modifier
    .fillMaxWidth()
    .padding(16.dp)
    .clip(RoundedCornerShape(8.dp))

@Composable
fun CardContent() {
    Box(cardModifier.background(Color.White)) { ... }
}

// ❌ 悪い:再コンポジションごとの再作成
@Composable
fun BadCard() {
    Box(Modifier.fillMaxWidth().padding(16.dp)) { ... }
}

// ✅ 良い:動的Modifierにはremember
@Composable
fun DynamicCard(color: Color) {
    val modifier = remember(color) {
        Modifier.fillMaxWidth().background(color)
    }
    Box(modifier) { ... }
}

よくある質問

複数のComposable要素に同じModifierを使用できますか?

はい、Modifierは不変なので、同じオブジェクトを複数の場所で安全に使用できます。ただし、composed修飾子を使用する場合、各呼び出しで新しいインスタンスが作成されます。静的なチェーンの場合は、Composableの外側の定数またはvalが最適な解決策です。

修飾子チェーンをデバッグするにはどうすればよいですか?

Android StudioのLayout Inspectorを使用してください。各Modifierの境界を視覚的に表示します。プログラムによるデバッグの場合は、チェーンの各ステップで異なる色のModifier.border()を追加して、各修飾子の境界を確認します。

Modifier.then()とは何ですか?逐次呼び出しとどう違いますか?

Modifier.then(other)は、otherチェーンをthisに追加します。逐次呼び出し(Modifier.a().b())はModifier.then(a()).then(b())と同等です。違いはありません。同じチェーンメカニズムです。then()は、変数から既製のチェーンを追加する必要がある場合に便利です。

Modifierはアクセシビリティのセマンティクスにどのように影響しますか?

Modifier.semantics {}は、要素がスクリーンリーダーにどのように説明されるかを定義します。Modifier.clickable()は自動的にButtonロールとAction(OnClick)を追加します。カスタムジェスチャーの場合は、明示的にsemanticsを指定する必要があります。セマンティック修飾子がないと、TalkBackユーザーはカスタムコンポーネントを操作できません。

Modifierのbackgroundが角丸で機能しないのはなぜですか?

Modifier.background(color, shape)は角と連携しますが、角をクリップするにはclip()をbackgroundの前に配置する必要があります。正しい順序:clip(shape).background(color)。内部のコンテンツもクリップする必要がある場合は、親でclipToBounds()を使用してください。

まとめ

  • Modifierは外観と動作を宣言的に記述するための不変オブジェクト
  • 順序が結果を決定:padding → clickable と clickable → padding は異なる
  • チェーンは逐次的に構築され、各要素が前のものをラップする
  • Modifier.composedで状態とCompositionLocalを持つ修飾子を作成可能
  • パフォーマンス:静的なチェーンは定数に、動的なものにはrememberを使用
  • セマンティクス:カスタムコンポーネントのアクセシビリティにはModifier.semanticsが必須
  • 推奨:修飾子をレイアウト→装飾→動作の順に配置する

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください