昨年の 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 レポジトリの問題にご報告ください。