2017/03/25

ConstraintLayout

勘所

ConstraintLayoutは名前の通り, 制約によってレイアウトを組むものです.
今までのLinearLayoutやRelativeLayoutのようにViewの配置によるレイアウトから考え方を変えて, レイアウトを制約で定義することによってレスポンシブUIのような柔軟なUIや複雑なレイアウトをよりフラットなViewヒエラルキで実現できるようになり, パフォーマンスの向上が期待できます.

ConstraintLayoutについては下記のDeveloperサイトにまとめられています.
導入方法まで含めた動画も公開されています.

Build a Responsive UI with ConstraintLayout
https://developer.android.com/training/constraint-layout/index.html

Layout Editorの使い方.
https://developer.android.com/studio/write/layout-editor.html

本稿はドキュメントを読むだけではわからなかった箇所+簡易な基礎説明を主に載せています.

Horizontal / Vertical constraint

ConstraintLayoutにおけるViewのポジションは水平 and 垂直方向の制約(Constraint)を定義することで指定します. ConstraintLayoutにおいてViewは水平方向と垂直方向の制約両方を定義しなければなりません.

制約はRelativeLayout`の相対位置指定に一見似ていますが, RelativeLayoutがView自体のポジションを定義するのに対して, ConstraintLayoutの制約はViewのルール(制約)を決めるだけであり, Viewのサイズ指定には別のパラメータが用意されています.

従来のレイアウトより高次な”制約”という概念がConstraintLayoutには追加されており, これがよりレスポンシブなUIを作成する助けとなっています.
下記のイメージはImageViewの左辺/下辺が親レイアウトに, ImageViewの上辺/右辺がTextViewに整列する”制約”を追加した例です.

ImageViewが制約の範囲内でレイアウトされているのがわかります.
さらに, Viewのポジションを決定する要素として”バイアス”が追加されました. これは決められた制約の中でViewの位置を決めるものです.

ConstraintLayoutのlayout_width / height

ConstraintLayoutでは従来のmatch_parentが廃止されました.
Viewのサイズ(width/height)指定には次の3つの考え方があります.

  • Wrap Content
  • Match Constraints
  • Fixed

Wrap Content

Viewコンテンツに必要な分だけの領域をサイズとします. 従来のそれと同じ効果のものです.
XMLでの指定も同じです android:layout_width="wrap_content"

Match Constraints

制約のルールを満たす範囲内で指定できる最大限の領域をサイズとします.
以前のmatch_parentは廃止され, かわりにMatch Constraintsを指定することになります.

例えば, 画面の端から端までのViewサイズを定義するのであれば, 親レイアウトの両端に制約を追加してMatch Constraintsを指定すれば完成です.
制約のルールを満たす範囲内で指定できる最大限の領域をサイズとするわけです.

ちなみに, match_constraintsという定義値はありません. Viewのサイズが0(dp)である場合, ConstraintsLayoutがMatch Constraintsとして扱います.
そのため, レイアウトXML上はandroid:layout_width="0dp"となります.

これに加えて, Match Constraintsに限りwidth or heightをもう一方のheight or widthとのratio(比率)で指定することができます(比率はwidth:heightの順)

例えば, widthをheightの2倍にしたい場合は次のように指定します.

android:layout_width="0dp"
android:layout_height="100dp"
app:layout_constraintDimensionRatio="w,2:1"

あるいは, 次のように操作します.

heightは固定値で, widthにはMatch Constraint(0dp)を指定しています. さらに, widthのサイズ比率の制約を定義するapp:layout_constraintDimensionRatioを指定します.
例では, ここに"w,2:1"と定義しています. この値の意味は次の通りです.

`w`(width)のサイズを 2(width) : 1(height) の比率で指定する

heightが100dpで指定されているので, 結果的にwidthは200dpになります.
widthを固定サイズとしてheightをratio指定することも可能です. その場合は

android:layout_width="100dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="h,2:1"

といった具合になります. 上記の例だとwidthが100dpなので, 2(width) : 1(height) の割合で結果的にheightは50dpのサイズになります.

もし, widthとheight両方にMatch Constraint(0dp)が指定されている場合, ratioはwidthを基準とするのか, あるいはheightを基準とするのかを選択することになります.

親レイアウトの上下左右の両端に制約をもたせたMatch ConstraintなImageViewでratioを指定する例をみてみます.

まず, Match Constraintがwidth/height両方に指定されていますので親レイアウトいっぱいにViewが広がります.
その後, heightのサイズを 1:1 とするratioが指定されますが, 親レイアウトのwidthがheightより大きいので, 親レイアウトの高さに収まらなくなっています.
次に, widthのサイズを 1:1 とするratioに切り替えられます. これによってheightはMatch Constraintにより親レイアウトの高さに収まり, widthはheightと同じサイズ(ratio 1:1)が適用されているのがわかります.
最後には, 親レイアウト(画面)のwidth/hight比率と近似の 16:9 に設定されています. ratioを適用するのは短辺にあたる height が指定されています.
レイアウトXMLには次のように定義されます.

android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="h,16:9"

Fixed

名前の通り, 固定値でサイズを指定するものです.
指定方法も従来と変わりありません. android:layout_width="48dp"

Text Baseline alignment

テキストのベースラインでViewのポジションを調整したい場合はベースライン制約を追加します.

Guideline

垂直または水平なガイドライン(基準線)を定義することができます.
これによって, 親レイアウトの端ではなく, 特定の余白を上下左右に設けたレイアウトの定義が楽になります.

タブレットのような大画面時に左右余白をもたせたい場合など, ガイドラインの位置を調整するだけで, これを制約とするViewの相対位置が変化するためより直感的なレイアウトを組むことができます.

layout_goneMargin

ConstraintLayoutではViewのVisibilityがGONEに設定されても, 他の制約が崩れないよう, サイズ0のViewがいるかのように振る舞います.

もし, 依存先のViewが存在しなくなった場合のマージンを別で指定したい場合は, layout_goneMarginStart / Top / End / Bottomを使うことができます.

以上です.