画面レイアウトの基礎
前回は、アプリにフラグメントを追加するところまでを説明しました。今回は画面レイアウトについて説明します。
View
Androidアプリの画面はViewという部品のツリー構造でできています。Viewの中でも他のViewを子として持てるViewをViewGroupと呼びます。
ConstraintLayout
ツリー構造の起点(ルート)となるViewはConstraintLayoutです。2016年に導入されました。以前はRelativeLayoutをルートにするのが主流でしたが、2019年時点ではConstraintLayoutをルートにして画面レイアウトをするのが主流です。
しかし、Android StudioのフラグメントテンプレートはなぜかFrameLayoutがルートになっています。まずこれをConstraintLayoutに変更します。手でXMLを書き換えるのではなく、Android Studioの機能を使います。
res/layout/fragment_title.xmlを開き、レイアウトエディタを表示します。そして画面の左下にあるComponent Treeで、FrameLayoutを右クリックし、「Convert FrameLayout to ConstraintLayout」を選びます。これでViewツリーのルートがConstraintLayoutに変更されます。

ConstraintLayoutの基本
ConstraintLayoutでは、各Viewが
- 位置が決まる
- 大きさが決まる
よう、制約を追加していきます。制約が足りない場合(例えば水平方向でどこに配置すればわからない)はエラーとなり、予期せぬ配置が行われてしまいます。

ViewのID
各ViewにはIDをつけることができます。レイアウトエディタで配置した場合は適当なIDが自動で付与されます。XMLでは、次のように@+id/ID名という値で設定します。
android:id="@+id/login_button"
このIDはJava/KotlinでツリーからViewを取り出すときに使用したり、制約をつけるときのViewの指定に使用したりします。そのため、Viewツリーの中でIDは重複してはいけません。
左端・右端・上端・下端を合わせる
まずは2つのViewの端を合わせる方法です。例えばボタンの上端とテキストの上端を合わせたい場合、レイアウトエディタでボタンを選び、ボタンの上端の●を合わせたいテキストの上端にある●までドラッグします。

XMLで直接編集する場合は、次のようにlayout_constraintTop_toTopOf
を使います。
app:layout_constraintTop_toTopOf="@+id/textView"
また、合わせる対象Viewを親のConstraintLayoutにすることもできます。レイアウトエディタの場合は●を画面の端にドラッグします。XMLで直接編集の場合は、対象ViewのIDの代わりにparent
を指定します。
app:layout_constraintTop_toTopOf="parent"
例として、TextViewを画面の左上に配置する場合のXML指定です。
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
テキストの下にボタンを配置する
これもよくあるパターンです。レイアウトエディタの場合、ボタン上端の●をテキスト下端の●にドラッグします。XMLで直接記述する場合は、app:layout_constraintTop_toBottomOf="@+id/textView"
のように、layout_constraintTop_toBottomOf
を使います。

マージンの指定
マージンを指定することで、View間に隙間をあけることができます。レイアウトエディタではViewを選択し、右側のAttributesのLayoutの所で指定します。XMLで直接記述する場合は、layout_marginTop
(上)やlayout_marginBottom
(下)などを使います。

例として、左側と上側にそれぞれ8dpのマージンを指定する例です。なお、dp単位がどのようなものかは次回以降で説明します。
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_marginTop="8dp"
app:layout_marginLeft="8dp"/>
Viewのサイズ
Viewのサイズはlayout_width
(幅)とlayout_height
(高さ)で指定します。単位はdp単位を使用します。TextViewやImageViewといった、内部にコンテンツを持つViewの場合、「コンテンツが入るサイズ」というwrap_content
という特殊な値を使用することができます。
水平方向中央に配置する
これまでの例は左端のみ制約を指定していました。そこで右端も同時に制約を指定すると、Viewは両端から引っ張られることになります。この場合、Viewは中央に配置されます。layout_constraintHorizontal_bias
を指定すると、左右で引っ張る強さに差をつけ、「中央から少しだけ寄せ」みたいな配置ができます。なお上下の場合はlayout_constraintVertical_bias
で指定します。
次の例はTextViewを親Viewの左右中央に配置する例です。LeftとRightの両方が指定されています。
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
Viewのサイズを水平方向いっぱいにする
Viewに対し、左右の制約をつけると中央に配置されます。この状態で幅を0dpにするとViewのサイズは0dpではなく、制約で左右目一杯に引っ張ったサイズになります。
次の例はメールアドレス入力欄を画面いっぱいにする例です。左端と右端を親Viewに合わせるので、結果として水平方向いっぱいのサイズになります。
<EditText
android:id="@+id/edit_email"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
まとめ
ConstraintLayoutを使ったレイアウト方法を説明しました。画面レイアウトはアプリ開発で何度も行うことになるので、自由自在にレイアウトできるようになりましょう。