昨年の Google I/O では、フラットなビュー階層を維持しつつ複雑なレイアウトを構築でき、Android Studio の
Visual Layout Editor でも完全にサポートされている
ConstraintLayout を発表しました。
同じタイミングで
FlexboxLayout をオープンソース化し、Android で
CSS Flexible Layout モジュールと同じ機能が使えるようになりました。この記事では、
FlexboxLayout
が特に有用ないくつかのケースを紹介します。
FlexboxLayout
は、高度な
LinearLayout と考えることができます。どちらのレイアウトも、子ビューを順番に配置する機能を持っているためです。
LinearLayout と
FlexboxLayout
のもっとも顕著な違いは、
FlexboxLayout
には折り返し機能があることです。
FlexboxLayout
に
flexWrap="wrap" 属性を追加すると、次の図のように現在の行に十分なスペースがない場合に、ビューが新しい行に配置されるようになります。
1 つのレイアウトでさまざまな画面サイズに対応
この特徴を考慮して、ビューを順番に配置しつつ、(端末のファクターの違い、画面の向きの変更、マルチウィンドウ モードでのウィンドウのサイズ変更などにより)利用できるスペースが変化した場合に次の行にビューを移動させるケースについて考えてみましょう。
Pixel C でマルチウィンドウ モードが有効になっていて、分割線が左側にある場合
Pixel C でマルチウィンドウ モードが有効になっていて、分割線が中央にある場合
Pixel C でマルチウィンドウ モードが有効になっていて、分割線が右側にある場合
LinearLayout
や
RelativeLayout
などの従来のレイアウトでさまざまな画面サイズに対応するには、DP を指定した複数のレイアウト(layout-600dp、layout-720dp、layout-1020dp など)を定義する必要がありました。一方、上のダイアログは
FlexboxLayout
を使用した一つのレイアウトだけでできています。
この例では、先ほど説明した
flexWrap="wrap"
を設定するというテクニックを利用しています。
<com .google.android.flexbox.flexboxlayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:flexwrap="wrap">
これによって、子ビューが親からはみ出るのではなく、新しい行に配置されるようになり、次のようなレイアウトが得られます。
もう 1 つ紹介しておきたいのは、個々の子ビューに
layout_flexGrow
属性を設定するテクニックです。これによって、スペースが余った場合の最終的なレイアウトの見栄えを改善できます。
layout_flexGrow
属性は
LinearLayout
の
layout_weight
属性に相当します。つまり、
FlexboxLayout
は、設定された
layout_flexGrow
の値に応じて、余ったスペースを同じ行に配置された子ビューに割り当てます。
下に示すのは、各子ビューの
layout_flexGrow
属性を
1
に設定している例です。そのため、余ったスペースがそれぞれの子ビューに均等に割り当てられています。
<android .support.design.widget.TextInputLayout
android:layout_width="100dp"
android:layout_height="wrap_content"
app:layout_flexgrow="1">
このケースで使われている
layout xml ファイルは、GitHub レポジトリで確認できます。
RecyclerView との統合
FlexboxLayout
が提供するもう 1 つのメリットは、
RecyclerView と統合できるという点です。最新リリースである
アルファ版の新しい
FlexboxLayoutManager
は、
RecyclerView.LayoutManager
を拡張しています。そのため、スクロール可能な Flexbox コンテナではるかにメモリ効率がよい Flexbox の機能を使うことができます。
なお、スクロール可能な Flexbox コンテナは、
ScrollView
の中に
FlexboxLayout
を配置することでも実現できますが、レイアウトに含めるアイテムの数が増えると、スムーズに動かなくなったり OutOfMemoryError が発生する可能性が高くなります。これは、
FlexboxLayout
ではユーザーがスクロールして画面外に移動したビューの再利用が考慮されていないためです。
(RecyclerView の詳細を知りたい方は、
1、
2 などの Android UI ツールキット チームの動画をご覧ください)
RecyclerView
の統合が役に立つ実例としてあげられるのは、Google Photos やニュースアプリのようなアプリです。これらはどちらも、さまざまな幅のアイテムを大量に処理する必要があります。
FlexboxLayout
のレポジトリには、例として
デモアプリが公開されています。レポジトリを見ればわかるように、
RecyclerView
内のイメージの幅はまちまちです。しかし、flexWrap で折り返すように設定してあり、
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager();
layoutManager.setFlexWrap(FlexWrap.WRAP);
さらに各子ビューの
flexGrow
属性(下記のコード例のように、
FlexboxLayoutManager
では xml ではなく、
FlexboxLayoutManager.LayoutParams
を使用して RecyclerView の各要素の属性を設定できます)も正の値に設定してあるため、
void bindTo(Drawable drawable) {
mImageView.setImageDrawable(drawable);
ViewGroup.LayoutParams lp = mImageView.getLayoutParams();
if (lp instanceof FlexboxLayoutManager.LayoutParams) {
FlexboxLayoutManager.LayoutParams flexboxLp =
(FlexboxLayoutManager.LayoutParams) mImageView.getLayoutParams();
flexboxLp.setFlexGrow(1.0f);
}
}
画面の向きによらず、すべてのイメージをきれいにレイアウト内に収めることができます。
さらに複雑な FlexboxLayout の例を確認したい方は、以下をご覧ください。
次のステップ
ニーズに合った柔軟なレイアウトの構築に役立つその他の属性については、完全版の
ドキュメントをご覧ください。皆さんのフィードバックをお待ちしています。問題や機能リクエストがある場合は、
GitHub レポジトリの問題にご報告ください。