制約と修飾子の順序

Compose では、複数の修飾子を連鎖させて、コンポーザブルのルック アンド フィールを変更できます。これらの修飾子の連鎖は、コンポーザブルに渡される制約に影響する可能性があります。制約は、幅と高さの境界を定義します。

このページでは、連鎖した修飾子が制約にどのように影響し、その結果としてコンポーザブルの測定と配置にどのように影響するかについて説明します。

UI ツリー内の修飾子

修飾子が互いにどのように影響するかを理解するには、コンポジション フェーズで生成される UI ツリーに修飾子がどのように表示されるかを視覚化すると便利です。詳細については、コンポジションのセクションをご覧ください。

UI ツリーでは、修飾子をレイアウト ノードのラッパーノードとして視覚化できます。

コンポーザブルと修飾子のコード、および UI ツリーとしての視覚的表現。
図 1.UI ツリーでレイアウト ノードをラップする修飾子。

コンポーザブルに複数の修飾子を追加すると、修飾子の連鎖が作成されます。複数の修飾子を連鎖させると、各修飾子ノードは連鎖の残りの部分とレイアウト ノードを内部でラップします。 たとえば、clip 修飾子と size 修飾子を連鎖させると、clip 修飾子ノードが size 修飾子ノードをラップし、 その size 修飾子ノードが Image レイアウト ノードをラップします。

レイアウト フェーズでは、ツリーを走査するアルゴリズムは変わりませんが、 各修飾子ノードも訪問されます。これにより、修飾子は、ラップする修飾子ノードまたはレイアウト ノードのサイズ要件と配置を変更できます。

図 2 に示すように、Image コンポーザブルと Text コンポーザブルの実装自体は、単一のレイアウト ノードをラップする修飾子の連鎖で構成されています。

RowColumn の実装は、子をどのようにレイアウトするかを記述するレイアウト ノードです。

以前のツリー構造ですが、各ノードはシンプルなレイアウトで、その周りに多くの修飾子ノードが配置されています。
図 2.図 1 と同じツリー構造ですが、UI ツリー内のコンポーザブルが修飾子の連鎖として視覚化されています。

まとめ

  • 修飾子は、単一の修飾子ノードまたはレイアウト ノードをラップします。
  • レイアウト ノードは、複数の子ノードをレイアウトできます。

以降のセクションでは、このメンタルモデルを使用して、修飾子の連鎖と、それがコンポーザブルのサイズに及ぼす影響について説明します。

レイアウト フェーズの制約

レイアウト フェーズでは、3 ステップのアルゴリズムに従って、各レイアウト ノードの幅、高さ、x 座標、y 座標を決定します。

  1. 子を測定する: ノードは子(存在する場合)を測定します。
  2. 自身のサイズを決定する: ノードは、測定結果に基づいて自身の サイズを決定します。
  3. 子を配置する: 各子ノードは、ノード自身の 位置を基準にして配置されます。

Constraints は、アルゴリズムの最初の 2 つのステップでノードの適切なサイズを決定するのに役立ちます。制約は、ノードの幅と高さの最小境界と最大境界を定義します。ノードがサイズを決定するとき、測定されたサイズはこのサイズ範囲内に収まる必要があります。

制約の種類

制約は次のいずれかになります。

  • 境界付き: ノードには、幅と高さの最大値と最小値があります。
コンテナ内のさまざまなサイズの境界付き制約。
図 3.境界付きの制約。
  • 境界なし: ノードはサイズに制約されません。幅と高さの最大境界は無限大に設定されます。
幅と高さが無限に設定されている制約のない制約。制約がコンテナの範囲を超えている。
図 4.境界なしの制約。
  • 正確: ノードは正確なサイズ要件に従うように求められます。最小境界と最大境界は同じ値に設定されます。
コンテナ内の正確なサイズ要件に準拠する正確な制約。
図 5.正確な制約。
  • 組み合わせ: ノードは、上記の制約 タイプの組み合わせに従います。たとえば、制約によって幅を制限しながら、高さの最大値を無制限にしたり、幅を正確に設定しながら、高さを制限したりできます。
境界付き制約と境界なし制約、正確な幅と高さの組み合わせを示す 2 つのコンテナ。
図 6.境界付きの制約と境界なしの制約、正確な幅と高さの組み合わせ。

次のセクションでは、これらの制約が親から子にどのように渡されるかについて説明します。

制約が親から子に渡される仕組み

レイアウト フェーズの制約で説明したアルゴリズムの最初のステップでは、制約は UI ツリー内で親から子に渡されます。

親ノードが子を測定するとき、これらの制約を各子に提供して、許容されるサイズを知らせます。次に、自身のサイズを決定するとき、自身の親から渡された制約にも従います。

アルゴリズムの概要は次のとおりです。

  1. 実際に占有するサイズを決定するために、UI ツリーのルートノードは子を測定し、同じ制約を最初の子に転送します。
  2. 子が測定に影響しない修飾子である場合、制約を次の修飾子に転送します。測定に影響する修飾子に到達するまで、制約は修飾子の連鎖にそのまま渡されます。その後、制約は適宜サイズ変更されます。
  3. 子を持たないノード(「リーフノード」と呼ばれる)に到達すると、渡された制約に基づいてサイズを決定し、この解決されたサイズを親に返します。
  4. 親は、この子の測定に基づいて制約を調整し、調整された制約を使用して次の子を呼び出します。
  5. 親のすべての子が測定されると、親ノードは自身のサイズを決定し、それを自身の親に伝えます。
  6. このようにして、ツリー全体が深さ優先で走査されます。最終的に、すべてのノードがサイズを決定し、測定ステップが完了します。

詳細な例については、制約と修飾子の順序 動画をご覧ください。

制約に影響する修飾子

前のセクションで説明したように、一部の修飾子は制約のサイズに影響する可能性があります。以降のセクションでは、制約に影響する特定の修飾子について説明します。

size 修飾子

size 修飾子は、コンテンツの推奨サイズを宣言します。

たとえば、次の UI ツリーは 300dp x 200dp のコンテナにレンダリングされます。制約は境界付きで、幅は 100dp300dp、高さは 100dp200dp の範囲で指定できます。

サイズ修飾子がレイアウト ノードをラップしている UI ツリーの一部と、コンテナ内のサイズ修飾子によって設定された境界付き制約の表現。
図 7.UI ツリー内の境界付きの制約と、コンテナでの表現。

size 修飾子は、渡された値に合わせて受信制約を調整します。この例では、値は 150dp です。

図 7 と同じですが、サイズ修飾子が渡された値と一致するように、受信した制約を調整します。
図 8.size 修飾子が制約を 150dp に調整しています。

幅と高さが最小制約境界より小さい場合、または最大制約境界より大きい場合、修飾子は渡された制約にできるだけ一致させながら、渡された制約に従います。

2 つの UI ツリーと、コンテナ内の対応する表現。1 つ目の例では、サイズ修飾子は入力制約を受け入れます。2 つ目の例では、サイズ修飾子は大きすぎる制約にできるだけ近づくように調整され、コンテナを埋める制約になります。
図 9.size 修飾子は、渡された制約にできるだけ準拠しています。

複数の size 修飾子を連鎖させても機能しません。最初の size 修飾子は、最小制約と最大制約の両方を固定値に設定します。2 番目のサイズ修飾子が小さいサイズまたは大きいサイズをリクエストしても、渡された正確な境界に従う必要があるため、これらの値はオーバーライドされません。

UI ツリー内の 2 つのサイズ変更子のチェーンと、コンテナ内のその表現。これは、渡された最初の値の結果であり、2 番目の値の結果ではありません。
図 10.2 つの size 修飾子の連鎖。2 番目に渡された値(50dp)は最初の値(100dp)をオーバーライドしません。

requiredSize 修飾子

ノードで受信制約をオーバーライドする必要がある場合は、size の代わりに requiredSize 修飾子を使用します。requiredSize 修飾子は、受信制約を置き換え、指定したサイズを正確な境界として渡します。

サイズがツリーに返されると、子ノードは使用可能なスペースの中央に配置されます。

UI ツリーでチェーンされた size 修飾子と requiredSize 修飾子、およびコンテナ内の対応する表現。requiredSize 修飾子の制約は、サイズ修飾子の制約をオーバーライドします。
図 11.requiredSize 修飾子が size 修飾子からの受信制約をオーバーライドしています。

width 修飾子と height 修飾子

size 修飾子は、制約の幅と高さの両方を調整します。width 修飾子を使用すると、幅を固定しながら高さを未決定のままにできます。同様に、height 修飾子を使用すると、高さを固定しながら幅を未決定のままにできます。

2 つの UI ツリー。1 つは幅修飾子とそのコンテナ表現、もう 1 つは高さ修飾子とその表現。
図 12.width 修飾子と height 修飾子が、それぞれ固定の幅と高さを設定しています。

sizeIn 修飾子

sizeIn 修飾子を使用すると、幅と高さの正確な最小制約と最大制約を設定できます。制約を細かく制御する必要がある場合は、sizeIn 修飾子を使用します。

最小幅と最大幅、最小高さと最大高さが設定された sizeIn 修飾子を含む UI ツリーと、コンテナ内のその表現。
図 13.minWidthmaxWidthminHeightmaxHeight が設定された sizeIn 修飾子。

このセクションでは、連鎖した修飾子を含むいくつかのコード スニペットの出力を示して説明します。

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

このスニペットは次の出力を生成します。

親コンテナいっぱいに表示された青い正方形。
図 14.修飾子の連鎖の結果、Image が最大サイズに拡大されます。
  • fillMaxSize 修飾子は、最小幅と最小高さを最大値(300dp の幅、200dp の高さ)に設定するように制約を変更します。
  • size 修飾子は 50dp のサイズを使用しようとしますが、受信する最小制約に従う必要があります。そのため、size 修飾子は 300 x 200 の正確な制約境界も出力し、size 修飾子で指定された値を事実上無視します。
  • Image はこれらの境界に従い、300 x 200 のサイズを報告します。このサイズはツリー全体に渡されます。

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

このスニペットは次の出力を生成します。

親コンテナの中央に配置された小さな青い正方形。
図 15.Image は中央に配置され、サイズは 50dp に設定されます。
  • fillMaxSize 修飾子は、最小幅と最小高さを最大値(幅 300dp、高さ 200dp)に設定するように制約を調整します。
  • wrapContentSize 修飾子は、最小制約をリセットします。したがって、fillMaxSize では制約が固定されますが、wrapContentSize では境界付きの制約にリセットされます。 次のノードは、再びスペース全体を占有することも、スペース全体より小さくすることもできます。
  • size 修飾子は、制約を 50 の最小境界と最大境界に設定します。
  • Image50 x 50 のサイズに解決され、size 修飾子がそれを転送します。
  • wrapContentSize 修飾子には特別なプロパティがあります。子を受け取り、渡された使用可能な最小境界の中央に配置します。 したがって、親に伝えるサイズは、渡された最小境界と同じになります。

3 つの修飾子を組み合わせるだけで、コンポーザブルのサイズを定義し、親の中央に配置できます。

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

このスニペットは次の出力を生成します。

修飾子の順序が原因で正しくクリップされていない円形。
図 16.修飾子の順序が原因で、形状が正しくクリップされていません。
  • clip 修飾子は制約を変更しません。
  • padding 修飾子は最大制約を減らします。
  • size 修飾子は、すべての制約を 100dp に設定します。
  • Image はこれらの制約に従い、100dp x 100dp のサイズを報告します。
  • padding 修飾子は、Image によって報告されたサイズにすべての辺で 10dp を追加するため、パディングを含むレイアウトの幅と高さは 120dp になります。
  • 描画フェーズでは、clip 修飾子は 120dp x 120dp のキャンバスで動作します。そのサイズの円形マスクを作成します。
  • 次に、padding 修飾子はコンテンツをすべての辺で 10dp インセットするため、Image のキャンバス サイズは 100dp x 100dp になります。
  • Image はその小さいキャンバスに描画されます。画像は元の 120dp の円に基づいてクリップされるため、出力は円形になりません。