SRIAの佐藤(琢)です。
みなさんAndroid開発してますか?
今回はConstraintLayoutのお話です。
ConstraintLayoutを使ってレイアウトを組むと、Viewの改装が少なくなってパフォーマンスが上がるとか、複雑なレイアウトをフラットに表現できるなどのメリットがあると言われています。
いろいろな記事でも紹介されています(ConstraintLayoutを入れ子にするとパフォーマンスが落ちるなどの諸説はありますが…。)
実際に使ってみた感想としては、今まではLinearLayoutなどの並び順を持ったViewに「要素を入れる」方法でレイアウトしていたのに対し、ConstraintLayoutでは「要素をつなげる・乗せる」方法でフラットなXMLの記述ができていると感じています。
spaceタグを使うとXMLの記述をシンプルでかつ短く描けた
ConstraintLayoutでレイアウトを組んでいる時に、要素ごとにマージンやパディングのつけ忘れがないか考えるのが嫌になりました。
もっとレイアウトXMLの記述をシンプルでかつ短く書けるのでは?と思った次第です。
レイアウトXML記述が短くなると内容が確認しやすくなり綺麗なXMLになります。
spaceタグを組み合わせることでViewの役割を明確にし、要素ごとの共通マージンやパディングの記述を無くしていきます。
space
https://developer.android.com/reference/android/widget/Space.html
Android4.0から追加されていたんですね。知らなかった…。
各レイアウトごとに比較していく
下記の画像をテーマごとにXMLに組んで比較していきます。
①LinearLayoutでレイアウトする
②LinearLayoutでレイアウトしたXMLをConstraintLayoutに変える
③ConstraintLayout+spaceでレイアウトする
各テーマのXMLの内容は違いますが、画面に表示されるViewは同じなので比較が難しいです。
開発者向けオプションの[レイアウト境界を表示]を有効にしてレイアウトの境界線を確認していきます。
①LinearLayoutでレイアウトする
LinearLayoutで組む場合は下記のようにならざるを得ないのではと思います。
LinearLayout:orientation=”vertical”で縦にレイアウトを並べて、
横並びにする箇所はLinearLayout:orientation=”horizontal”を入れ子にします。
さらにその中で縦並びを実現するためにLinearLayout:orientation=”vertical”を入れ子にしてデザインを表現していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical"> <!--タイトルテキスト--> <TextView android:id="@+id/titleText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:gravity="center_horizontal" android:text="画像タイトル" android:textSize="30sp" /> <!--トップ画像--> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:scaleType="centerCrop" android:src="@drawable/ic_launcher_background" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:orientation="horizontal"> <!--メニューへの遷移ボタン--> <ImageButton android:id="@+id/imageButton" android:layout_width="62dp" android:layout_height="62dp" android:src="@drawable/ic_launcher_background" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:orientation="vertical"> <!--メニューのタイトル--> <TextView android:id="@+id/imageTitleText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="画像アイコンのタイトルです" android:textSize="24sp" /> <!--メニューの説明文--> <TextView android:id="@+id/imageDetailText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="詳細テキストです" android:textSize="24sp" /> </LinearLayout> </LinearLayout> </LinearLayout> |
レイアウト境界のピンクの部分がマージンです。
全要素にマージンが指定されているのがわかります。
②LinearLayoutでレイアウトしたXMLをConstraintLayoutに変える
LinearLayoutを使用しないで、app:layout_constraint○○_to○○Ofの記述で各要素をつなげてデザインを表現していきます。
入れ子がなくなったのでXMLの内容がすっきりしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<?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"> <!--タイトルテキスト--> <TextView android:id="@+id/titleText" android:layout_width="0dp" android:layout_height="wrap_content" android:text="画像タイトル" android:textSize="30sp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:gravity="center_horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <!--トップ画像--> <ImageView android:id="@+id/imageView" android:layout_width="0dp" android:layout_height="200dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:scaleType="centerCrop" android:src="@drawable/ic_launcher_background" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/titleText" /> <!--メニューへの遷移ボタン--> <ImageButton android:id="@+id/imageButton" android:layout_width="62dp" android:layout_height="62dp" android:layout_marginStart="16dp" android:src="@drawable/ic_launcher_background" app:layout_constraintBottom_toBottomOf="@+id/imageDetailText" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/imageTitleText" /> <!--メニューのタイトル--> <TextView android:id="@+id/imageTitleText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="24dp" android:text="画像アイコン" android:textSize="24sp" app:layout_constraintStart_toEndOf="@+id/imageButton" app:layout_constraintTop_toBottomOf="@+id/imageView" /> <!--メニューの説明文--> <TextView android:id="@+id/imageDetailText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="詳細テキスト" android:textSize="24sp" app:layout_constraintStart_toStartOf="@+id/imageTitleText" app:layout_constraintTop_toBottomOf="@+id/imageTitleText" /> </androidx.constraintlayout.widget.ConstraintLayout> |
マージンが指定された要素にだけ存在していてガタガタしている印象です。
③ConstraintLayout+spaceでレイアウトする
spaceタグを追加して余計なマージンを除去していきます。
spaceの役割はView共通の余白として使用します。
大抵のデザインには上下左右に共通の余白ってありますよね。
XMLの記述量は増えますが上下左右のspaceを一度記述するだけで良くなります。
マージンの変わりにlayout_constraintXXで上下左右のspaceに対してレイアウトを繋げます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
<?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"> <!--縦の余白--> <Space android:id="@+id/spaceTop" android:layout_width="0dp" android:layout_height="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <!--左の余白--> <Space android:id="@+id/spaceStart" android:layout_width="16dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <!--右の余白--> <Space android:id="@+id/spaceEnd" android:layout_width="16dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <!--ボタン右の余白--> <Space android:id="@+id/spaceButtonEnd" android:layout_width="8dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="@+id/imageButton" app:layout_constraintStart_toEndOf="@+id/imageButton" app:layout_constraintTop_toTopOf="@+id/imageButton" /> <!--タイトルテキスト--> <TextView android:id="@+id/titleText" android:layout_width="0dp" android:layout_height="wrap_content" android:text="画像タイトル" android:textSize="30sp" android:gravity="center_horizontal" app:layout_constraintEnd_toStartOf="@+id/spaceEnd" app:layout_constraintStart_toEndOf="@+id/spaceStart" app:layout_constraintTop_toBottomOf="@+id/spaceTop" /> <!--トップ画像--> <ImageView android:id="@+id/imageView" android:layout_width="0dp" android:layout_height="200dp" android:layout_marginTop="16dp" android:scaleType="centerCrop" android:src="@drawable/ic_launcher_background" app:layout_constraintEnd_toStartOf="@+id/spaceEnd" app:layout_constraintStart_toEndOf="@+id/spaceStart" app:layout_constraintTop_toBottomOf="@+id/titleText" /> <!--メニューへの遷移ボタン--> <ImageButton android:id="@+id/imageButton" android:layout_width="62dp" android:layout_height="62dp" android:src="@drawable/ic_launcher_background" app:layout_constraintBottom_toBottomOf="@+id/imageDetailText" app:layout_constraintStart_toEndOf="@+id/spaceStart" app:layout_constraintTop_toTopOf="@+id/imageTitleText" /> <!--メニューのタイトル--> <TextView android:id="@+id/imageTitleText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="画像アイコン" android:textSize="24sp" app:layout_constraintStart_toEndOf="@+id/spaceButtonEnd" app:layout_constraintTop_toBottomOf="@+id/imageView" /> <!--メニューの説明文--> <TextView android:id="@+id/imageDetailText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="詳細テキスト" android:textSize="24sp" app:layout_constraintStart_toEndOf="@+id/spaceButtonEnd" app:layout_constraintTop_toBottomOf="@+id/imageTitleText" /> </androidx.constraintlayout.widget.ConstraintLayout> |
上記の実装をすることで、縦横の余白の幅を変更したい時は各spaceのwidth・heightの値を変えるだけで良くなります。
そもそもマージンを書かなくて良くなるので、つけ忘れを心配する必要もありません。
今回はViewの数が少ないので冗長ですが、Viewの数が増えるほど効果があると考えています。
別にspaceを使わなくても、マージンの値をdimenで定義していれば一括で値の変更もできますが、
Viewの役割を一度考えてみることが、レイアウトXMLの記述をシンプルに書けることに繋がるのではないかと思います。