ConstraintLayout でレスポンシブ UI を作成する Android Jetpack の一部
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 の制約レイアウトのサンプル プロジェクトをご覧ください。
制約の概要
ConstraintLayout
でビューの位置を定義するには、ビューに水平方向と垂直方向の制約をそれぞれ 1 つ以上追加します。各制約は、別のビュー、親レイアウト、非表示のガイドラインへの接続または配置を表します。各制約は、垂直軸または水平軸に沿ったビューの位置を定義します。各ビューでは、軸ごとに少なくとも 1 つの制約が必要ですが、多くの場合、より多くの制約が必要になります。
ビューを Layout Editor にドロップすると、制約がなくても、そのまま残ります。これは編集を容易にするための機能です。デバイスでレイアウトを実行するときにビューに制約がない場合は、位置 [0,0](左上隅)に描画されます。
図 1 では、エディタではレイアウトに問題はありませんが、ビュー C に垂直方向の制約はありません。このレイアウトをデバイス上に描画すると、ビュー C はビュー A の左右端に水平方向に位置揃えされます。ただし、垂直方向の制約がないため、ビュー C は画面の上部に表示されます。
制約が欠落していてもコンパイル エラーは発生しませんが、Layout Editor のツールバーに、欠落している制約がエラーとして表示されます。エラーやその他の警告を表示するには、[警告とエラーを表示] をクリックします。制約の欠落を避けるために、Layout Editor の自動接続と制約の推測機能を使用すると、制約が自動的に追加されます。
ConstraintLayout をプロジェクトに追加する
プロジェクトで ConstraintLayout
を使用する手順は次のとおりです。
settings.gradle
ファイルでmaven.google.com
リポジトリが宣言されていることを確認します。Groovy
dependencyResolutionManagement { ... repositories { google() } )
Kotlin
dependencyResolutionManagement { ... repositories { google() } }
- 次の例に示すように、モジュール レベルの
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") }
- ツールバーまたは同期通知で、[Sync Project with Gradle Files] をクリックします。
これで、ConstraintLayout
を使ってレイアウトを作成する準備が整いました。
レイアウトを変換する
既存のレイアウトを制約レイアウトに変換する手順は次のとおりです。
- Android Studio でレイアウトを開き、エディタ ウィンドウの下部にある [Design] タブをクリックします。
- [Component Tree] ウィンドウでレイアウトを右クリックし、[Convert LinearLayout to ConstraintLayout] をクリックします。
新しいレイアウトの作成
新しい制約レイアウト ファイルを作成する手順は次のとおりです。
- [Project] ウィンドウでモジュール フォルダをクリックし、[File] > [New] > [XML] > [Layout XML] を選択します。
- レイアウト ファイルの名前を入力し、[Root Tag] に「androidx.constraintlayout.widget.ConstraintLayout」と入力します。
- [Finish] をクリックします。
制約を追加または削除する
制約を追加する手順は次のとおりです。
ビューを [Palette] ウィンドウからエディタにドラッグします。
ConstraintLayout
にビューを追加すると、四隅に正方形のサイズ変更ハンドルがあり、両側に円形の制約ハンドルがある境界ボックスに表示されます。- ビューをクリックして選択します。
- 次のいずれかを行います。
- 制約ハンドルをクリックして、使用可能なアンカー ポイントにドラッグします。このポイントは、別のビューの端、レイアウトの端、ガイドラインのいずれかになります。制約ハンドルをドラッグすると、Layout Editor には接続アンカーの候補と青色のオーバーレイが表示されます。
図 4 に示すように、[Attributes] ウィンドウの [Layout] セクションで、[Create a connection] ボタン をクリックします。
制約が作成されると、エディタによりデフォルトのマージンが設定され、2 つのビューが分離されます。
制約を設定する際は、以下のルールを念頭に置いてください。
- すべてのビューには、少なくとも 2 つの制約(水平方向と垂直方向)が必要です。
- 制約を作成できるのは、同じ平面を共有する制約ハンドルとアンカー ポイントの間のみです。ビューの垂直面(左側と右側)は、別の垂直面にのみ制約でき、ベースラインは他のベースラインのみに制約できます。
- 各制約ハンドルは 1 つの制約にしか使用できませんが、異なるビューから同じアンカー ポイントに対して複数の制約を作成できます。
制約を削除するには、次のいずれかを行います。
- 制約をクリックして選択し、[削除] をクリックします。
制約アンカーを Ctrl キーを押しながらクリック(macOS では Command クリック)します。図 5 に示すように、制約が赤色に変わり、クリックして削除できることを示します。
図 6 に示すように、[Attributes] ウィンドウの [Layout] セクションで、制約アンカーをクリックします。
ビューに相反する制約を追加すると、動画 2 に示すように、制約線がバネのように巻き付けられ、反対の力を示します。ビューサイズが「fixed」または「wrap content」に設定されている場合に、その効果が最も顕著になります。この場合、ビューは制約間の中央に配置されます。制約に合わせてビューのサイズを拡大する場合は、サイズを切り替えて「制約に合致」させます。現在のサイズを維持したまま、ビューが中央に揃わないように移動する場合は、制約バイアスを調整します。
次のセクションで説明するように、制約を使用してさまざまなタイプのレイアウト動作を実現できます。
親の位置
ビューの側面をレイアウトの対応する端に制限します。
図 7 では、ビューの左側が親レイアウトの左端に接続されています。余白を使用して端からの距離を定義できます。
順序
2 つのビュー(垂直方向または水平方向)の表示順序を定義します。
図 8 では、B は常に A の右側に制約され、C は A の下に制約されています。ただし、これらの制約はアライメントを意味しないため、B は引き続き上下に移動できます。
配置
ビューの外辺を別のビューの同じ側の外辺と揃えます。
図 9 では、B の左側と A の左側が一致しています。ビューの中心を揃えたい場合は、両側に制約を作成します。
制約から内側にビューをドラッグすることで、位置揃えをオフセットできます。たとえば図 10 は、オフセットを 24 dp にした場合の B を示しています。このオフセットは、制約されたビューのマージンによって定義されます。
配置するすべてのビューを選択し、ツールバーの配置アイコン をクリックして配置の種類を選択することもできます。
ベースラインの位置揃え
ビューのテキストのベースラインを別のビューのテキストのベースラインと揃えます。
図 11 では、B の 1 行目と A のテキストの位置を揃えています。
ベースライン制約を作成するには、制約するテキストビューを右クリックし、[Show Baseline] をクリックします。次に、テキストのベースラインをクリックして、線を別のベースラインにドラッグします。
ガイドラインに対する制約を設定する
垂直または水平のガイドラインを追加すると、ビューを制限でき、アプリのユーザーには表示されません。ガイドラインは、dp 単位またはレイアウトの端に対する割合に基づいてレイアウト内に配置できます。
ガイドラインを作成するには、ツールバーのガイドライン アイコン をクリックし、[Add Vertical Guideline] または [Add Horizontal Guideline] をクリックします。
点線をドラッグして位置を変更し、ガイドラインの端にある円をクリックして測定モードを切り替えます。
バリアに対して制約を設定する
ガイドラインと同様に、バリアはビューを制約できる非表示の線ですが、バリア自体の位置は定義されません。代わりに、バリアの位置はそこに含まれるビューの位置に基づいて移動します。これは、ビューを特定の 1 つのビューではなく、一連のビューに制限する場合に役立ちます。
たとえば、図 13 のビュー C はバリアの右側に固定されています。バリアは、ビュー A とビュー B の両方の「終点」(左から右へのレイアウトでは右側)に設定されます。ビュー A の右側とビュー B の右側のどちらが右端にあるかによって、バリアが移動します。
バリアを作成する手順は次のとおりです。
- ツールバーのガイドライン アイコン をクリックし、[Add Vertical Barrier] または [Add Horizontal Barrier] をクリックします。
- [Component Tree] ウィンドウで、バリア内に配置するビューを選択し、バリア コンポーネントにドラッグします。
- [Component Tree] からバリアを選択し、[Attributes] ウィンドウを開いて、[barrierDirection] を設定します。
これで、別のビューからそのバリアに対して制約を設定できます。
バリアの内側にあるビューをバリアに制約することもできます。これにより、最も長いビューや最も高いビューがわからない場合でも、バリア内のすべてのビューを相互に整列させることができます。
また、バリアの「最小」位置を確保するために、バリアの内側にガイドラインを追加することもできます。
制約バイアスを調整する
ビューの両側に制約を追加し、同じディメンションのビューサイズが「固定」または「コンテンツをラップ」のいずれかである場合、ビューは 2 つの制約の中央に配置され、デフォルトでバイアスが 50% に設定されます。バイアスを調整するには、[Attributes] ウィンドウのバイアス スライダーをドラッグするか、動画 3 に示すようにビューをドラッグします。
制約に合わせてビューのサイズを拡大する場合は、制約に合わせてサイズを切り替えます。
ビューのサイズを調整する
角のハンドルを使用してビューのサイズを変更できますが、この場合、サイズがハードコードされます。つまり、コンテンツや画面サイズに応じてビューがサイズ変更されることはありません。別のサイズモードを選択するには、ビューをクリックして、エディタの右側にある [Attributes] ウィンドウを開きます。
[Attributes] ウィンドウの上部にあるビュー インスペクタには、図 14 に示すように、複数のレイアウト属性のコントロールが表示されます。これは、制約レイアウト内のビューでのみ使用できます。
図 14 のコールアウト 3 で示されている記号をクリックすると、高さと幅の計算方法を変更できます。これらの記号は、次のようにサイズモードを表します。アイコンをクリックすると、次の設定を切り替えられます。
- 固定: 次のテキスト ボックスで特定のディメンションを指定するか、エディタでビューのサイズを変更します。
- コンテンツをラップ: ビューは、コンテンツを収めるために必要な数だけ拡大します。
- layout_constraintWidth(レイアウトの制約付き幅)
-
Match Constraints: ビューのマージンを考慮したうえで、両側の制約を満たすためにビューが可能な限り拡張されます。ただし、次の属性と値を使用して、この動作を変更できます。これらの属性は、ビューの幅を「制約に合致」に設定した場合のみ有効になります。
- layout_constraintWidth_min
ビューの最小幅の
dp
サイズを指定します。 - layout_constraintWidth_max
ビューの最大幅の
dp
サイズを指定します。
ただし、指定されたディメンションに 1 つの制約しかない場合、ビューはそのコンテンツに合わせて拡大されます。このモードを高さまたは幅に対して使用すると、サイズ比率を設定することもできます。
- layout_constraintWidth_min
制約に合わせて水平方向の寸法を変更するには、これを true
に設定します。デフォルトでは、WRAP_CONTENT
に設定されたウィジェットは制約によって制限されません。
サイズを比として設定する
少なくとも 1 つのビュー ディメンションが「制約に合致」(0dp
)に設定されている場合、ビューサイズを 16:9 などの比率に設定できます。比率を有効にするには、[Toggle Aspect Ratio Constraint](図 14 の 1 のコールアウト)をクリックして、表示される入力に width:height の比率を入力します。
幅と高さの両方が「制約に合致」に設定されている場合は、[Toggle Aspect Ratio Constraint] をクリックして、もう一方の比率に基づくサイズを選択できます。ビュー インスペクタでは、対応する辺を実線で接続することで、比率として設定されているディメンションが示されます。
たとえば、両端を「制約に合致」に設定した場合は、[Toggle Aspect Ratio Constraint] を 2 回クリックして、幅を高さの比率に設定します。全体のサイズはビューの高さによって決まりますが、ビューの高さは任意の方法で定義できます(図 15 を参照)。
ビューのマージンを調整する
ビューの間隔を均等にするには、ツールバーのマージン アイコン をクリックして、レイアウトに追加する各ビューのデフォルトのマージンを選択します。デフォルトのマージンに加えた変更は、それ以降に追加したビューにのみ適用されます。
[Attributes] ウィンドウで各ビューのマージンを制御するには、各制約を表す線の番号をクリックします。図 14 の 4 は、下余白が 16 dp に設定されていることを示しています。
このツールで提供されるマージンはすべて 8 dp の係数であるため、ビューをマテリアル デザインの 8 dp 正方形のグリッドの推奨値に合わせることができます。
チェーンを使って線形グループを管理する
チェーンとは、双方向の位置制約を設定して相互にリンクしたビューのグループです。チェーン内のビューは、垂直方向または水平方向に分散できます。
チェーンには、次のいずれかの方法でスタイルを設定できます。
- Spread: マージンを考慮に入れ、ビューは均等に分散されます。これがデフォルトです。
- 内部展開: 最初と最後のビューはチェーンの両端の制約が適用され、残りは均等に分散されます。
- 重み付き: チェーンを spread または spread internal に設定した場合、1 つ以上のビューを「制約に合致」(
0dp
)に設定することで残りのスペースを埋めることができます。デフォルトでは、「制約に合致」に設定された各ビューにスペースが均等に分配されますが、layout_constraintHorizontal_weight
属性とlayout_constraintVertical_weight
属性を使用して各ビューに重要度の重みを割り当てることができます。 これは、線形レイアウトのlayout_weight
と同じように機能します。つまり、重みが最も高いビューが最も大きいスペースを占有し、同じ重みのビューも同じスペース量を取得します。 - パック: ビューはマージンを考慮したうえで一緒にパックされます。チェーンの「ヘッド」ビューのバイアスを変更することで、チェーン全体のバイアス(左右または上下)を調整できます。
チェーンの「ヘッド」ビュー(横方向のチェーン(左から右のレイアウト)の左端のビューと縦方向のチェーンの一番上のビュー)は、チェーンのスタイルを XML で定義します。ただし、チェーン内の任意のビューを選択して、ビューの下に表示されるチェーンボタン をクリックすることで、spread、spread internal、packed を切り替えることができます。
チェーンを作成する手順は次のとおりです。動画 4 をご覧ください。
- チェーンに含めるすべてのビューを選択します。
- いずれかのビューを右クリックします。
- [チェーン] を選択します。
- [中央揃え] または [上下中央揃え] を選択します。
チェーンを使用する際は、次の点を考慮してください。
- ビューを水平方向と垂直方向の両方のチェーンに含めることができるため、柔軟なグリッド レイアウトを作成できます。
- 図 14 に示すように、チェーンの両端が同じ軸上の別のオブジェクトに制約されている場合にのみ、チェーンは適切に機能します。
- チェーンの向きは垂直または水平ですが、チェーンを使用してもその方向にビューが揃えられることはありません。チェーン内の各ビューを適切な位置にするには、アライメントの制約などの他の制約を含めます。
制約を自動的に設定する
レイアウト内に配置する際にすべてのビューに制約を追加する代わりに、Layout Editor 内の目的の位置に各ビューを移動し、[Infer Constraints] をクリックして制約を自動的に作成できます。
[Infer Constraints] は、レイアウトをスキャンして、すべてのビューに対して最も効果的な制約のセットを判断します。これにより、柔軟性を実現しつつ、ビューを現在の位置に制限します。さまざまな画面サイズと向きで意図したとおりにレイアウトが応答するように、調整が必要になることがあります。
[親への自動接続] は、有効にできる別個の機能です。この機能が有効になっていて、子ビューを親に追加すると、ビューをレイアウトに追加すると、各ビューに 2 つ以上の制約が自動的に作成されます。これは、ビューを親レイアウトに制限するのが適切な場合に限られます。自動接続によって、レイアウト内の他のビューに対する制約が生じることはありません。
自動接続はデフォルトで無効です。これを有効にするには、Layout Editor ツールバーの [Enable Autoconnection to Parent] をクリックします。
キーフレーム アニメーション
ConstraintLayout
内では、ConstraintSet
と TransitionManager
を使用して、要素のサイズと位置の変更をアニメーション化できます。
ConstraintSet
は、ConstraintLayout
内のすべての子要素の制約、マージン、パディングを表す軽量のオブジェクトです。表示されている ConstraintLayout
に ConstraintSet
を適用すると、レイアウトはそのすべての子の制約を更新します。
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 デモアプリで使用されています。