ConstraintLayout を使用してレスポンシブ UI を作成する Android Jetpack の一部。

Compose を試す
Jetpack Compose は Android の推奨 UI ツールキットです。Compose でレイアウトを操作する方法を学習します。

ConstraintLayout を使用すると、ネストされたビューグループではなく、フラットなビュー階層を持つ、大規模で複雑なレイアウトを作成できます。兄弟ビューと親レイアウトの関係に従ってすべてのビューがレイアウトされるという点で RelativeLayout と似ていますが、RelativeLayout よりも柔軟性が高く、Android Studio の Layout Editor で簡単に使用できます。

Layout API と Layout Editor は相互に特別に構築されているため、ConstraintLayout のすべての機能を Layout Editor のビジュアル ツールから直接利用できます。XML を編集する代わりに、ドラッグして ConstraintLayout 全体を使用してレイアウトを作成できます。

このページでは、Android Studio 3.0 以上で ConstraintLayout を使用してレイアウトを作成する方法について説明します。Layout Editor の詳細については、Layout Editor を使用して UI を作成するをご覧ください。

ConstraintLayout で作成できるさまざまなレイアウトを確認するには、GitHub にある Constraint Layout のサンプル プロジェクトをご覧ください。

制約の概要

ConstraintLayout でビューの位置を定義するには、ビューに水平方向と垂直方向の制約をそれぞれ 1 つ以上追加します。各制約は、別のビュー、親レイアウト、非表示のガイドラインとの接続または配置を表します。各制約は、垂直軸または水平軸に沿ったビューの位置を定義します。各ビューには各軸ごとに少なくとも 1 つの制約が必要ですが、多くの場合、それ以上にする必要があります。

ビューを Layout Editor にドロップすると、制約がなくても、そのまま残ります。これは編集を容易にするためのものです。デバイスでレイアウトを実行するときにビューに制約がない場合、ビューは位置 [0,0](左上隅)に描画されます。

図 1 では、レイアウトはエディタでは適切に表示されますが、ビュー C には垂直方向の制約がありません。このレイアウトがデバイス上に描画されると、ビュー C はビュー A の左右端に水平方向に揃えられますが、垂直方向の制約がないため、画面の最上部に表示されます。

図 1. エディタでは A の下にビュー C が表示されますが、垂直方向の制約はありません。

図 2. ビュー C はビュー A の下に垂直方向の制約を受けます。

制約が欠落していてもコンパイル エラーは発生しませんが、Layout Editor により、制約の欠落がツールバーにエラーとして表示されます。エラーやその他の警告を表示するには、[警告とエラーを表示] をクリックします。制約の欠落を避けるため、Layout Editor の自動接続と制約の推定機能により、制約が自動的に追加されます。

ConstraintLayout をプロジェクトに追加する

プロジェクトで ConstraintLayout を使用する手順は次のとおりです。

  1. settings.gradle ファイルで maven.google.com リポジトリが宣言されていることを確認します。

    Groovy

        dependencyResolutionManagement {
          ...
          repositories {
              google()
          }
        )
        

    Kotlin

        dependencyResolutionManagement {
          ...
          repositories {
              google()
          }
        }
        
  2. 次の例に示すように、モジュール レベルの build.gradle ファイルにライブラリを依存関係として追加します。最新バージョンは、例に示されているものと異なる場合があります。

    Groovy

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha13"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha13")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13")
    }
    
  3. ツールバーまたは同期通知で、[Sync Project with Gradle Files] をクリックします。

これで、ConstraintLayout を使ってレイアウトを作成する準備が整いました。

レイアウトを変換する

図 3. レイアウトを ConstraintLayout に変換するためのメニュー。

既存のレイアウトを制約レイアウトに変換する手順は次のとおりです。

  1. Android Studio でレイアウトを開き、エディタ ウィンドウの下部にある [Design] タブをクリックします。
  2. [Component Tree] ウィンドウで、レイアウトを右クリックし、[Convert LinearLayout to ConstraintLayout] をクリックします。

新しいレイアウトの作成

新しい制約レイアウト ファイルを作成する手順は次のとおりです。

  1. [Project] ウィンドウでモジュール フォルダをクリックし、[File] > [New] > [XML] > [Layout XML] を選択します。
  2. レイアウト ファイルの名前を入力し、[Root Tag] に「androidx.constraintlayout.widget.ConstraintLayout」と入力します。
  3. [Finish] をクリックします。

制約を追加または削除する

制約を追加する手順は次のとおりです。

動画 1. ビューの左側は親の左側に制約されます。

  1. ビューを [Palette] ウィンドウからエディタにドラッグします。

    ConstraintLayout にビューを追加すると、各隅に正方形のサイズ変更ハンドル、両側に円形の制約ハンドルがある境界ボックスに表示されます。

  2. ビューをクリックして選択します。
  3. 次のいずれかを行います。
    • 制約ハンドルをクリックして、使用可能なアンカー ポイントにドラッグします。このポイントは、別のビューの端、レイアウトの端、ガイドラインのいずれかです。制約ハンドルをドラッグすると、Layout Editor に、接続アンカー候補と青いオーバーレイが表示されます。
    • 図 4 に示すように、[Attributes] ウィンドウの [Layout] セクションにある [Create a connection] ボタン をクリックします。

      図 4. [Attributes] ウィンドウの [Layout] セクションでは、接続を作成できます。

制約を作成すると、エディタにより、2 つのビューを分離するためのデフォルトのマージンが設定されます。

制約を設定する際は、以下のルールを念頭に置いてください。

  • 各ビューには、少なくとも 2 つの制約(水平方向と垂直方向)が必要です。
  • 制約は、同じ平面を共有する制約ハンドルとアンカー ポイントの間でのみ作成できます。ビューの垂直面(左側と右側)は、別の垂直面にのみ制約でき、ベースラインは他のベースラインにのみ制約できます。
  • 各制約ハンドルを 1 つの制約に使用できますが、異なるビューから同じアンカー ポイントに対して複数の制約を作成することもできます。

制約を削除するには、次のいずれかを行います。

  • 制約をクリックして選択し、[削除] をクリックします。
  • 制約アンカーを Ctrl-クリック(macOS では Command-クリック)します。図 5 に示すように、制約が赤色に変わり、クリックすると削除できることを示します。

    図 5. 赤色の制約は、クリックして削除できることを示しています。

  • 図 6 に示すように、[Attributes] ウィンドウの [Layout] セクションで、制約アンカーをクリックします。

    図 6. 制約アンカーをクリックして削除します。

動画 2. 既存の制約に対抗する制約を追加する。

ビューに反対側の制約を追加すると、動画 2 に示すように、制約線がバネのように巻き付けられ、反対側の力を示します。ビューサイズが「固定」または「コンテンツをラップ」に設定されている場合、ビューが制約の中央に配置されると、その効果が最も顕著になります。制約に合わせてビューのサイズを伸縮する場合は、「制約に合致」にサイズを切り替えます。現在のサイズを維持したままビューを中央に配置しないように移動する場合は、制約のバイアスを調整します。

次のセクションで説明するように、制約を使用してさまざまなタイプのレイアウト動作を実現できます。

親の位置

ビューの端をレイアウトの対応する端に制約します。

図 7 では、ビューの左側が親レイアウトの左端に接続されています。余白を使用して端からの距離を定義できます。

図 7. 親に対する水平方向の制約。

順序

2 つのビュー(垂直方向または水平方向)の表示順序を定義します。

図 8 では、B は常に A の右側になるように制約され、C は A の下に制約されています。ただし、これらの制約は位置揃えを意味しないため、B は上下に移動できます。

図 8. 水平方向と垂直方向の制約。

配置

ビューの外辺を別のビューの同じ側の外辺と揃えます。

図 9 では、B の左側と A の左側が揃えられています。ビューの中心を揃えるには、両側に制約を作成します。

位置揃えをオフセットするには、制約から内側にビューをドラッグします。たとえば、図 10 は 24 dp のオフセット位置に配置した B を示しています。このオフセットは、制約されたビューのマージンによって定義されます。

配置するビューをすべて選択して、ツールバーの配置アイコン をクリックして、配置タイプを選択します。

図 9. 水平方向の配置制約。

図 10. 水平方向のオフセット制約。

ベースラインの位置揃え

ビューのテキストのベースラインを別のビューのテキストのベースラインと揃えます。

図 11 では、B の 1 行目と A のテキストの位置を揃えています。

ベースライン制約を作成するには、制約するテキストビューを右クリックし、[Show Baseline] をクリックします。次に、テキスト ベースラインをクリックし、線を別のベースラインにドラッグします。

図 11. ベースラインのアライメント制約。

ガイドラインに対する制約を設定する

垂直または水平のガイドラインを追加して、ビューに制約をかけ、アプリのユーザーには表示されません。dp 単位またはレイアウトの端からのパーセンテージに基づいて、レイアウト内にガイドラインを配置できます。

ガイドラインを作成するには、ツールバーのガイドライン アイコン をクリックし、[Add Vertical Guideline] または [Add Horizontal Guideline] をクリックします。

点線をドラッグして位置を変更し、ガイドラインの端にある円をクリックして測定モードを切り替えます。

図 12. ガイドラインに制限されたビュー。

バリアに対して制約を設定する

ガイドラインと同様に、バリアはビューを制約できる目に見えない線ですが、バリア自体の位置は定義されません。代わりに、バリアの位置は、その中のビューの位置に基づいて移動します。これは、ビューを特定のビューに制約するのではなく、ビューのセットに制約する場合に便利です。

たとえば、図 13 では、ビュー C がバリアの右側に固定されています。バリアは、ビュー A とビュー B の両方の「終点」(左から右へのレイアウトでは右側)に設定されます。バリアは、ビュー A またはビュー B の右側が最も右にあるかどうかに応じて移動します。

バリアを作成する手順は次のとおりです。

  1. ツールバーのガイドライン アイコン をクリックし、[Add Vertical Barrier] または [Add Horizontal Barrier] をクリックします。
  2. [Component Tree] ウィンドウで、バリア内にあるビューを選択してバリア コンポーネントにドラッグします。
  3. [Component Tree] からバリアを選択し、[Attributes] ウィンドウを開き、[barrierDirection] を設定します。

これで、別のビューからそのバリアに対して制約を設定できます。

バリアの内側にあるビューを制限することもできます。これにより、どのビューが最も長いか、または最も高いかわからない場合でも、バリア内のすべてのビューを相互に揃えることができます。

バリアの「最小」位置を確保するために、バリアの内側にガイドラインを追加することもできます。

図 13. ビュー C はバリアに制限され、ビュー A とビュー B の両方の位置とサイズに応じて移動します。

制約バイアスを調整する

ビューの両側に制約を追加し、同じディメンションのビューサイズが「固定」または「コンテンツをラップ」のいずれかである場合、ビューは 2 つの制約の中央に、デフォルトのバイアスで 50% に設定されます。バイアスを調整するには、[Attributes] ウィンドウのバイアス スライダーをドラッグするか、ビューをドラッグします(動画 3 を参照)。

制約に合わせてビューのサイズを伸縮する場合は、「制約に合致」にサイズを切り替えます。

動画 3. 制約バイアスを調整する。

ビューのサイズを調整する

図 14. ビューを選択すると、[Attributes] ウィンドウに、サイズ比率 1、制約の削除 2、高さまたは幅モード 3、マージン 4、制約バイアス 5 のコントロールが表示されます。6 個の制約リストで制約をクリックして、Layout Editor で個々の制約をハイライト表示することもできます。

角のハンドルを使用してビューのサイズを変更できますが、その場合、サイズがハードコードされます。つまり、コンテンツや画面サイズに応じてビューのサイズが変更されることはありません。別のサイズモードを選択するには、ビューをクリックして、エディタの右側にある [Attributes] ウィンドウを開きます。

[Attributes] ウィンドウの上部にはビュー インスペクタがあり、図 14 に示すように、複数のレイアウト属性のコントロールが表示されます。これは、制約レイアウト内のビューでのみ使用できます。

図 14 のコールアウト 3 で示されている記号をクリックすると、高さと幅の計算方法を変更できます。 これらの記号は、次のようにサイズモードを表します。このアイコンをクリックすると、次の設定を切り替えることができます。

  • 固定: 次のテキスト ボックスで特定のディメンションを指定するか、エディタでビューのサイズを変更します。
  • コンテンツをラップ: ビューは、そのコンテンツを収めるのに必要な分だけ拡張されます。
    • layout_constraintWidth
    • これを true に設定すると、制約に合わせて水平方向のディメンションが変更されます。デフォルトでは、WRAP_CONTENT に設定されたウィジェットは制約によって制限されません。

  • Match Constraints: ビューのマージンを考慮して、両側の制約を満たすようにビューが可能な限り拡大されます。ただし、次の属性と値でこの動作を変更できます。以下の属性は、ビューの幅を「制約に合致」させた場合にのみ有効になります。
    • layout_constraintWidth_min

      ビューの最小幅の dp サイズを指定します。

    • layout_constraintWidth_max

      ビューの最大幅の dp サイズを指定します。

    ただし、指定されたディメンションに 1 つの制約しか適用されない場合、ビューはその内容に合うように拡大されます。高さまたは幅でこのモードを使用すると、サイズ比率を設定することもできます。

サイズを比として設定する

図 15. ビューは、高さの比率に基づいた 16:9 のアスペクト比に設定されます。

少なくとも 1 つのビュー ディメンションが「制約に合わせる」(0dp)に設定されている場合、ビューサイズを 16:9 などの比率に設定できます。比率を有効にするには、[アスペクト比の制約を切り替える](図 14 の 1 の吹き出し)をクリックして、表示される入力に width:height の比率を入力します。

幅と高さの両方が「制約に合致」に設定されている場合は、[Toggle Aspect Ratio Constraint] をクリックすると、一方の比率に基づいてサイズを選択できます。ビュー インスペクタでは、対応するエッジを実線で結んで、比率として設定されているディメンションが示されます。

たとえば、両辺を「制約に合わせ」に設定した場合、[Toggle Aspect Ratio Constraint] を 2 回クリックして、幅を高さの比率に設定します。全体のサイズは、ビューの高さによって決まります。ビューの高さは、図 15 に示すように任意の方法で定義できます。

ビューのマージンを調整する

ビューの間隔を均等にするには、ツールバーのマージン アイコン をクリックして、レイアウトに追加する各ビューのデフォルトのマージンを選択します。デフォルトのマージンに加えた変更は、それ以降に追加したビューにのみ適用されます。

各制約を表す線の番号をクリックすると、[Attributes] ウィンドウの各ビューのマージンを制御できます。図 14 のコールアウト 4 では、下余白が 16 dp に設定されています。

図 16. ツールバーの [Margin] ボタン

このツールで提供されるマージンはすべて 8 dp であるため、ビューをマテリアル デザインの 8 dp 四方グリッドの推奨に合わせることができます。

チェーンを使って線形グループを管理する

図 17. 2 つのビューがある水平チェーン。

チェーンとは、双方向の位置制約を設定して相互にリンクされているビューのグループです。チェーン内のビューは、垂直方向または水平方向に分散できます。

図 18. 各チェーン スタイルの例。

チェーンには、次のいずれかの方法でスタイルを設定できます。

  1. Spread: マージンを考慮して、ビューは均等に分割されます。これがデフォルトです。
  2. 内部に分散: 最初と最後のビューにはチェーンの両端の制約が適用され、残りは均等に分散されます。
  3. Weighted: チェーンが [spread] または [spread 内部] に設定されている場合、1 つ以上のビューを「制約に合致」(0dp)に設定すると、残りのスペースを埋めることができます。デフォルトでは、「制約に合致」に設定された各ビューにスペースが均等に配分されますが、layout_constraintHorizontal_weight 属性と layout_constraintVertical_weight 属性を使用して、各ビューに重要度の重み付けを割り当てることができます。 これは、線形レイアウトlayout_weight と同じように機能します。つまり、最も高いウェイト値を持つビューが最も多くのスペースを取得し、同じウェイトを持つビューは同じスペース量を使用します。
  4. パック済み: マージンを考慮してビューがパックされます。チェーンの「ヘッド」ビューのバイアスを変更することで、チェーン全体のバイアス(左右または上下)を調整できます。

チェーンの「ヘッド」ビュー(水平チェーンの左端のビュー(左から右のレイアウト)と、垂直チェーンの最上位のビュー)は、XML のチェーンのスタイルを定義します。チェーン内の任意のビューを選択し、ビューの下に表示されるチェーンボタン をクリックすることで、スプレッド、内部スプレッド、パックを切り替えることができます。

チェーンを作成する手順は次のとおりです(動画 4 を参照)。

  1. チェーンに含めるビューをすべて選択します。
  2. いずれかのビューを右クリックします。
  3. [チェーン] を選択します。
  4. [水平方向に配置] または [垂直方向に配置] を選択します。

動画 4. 水平方向のチェーンを作成する。

チェーンを使用する際は、次の点に注意してください。

  • ビューを水平方向と垂直方向の両方のチェーンに含めることができるため、柔軟なグリッド レイアウトを構築できます。
  • 図 14 に示すように、チェーンの両端が同じ軸上の別のオブジェクトに制約されている場合にのみ、チェーンは適切に機能します。
  • チェーンの向きは垂直または水平ですが、これを使用してもビューはその方向に揃えられません。チェーン内の各ビューを適切に配置するには、アライメントの制約など、他の制約を含めます。

制約を自動的に設定する

レイアウト内に配置する際にすべてのビューに制約を追加するのではなく、各ビューを Layout Editor 内の目的の位置に移動し、[Infer Constraints] をクリックして制約を自動的に作成できます。

[制約の推定] は、レイアウトをスキャンし、すべてのビューに対して最も効果的な制約のセットを決定します。これにより、柔軟性を実現しつつ、ビューを現在の位置に制限できます。さまざまな画面サイズと画面の向きに合わせてレイアウトが反応するように、調整が必要になる場合があります。

[保護者に自動接続する] は、有効にできる独立した機能です。この機能を有効にすると、子ビューを親に追加すると、レイアウトに追加するときに各ビューに 2 つ以上の制約が自動的に作成されますが、これはビューを親レイアウトに制約するのが適切である場合に限られます。自動接続によって、レイアウト内の他のビューに対する制約が生じることはありません。

自動接続はデフォルトで無効です。有効にするには、Layout Editor ツールバーの [Enable Autoconnection to Parent] をクリックします。

キーフレーム アニメーション

ConstraintLayout 内では、ConstraintSetTransitionManager を使用して、要素のサイズと位置の変化をアニメーション化できます。

ConstraintSet は、ConstraintLayout 内のすべての子要素の制約、マージン、パディングを表す軽量のオブジェクトです。表示された ConstraintLayoutConstraintSet を適用すると、レイアウトはそのすべての子の制約を更新します。

ConstraintSet を使用してアニメーションを作成するには、アニメーションの開始キーと終了キーフレームとして機能する 2 つのレイアウト ファイルを指定します。次に、2 番目のキーフレーム ファイルから ConstraintSet を読み込み、表示される ConstraintLayout に適用します。

次のコード例は、1 つのボタンを画面の下部に移動することをアニメーション化する方法を示しています。

// MainActivity.kt

fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.keyframe_one)
    constraintLayout = findViewById(R.id.constraint_layout) // member variable
}

fun animateToKeyframeTwo() {
    val constraintSet = ConstraintSet()
    constraintSet.load(this, R.layout.keyframe_two)
    TransitionManager.beginDelayedTransition()
    constraintSet.applyTo(constraintLayout)
}
// layout/keyframe1.xml
// Keyframe 1 contains the starting position for all elements in the animation
// as well as final colors and text sizes.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
// layout/keyframe2.xml
// Keyframe 2 contains another ConstraintLayout with the final positions.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

参考情報

ConstraintLayout は、Sunflower デモアプリで使用されています。