XMLで始めるConstraintLayout

## はじめに
みなさんConstraintLayoutは使っているでしょうか？
ConstraintLayoutはGoogleI/O 2016で新しく登場したLayoutです。その名の通り制約によってレイアウトを決めること複雑なレイアウトを、ネストを深くせずに作ることが出来るようになりました。
RelativeLayoutと似ていますが、iOSのAutoLayoutのようにAndroid Studio上のLayoutEditorでドラックアンドドロップによって簡単にレイアウトが作れます。

また、[Android Developers Blog](https://android-developers.googleblog.com/2017/08/understanding-performance-benefits-of.html?m=1)では、RelativeLayoutに比べてパフォーマンスが約 **40%** 優れているといっています。
>Our performance comparison shows that ConstraintLayout performs about 40% better in the measure/layout phase than RelativeLayout:

![](https://4.bp.blogspot.com/-iZSeC2m1Ti8/WZ3j3rAiq6I/AAAAAAAAEhE/4bdOKMUvUSsyx563WZ7lIgZoeYdGmLgTwCLcBGAs/s1600/image2.png)

しかし、複雑なレイアウトを作る場合には、これまで通りLayout EditorからでなくXMLからConstraintLayoutを使いたいです。
そこで今回はConstraintLayoutをXML上で書くためのタグと意味をRelativeLayoutと比較しながら説明していきます。

RelativeLayoutの詳しい説明は[今さら聞けないRelativeLayoutの話](http://qiita.com/yysk/items/c686153b39d32571d1bd)がおすすめです。

## 大きさ(width/height)
- WRAP_CONTENT
- MATCH_CONSTRAINT( **0dpを指定** )

ConstraintLayoutでは **MATCH_PARENTがサポートされていません**。
その代わりにMATCH_CONSTRAINTを用いることで、制約の範囲内でViewを最大サイズにします。
MATCH_PARENT的なことをするためには、対応する左/右または上/下の制約を「親」に設定することで可能となります。

## 位置
![](https://developer.android.com/reference/android/support/constraint/resources/images/relative-positioning.png)

ConstraintLayoutでは属性を何も指定しない場合、左端に重なるようにして表示されてしまいます。
位置を指定するために、ConstraintLayoutでは自身のViewの一辺をどこに合わせるかという制約を考える必要があります。
例えば上の図では、ViewAの右端をViewBの左端に合わせています。これをxmlで書くと、`layout_constraintEnd_toStartOf="B"` となります。つまり、`layout_自身のViewの一辺_対象のViewの一辺`というように書くことで位置を指定することができるようになります。

### layout_to~
特定のViewの右側・左側に配置したいときに指定するのがこの`layout_constraintStart_toEndOf`です。
`layout_constraintStart_toEndOf="parent"` または `layout_constraintStart_toEndOf="@id/hoge"`のように使います。

|属性|効果|RelativeLayout|
|:--|:--|:--|
|layout_constraintEnd_toStartOf|指定したViewの左側に配置| layout_toStartOf |
|layout_constraintStart_toEndOf|指定したViewの右側に配置| layout_toEndOf |
|layout_constraintRight_toLeftOf|指定したViewの左側に配置|layout_toLeftOf |
|layout_constraintLeft_toRightOf|指定したViewの右側に配置| layout_toRightOf |
|layout_constraintBottom_toTopOf|指定したViewの上側に配置| layout_above |
|layout_constraintTop_toBottomOf|指定したViewの下側に配置| layout_below |

### layout_align
特定のViewに対して上端の位置や右端の位置を揃えるように配置したいときに使うのがこの`layout_constraintStart_toStartOf`です。
同じように、`layout_constraintStart_toStartOf="parent"(layout_constraintLeft_toLeftOf="@id/hoge")` `layout_constraintEnd_toEndOf="@id/hoge"(layout_constraintRight_toRightOf="@id/hoge")`のように使います。

|属性|効果|RelativeLayout|
|:--|:--|:--|
|layout_constraintStart_toStartOf|自身の左端と指定したViewの左端を揃える|layout_alignStart|
|layout_constraintEnd_toEndOf|自身の右端と指定したViewの右端を揃える| layout_alignEnd |
|layout_constraintLeft_toLeftOf|自身の左端と指定したViewの左端を揃える| layout_alignLeft |
|layout_constraintRight_toRightOf|自身の右端と指定したViewの右端を揃える|layout_alignRight|
|layout_constraintTop_toTopOf|自身の上端と指定したViewの上端を揃える| layout_alignTop |
|layout_constraintBottom_toBottomOf|自身の下端と指定したViewの下端を揃える| layout_alignBottom |

### センタリング
![](https://developer.android.com/reference/android/support/constraint/resources/images/centering-positioning.png)
この左寄せと右寄せを同時に指定することで、センタリングをすることが出来ます。
水平方向・垂直方向どちらに対しても指定できます。

```centering.xml
<TextView android:id="@+id/text_view"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent" />
```

### バイアス
センタリングをすると、名前の通りViewは中央に配置されます。
そこで、0-1の範囲でバイアスを指定することで位置を調整させることが出来ます。

|属性|効果|
|:--|:--|
|layout_constraintHorizontal_bias|水平方向のバイアスを指定|
|layout_constraintVertical_bias|垂直方向のバイアスを指定|

左側に30%のバイアスを掛けた例

![](https://developer.android.com/reference/android/support/constraint/resources/images/centering-positioning-bias.png)

```bias.xml
<TextView android:id="@+id/text_view"
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />
```

### Baseline
その名の通り、特定のViewのベースラインの位置と指定したViewのベースラインを揃えて配置することができます。
Textのベースラインをしっかりと揃えたいときに使います。

|属性|効果|RelativeLayout|
|:--|:--|:--|
|layout_constraintBaseline_toBaselineOf|自身のベースラインと指定したViewのベースラインを揃える|layout_alignBaseline|

## Margin
ConstraintLayoutでは **一般的なマージン** と・**制約の対象となるViewがGONEだったときのマージン** の２種類のマージンを使うことが出来ます。

### 一般的なマージン
![](https://developer.android.com/reference/android/support/constraint/resources/images/relative-positioning-margin.png)

|属性|パラメーター|
|:--|:--|
|android:layout_marginLeft|dp|
|android:layout_marginRight|dp|
|android:layout_marginStart|dp|
|android:layout_marginEnd|dp|
|android:layout_marginTop|dp|
|android:layout_marginBottom|dp|

### 制約の対象となるViewがGONEだったときのマージン
![](https://developer.android.com/reference/android/support/constraint/resources/images/visibility-behavior.png)

|属性|パラメーター|
|:--|:--|
|layout_goneMarginLeft|dp|
|layout_goneMarginRight|dp|
|layout_goneMarginStart|dp|
|layout_goneMarginEnd|dp|
|layout_goneMarginTop|dp|
|layout_goneMarginBottom|dp|

## 比
ViewのWidth/HeightがMATCH_CONSTRAINTのときに比率を用いて大きさを指定することが出来ます。

|属性|パラメーター|
|:--|:--|
| layout_constraintDimensionRatio |X:Y|

```.xml
<Button 
    android:layout_width="wrap_content"
    android:layout_height="0dp"
    app:layout_constraintDimensionRatio="1:1" />
```

WidthとHeight両方がMATCH_CONSTRAINTのときには`app:layout_constraintDimensionRatio="w,1:1" `のようにカンマ区切りで`w`または`h`を指定することでどちらを基準にするかを選択できます。

比で指定する方法としては@mochicoさんの[こちら](https://qiita.com/mochico/items/6e914a1c304d8b358f6e)の記事がわかりやすいです。


## チェーン
![](https://developer.android.com/reference/android/support/constraint/resources/images/chains.png)

複数のViewが双方向に作用するように制約をかけている状態をチェーンと言います。
チェーンは起点となる位置のView(ヘッド)に`layout_constraintHorizontal_chainStyle` もしくは、`layout_constraintVertical_chainStyle`を利用することで設定できます。また、`layout_constraintHorizontal_chainStyle`は水平方向、`layout_constraintVertical_chainStyle`は垂直方向の設定です。


|属性|パラメーター|
|:--|:--|
| layout_constraintHorizontal_chainStyle |スタイル|
| layout_constraintVertical_chainStyle |スタイル|

```.xml
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_chainStyle="packed" />
```

### チェーンスタイル

チェーンのスタイルは3種類あります。

![](https://developer.android.com/reference/android/support/constraint/resources/images/chains-styles.png)

|種類|意味|
|:--|:--|
|spread|均等になるようにマージンを取って並べる|
|spread_inside|エンドポイントのマージンは取らずに、中のViewは均等になるようにマージンを取って並べる|
|packed|一つの要素としてくっつけた状態で均等にマージンを設定する|

### Weighted Chain
スタイルがspreadもしくはspread_insideのときに`layout_constraintHorizontal_weight`または`layout_constraintVertical_weight`を指定することで、ウエイトを用いてViewの幅を指定することが出来ます。

### Packed Chain with Bias
スタイルがpackedのときに`layout_constraintHorizontal_bias`または`layout_constraintVertical_bias`を指定することでバイアスを掛けることが出来ます。

## さいごに
さて、以上をもとに、このようなよく使うレイアウトを作ってみましょう。
![](https://storage.googleapis.com/material-design/publish/material_v_11/assets/0Bx4BSt6jniD7Tm9ZQmFCZzlITkE/components_lists_keylines_two7.png)

もちろんRelativeLayoutでも書くことが出来ます。

```relative_sample.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="72dp"
    android:paddingEnd="16dp"
    android:paddingRight="16dp">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="72dp"
        android:layout_height="72dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_centerVertical="true" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toEndOf="@id/icon"
        android:layout_toRightOf="@id/icon"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="bottom"
            tools:text="Title" />

        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="top"
            tools:text="text" />

    </LinearLayout>
    
</RelativeLayout>
```

これをConstraintLayoutで書くと...

```constraint_sample.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="72dp"
    android:paddingEnd="16dp"
    android:paddingRight="16dp">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="72dp"
        android:layout_height="72dp"
        app:layout_constraintLeft_toLeftOf="parent" />

    <TextView
        android:id="@+id/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/icon"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="Title" />

    <TextView
        android:id="@+id/text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/icon"
        app:layout_constraintTop_toBottomOf="@id/title"
        tools:text="text" />

</android.support.constraint.ConstraintLayout>
```
ネストが減ってパフォーマンスの向上とともにコードが見やすくなりました。
ぜひXMLでConstraintLayoutを使っていきましょう。

## 参考
https://developer.android.com/reference/android/support/constraint/ConstraintLayout.html
https://developer.android.com/training/constraint-layout/index.html
https://android-developers.googleblog.com/2017/08/understanding-performance-benefits-of.html?m=1
http://qiita.com/yysk/items/c686153b39d32571d1bd
http://yuki312.blogspot.jp/2017/03/constraintlayout.html
http://blog.techium.jp/entry/2017/02/27/194714