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 Examples プロジェクトをご覧ください。

制約の概要

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

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

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

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

図 2. ビュー C がビュー A の下に垂直方向に制約されました。

制約が欠落していても、コンパイル エラーは発生しませんが、Layout Editor のツールバーに、欠落している制約がエラーとして表示されます。エラーとその他の警告を表示するには、[Show Warnings and errors] をクリックします。制約の欠落を回避するために、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. レイアウト ファイルの名前を入力し、[ルートタグ] に「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. ガイドラインの制約を受けたビュー。

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

ガイドラインと同様に、バリアはビューを制約できる非表示の線です。ただし、バリアはそれ自身の位置を定義しません。バリアの位置は、バリア内に含まれるビューの位置に基づいて移動します。これは、ビューを特定の 1 つのビューではなく、一連のビューに制限する場合に便利です。

たとえば、図 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 に示すようにビューをドラッグします。

代わりに、制約を満たすためにビューのサイズを引き伸ばす場合は、サイズを切り替えて [match constraints](制約に合わせる)に切り替えます。

動画 3. 制約バイアスの調整。

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

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

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

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

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

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

  • Match Constraints: ビューは、各辺の制約を満たすように、ビューのマージンを考慮して可能な限り拡大されます。ただし、次の属性と値を使用することで、この動作を変更できます。これらの属性は、ビューの幅を [match constraints] に設定した場合のみ有効になります。
    • layout_constraintWidth_min

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

    • layout_constraintWidth_max

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

    ただし、指定されたディメンションに制約が 1 つしかない場合、ビューはコンテンツに合わせて拡大されます。このモードを高さまたは幅に使用すると、サイズ比率を設定できます。

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

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

少なくとも 1 つのビュー ディメンションが「制約に一致」(0dp)に設定されている場合、ビューサイズを 16:9 などの比率に設定できます。この比率を有効にするには、[Toggle Aspect Ratio Constraint](図 14 の図番号 1)をクリックして、表示される入力に width:height 比率を入力します。

幅と高さの両方が「制約に合わせる」に設定されている場合、[アスペクト比の制約を切り替え] をクリックすると、一方のアスペクト比に基づいてどちらの寸法を使用するかを選択できます。ビュー インスペクタでは、対応するエッジを実線で結んで、どのディメンションが比率に設定されているかが示されます。

たとえば、両側を「制約に合わせる」に設定した場合は、[アスペクト比の制約を切り替え] を 2 回クリックすると、幅が高さの比率に設定されます。全体のサイズはビューの高さによって決まります。ビューの高さは自由に定義できます(図 15 を参照)。

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

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

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

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

このツールで提供されるマージンはすべて 8 dp の係数であり、ビューをマテリアル デザインの 8 dp 正方形のグリッドの推奨事項に合わせて調整します。

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

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

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

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

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

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

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

チェーンを作成するには、動画 4 に示すように次の操作を行います。

  1. チェーンに含めるすべてのビューを選択します。
  2. いずれかのビューを右クリックします。
  3. [チェーン] を選択します。
  4. [Center Horizontally(水平方向に中央揃え)] または [Center Vertically(垂直方向に中央揃え)] を選択します。

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

チェーンを使用する際は、以下の点を考慮してください。

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

制約を自動的に設定する

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

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

[Autoconnect to Parent](親への自動接続)は、有効にできる別の機能です。この機能が有効になっていて、子ビューを親に追加すると、レイアウトに追加するときに、ビューごとに 2 つ以上の制約が自動的に作成されます。ただし、ビューを親レイアウトに制約することが適切な場合に限られます。Autoconnect は、レイアウト内の他のビューに対する制約を作成しません。

自動接続はデフォルトで無効です。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>

参考情報

ConstraintLayoutSunflower デモアプリで使用されています。