こんにちは、株式会社ヤプリでAndroidエンジニアをしているdais-sasaです!
最近、ユーザーの指に追従するスマートなアニメーションを簡単に実装できると噂の「MotionLayout」について勉強しているのですが、「MotionLayout」は「ConstraintLayout」を継承したLayoutであり、アニメーション部分以外は「ConstraintLayout」の属性を使います。
そのため、まずは「ConstraintLayout」を使ってみてLayoutについての理解を深めようと思い、実際に使ってみました。
本記事では、実際に「ConstraintLayout」を使ってみて、気をつけなければならない点を紹介します。
Androidエンジニアの皆さんの何かの参考になれば幸いです。
概要
本記事で紹介する気をつけなければならない点は、以下になります。
- marginの設定
- Layoutの大きさが取得できないパターン
- constraintDimensionRatioの設定
また、前提として、ConstraintLayoutの基本的な使い方は知っているとします。
一応、次の「ConstraintLayoutとは?」で軽く説明しますが、詳しくは、「ConstraintLayoutとは?」で紹介しているリンク先で確認して下さい。
ConstraintLayoutとは?
まず、前提となる知識を記載します。
「ConstraintLayout」ですが、GoogleI/O 2016で新しく登場したLayoutです。
子供のViewに制約を設定する事によって画面を作成するLayoutで、ほとんどネストを必要としないで複雑なレイアウトを作成できるようになりました。
今までのLayoutとは、大分使い方が異なるためか、Android Developersに詳細に使用方法が記載されているので、参考にしてみてください。 developer.android.com
なお、Qitaにも「ConstraintLayout」の使用方法について分かり易くまとめてある記事があるので、そちらを参考にすると基本的な使い方は分かると思います。 qiita.com
上述の記事を読んで基本的な使い方を学び、そして、実際に使う際に以降に記載するトピックを頭に入れておくと「ConstraintLayout」を問題なく使えるようになると思います。
marginの設定
marginの設定方法については既存のLayoutと同じですが、「ConstraintLayout」はmarginを設定する際に既存のLayoutとは異なるルールがあります。
例として、以下の図のように「LayoutA」と「LayoutB」があり、「LayoutB」に「layout_constraintStart_toEndOf」で「LayoutA」の右側に配置するように制限が設定されているとします。
そして、「LayoutA」と「LayoutB」の間に余白を設定したいとします。
親のLayoutが「LinearLayout」や「RelativeLayout」の場合、marginはどちらのLayoutに設定しても問題なく余白が表示されるようになります。
しかし、「ConstraintLayout」の場合、必ず「LayoutB」にmarginの設定をする必要があります。
どうやら「ConstraintLayout」内では、marginは「layout_constraint○○〜」を設定した場所でのみ有効になるようです。
なお、「LayoutA」側にmarginを設定したい理由の一つとして、「LayoutA」が非表示になった場合にmarginも併せてなくなって欲しいというものが考えられますが、その場合は「LayoutB」側に「layout_goneMarginStart」で「0dp」を設定することで「LayoutA」が非表示になった場合に、marginがなくなるようにできるので、無理に「LayoutA」側にmarginを指定する必要はないと思います。
Layoutの大きさが取得できないパターンがある
ケースとしては少ないと思いますが、「ConstraintLayout」自体の大きさを取得しようとした際に、大きさを取得できないパターンが存在します。
そのパターンというのは簡単で、「ConstraintLayout」に「wrap_content」が設定されている場合です。
「height」や「width」、「onMesure」等、Viewの大きさを取得する方法は色々ありますが、「ConstraintLayout」自体は「wrap_content」が設定されていると大きさが取得できません。
内部の処理を見た訳ではないのですが、恐らく「ConstraintLayout」は自身の子供のViewがどこにどのように配置されているかは、一切管理しておらず、wrap_contentのように内部のコンテンツにあわせて大きさを設定した場合は、大きさを計算するための情報がないのではないかと思います。
ただ、wrap_contentを設定していても画面上は想定した通りの表示になったので、既存のLayoutとは異なる手段でLayoutの大きさを取得できるかもしれません。
残念ながら、現状、その手段を見つけることはできていません。
「ConstraintLayout」の使用方法としては一番親のLayoutとしてmatch_parentで設定するものが基本となると思うので、上記ケースはあまりないと思いますが、覚えておくといざという時に困ったりしないと思います。
constraintDimensionRatioの設定
「ConstraintLayout」内では縦横の比を指定して縦と横のサイズを指定することができます。
設定方法は、「ConstraintLayoutとは?」で紹介した記事で記載されていますが、勘違いして迷走してしまったので、設定のルールを記載します。
まず、縦と横のどちらかが、match_parent(0dp)で、どちらかがmatch_parent(0dp)ではない場合、「横:縦」(横、縦には数値が入ります)で設定できます。
この場合、「match_parent(0dp)ではない」の方の長さを基準にして、「横:縦」の比になるように「match_parent(0dp)」の方の長さを変更します。
例えば、以下のように設定した場合
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" ... > <View android:layout_width="wrap_content" android:layout_height="0dp" ... app:layout_constraintDimensionRatio="2:1" ... /> </android.support.constraint.ConstraintLayout>
高さが「幅 * 1/2」となるように設定されます。
次に、縦と横の両方が「match_parent(0dp)」の場合、「w,横:縦」または「h,横:縦」で設定できます。
- 「w,横:縦」と設定した場合、「縦」の長さを基準にして、設定した比となるように「横」の長さを変更します。
- 「h,横:縦」と設定した場合、「横」の長さを基準にして、設定した比となるように「縦」の長さを変更します。
どちらの辺が基準になるかを間違えないように注意して下さい。
(最初、基準になる辺を勘違いして、しばらく悩むようになってしまいました)
例えば、以下のように設定した場合
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" ... > <View android:layout_width="0dp" android:layout_height="0dp" ... app:layout_constraintDimensionRatio="w,2:1" ... /> </android.support.constraint.ConstraintLayout>
幅が「高さ*2」となるように設定されます。
最後に
いかがだったでしょうか。
「ConstraintLayout」は今までのLayoutとは、考え方を変えて実装する必要があるので、最初は戸惑う事が多いですが、慣れてくるとネストがなくなりフラットなXMLを記述できるようになります。
特に、画面が複雑であればあるほど有用です。
また、まだ不便な部分もありますが、GUIが充実しているので、そもそもxmlを直接いじる必要がほとんどなくなるようになります。
今後は、ScrollViewやRecyclerViewのような特殊なView以外は、「ConstraintLayout」を使って画面を作成する事が主流になっていくと思います。
是非、皆さんも「ConstraintLayout」を使ってLayoutを作成して見て下さい。