Posted by Takeshi Hagikura, Developer Programs Engineer
At Google I/O last year we announced
ConstraintLayout,
which enables you to build complex layouts while maintaining a flat view
hierarchy. It is also fully supported in Android Studio's
Visual
Layout Editor.
At the same time, we open sourced
FlexboxLayout to bring the
same functionalities of the
CSS
Flexible Layout module to Android. Here are some cases where
FlexboxLayout
is particularly effective.
FlexboxLayout
can be interpreted as an advanced LinearLayout
because both layouts align their child views sequentially. The significant
difference between LinearLayout and
FlexboxLayout
is that
FlexboxLayout
has a feature for wrapping.
That means if you add the
flexWrap="wrap" attribute,
FlexboxLayout
puts a view to a new line if there is not enough
space left in the current line as shown in the picture below.
One layout for various screen sizes
With that characteristic in mind, let's take a case where you want to put views
sequentially but have them move to new lines if the available space changes (due
to a device factor, orientation changes or the window resizing in the
multi-window mode).
Pixel C with multi window mode enabled, divider line on the left.
Pixel C with multi window mode enabled, divider line on the middle.
Pixel C with multi window mode enabled, divider line on the right.
You would need to define multiple DP-bucket layouts (such as layout-600dp,
layout-720dp, layout-1020dp) to handle various screen sizes with traditional
layouts such as
LinearLayout
or
RelativeLayout
. But
the dialog above is built with a single
FlexboxLayout
.
The technique used in the example is setting the
flexWrap="wrap"
as
explained above,
<com .google.android.flexbox.flexboxlayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:flexwrap="wrap">
then you can get the following layout where child views are aligned to a new
line instead of overflowing its parent.
Another technique I'd like to highlight is setting the
layout_flexGrow
attribute to an individual child. This helps
improve the look of the final layout when free space is left over. The
layout_flexGrow
attribute works similar to the
layout_weight
attribute in
LinearLayout
. That means
FlexboxLayout
will distribute the remaining space according to the
layout_flexGrow
value set to each child in the same line.
In the example below, it assumes each child has the
layout_flexGrow
attribute set to
1
, so free space will be evenly distributed to
each of them.
<android .support.design.widget.TextInputLayout
android:layout_width="100dp"
android:layout_height="wrap_content"
app:layout_flexgrow="1">
You can check out the complete
layout
xml file in the GitHub repository.
RecyclerView integration
Another advantage of
FlexboxLayout
is that it can be integrated
with
RecyclerView.
With the latest release of the
alpha
version the new
FlexboxLayoutManager
extends
RecyclerView.LayoutManager
, now you can make use of the Flexbox
functionalities in a scrollable container in much more memory-efficient way.
Note that you can still achieve a scrollable Flexbox container with
FlexboxLayout
wrapped with
ScrollView
. But, you will
be likely to experience jankiness or even an OutOfMemoryError if the number of
items contained in the layout is large, as
FlexboxLayout
doesn't
take view recycling into account for the views that go off the screen as the
user scrolls.
(If you would like to learn more about the RecyclerView in details, you can
check out the videos from the Android UI toolkit team such as
1,
2)
A real world example where the
RecyclerView
integration is useful
is for apps like the Google Photos app or News apps, both expect large number of
items while needing to handle various width of items.
One example is found in the
demo
application in the
FlexboxLayout
repository. As you can see in
the repository, each image shown in
RecyclerView
has a different
width. But by setting the flexWrap setting to wrap,
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager();
layoutManager.setFlexWrap(FlexWrap.WRAP);
and setting the
flexGrow
(as you can see, you can configure the
attributes through
FlexboxLayoutManager
and
FlexboxLayoutManager.LayoutParams
for child attributes instead of
configuring it from xml) attribute to a positive value for each child,
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);
}
}
you can see every image fits within the layout nicely regardless of the screen
orientation.
If you would like to see complete FlexboxLayout example, you can check:
What's next?
Check out the full
documentation for other
attributes to build flexible layouts tailored for your needs. We're very open to
hear your feedback, if you find any issues or feature requests, please file an
issue on the GitHub
repository.