使用FlexboxLayout构建灵活的布局
去年的 Google I/O大会上我们宣布了ConstraintLayout, 一个用简单的视图结构就能构建复杂布局的控件。另外,它还完全支持Android Studio的可视化布局编辑器。
与此同时,我们开源了FlexboxLayout,把css中的 Flexbox布局(Flexible Box)模块 带到了安卓中。这里就是一些FlexboxLayout特别有用的情形。
FlexboxLayout可以理解成一个高级版的LinearLayout,因为两个布局都把子view按顺序排列。两者之间最大的差别在于FlexboxLayout具有换行的特性。
也就是说如果你添加了flexWrap="wrap"属性,在当前行空间不足的情况下FlexboxLayout将把view放在下一行,就如下图这样:
一个布局适应不同屏幕尺寸
在这个特性的基础上,让我们考虑一个按序排列视图,但是在空间发生变化的时候可以让view移到新的一行的情况(可能因为设备因素,横竖屏变化,或者多窗口模式下的窗口调整)。
Nexus5X 竖屏
Nexus5X 横屏
启用多窗口模式,分割线在左边的的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">
然后你就可以得到下面这种能换行的布局,而不是视图从它的父布局益出。
另一个要强调的技术就是为单个子view设置layout_flexGrow 。在空间有剩余的情况下,这可以帮助改善布局的最终外观。layout_flexGrow属性类似于LinearLayout中的layout_weight。也就是说,FlexboxLayout将根据同一行中每个子view的layout_flexGrow的值来分配剩余的空间。
在下面的例子中,假设每个子view的layout_flexGrow属性为1,那么剩余空间将平均分配到每个子view。
<android .support.design.widget.TextInputLayout
android:layout_width="100dp"
android:layout_height="wrap_content"
app:layout_flexgrow="1">
你可以在GitHub仓库上找到完整的 layout xml 文件。
RecyclerView集成
FlexboxLayout的另一个好处是可以和RecyclerView集成。最新的alpha 版本中,Flexbox可以作为一个LayoutManager(FlexboxLayoutManager)用在RecyclerView中。现在你可以以更内存友好的方式在滚动容器中使用Flexbox的功能了。
不过你仍然可以把FlexboxLayout放在ScrollView中来实现滚动。但是这样的话如果item过多很可能会造成卡顿和内存益处的错误,因为FlexboxLayout并不会回收那些滚动出屏幕的view。
(如果你想了解RecyclerView的更多细节,可以看看来自ndroid UI toolkit团队的视频,比如 1, 2)
集成RecyclerView能带来好处的真实的例子是 Google Photo app或者新闻app,两者都具有大量的宽度各异的item。
FlexboxLayout仓库的demo application中有一个这样的例子,RecyclerView中的每个image宽度不同。但是通过把flexWrap设置为wrap,
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager();
layoutManager.setFlexWrap(FlexWrap.WRAP);
并把每个子view的flexGrow设置为一个正数,(就如你看到的,我们可以通过FlexboxLayoutManager和FlexboxLayoutManager.LayoutParam设置这个属性,而不通过xml)
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);
}
}
你可以看到不管屏幕方向如何,布局中的每一个image都排列得都很合适。
要看看完整的FlexboxLayout,请到:
-
Playground demo app - 使用 FlexboxLayout 和 FlexboxLayoutManager.
-
Cat gallery demo app - 使用 FlexboxLayoutManager
接下来是什么?
需要其它属性构建量为自己身定做的布局请看完整的文档。我们非常欢迎来自你的反馈,如果发现什么问题或者有新的功能需求,请在 GitHub repository上提交issue。