2.4 Android的主要布局对象
Android的布局管理控件用于把多个控件集成在一个用户界面中。Android常见的布局对象有:ConstraintLayout(约束布局)、LinearLayout(线性布局)、TableLayout(表格布局)和FrameLayout(帧布局)。它们都继承了ViewGroup,作为各种不同布局管理模型的容器,它们都提供了各自独到的功能。
1.ConstraintLayout布局
ConstraintLayout使得我们在构建复杂布局的同时能够让视图层级得到精简,而且可以通过布局工具拖拽轻松实现布局。从Android Studio 2.3.x版本开始,ConstraintLayout(约束布局)是Android Studio的默认布局,能够减少布局的层级并改善布局性能。ConstraintLayout能够灵活地定位和调整子View的大小,子View依靠约束关系来确定位置。在一个约束关系中,需要有一个Source(源)以及一个Target(目标), Source的位置依赖于Target,可以理解为“通过约束关系,Source与Target链接在了一起”, Source相对于Target的位置便是固定的了。
ConstraintLayout约束布局和其他布局容器一样,都是继承自ViewGroup的,所以它也拥有其他布局的一些公用属性,与其他布局不同的是它是通过约束规则来实现布局的,所以它还新增了一些特有的属性。
(1)ConstraintLayout的基本操作。
ConstraintLayout的基本用法很简单,例如我们要向布局中添加一个按钮,那么只需从左侧的Palette区域拖一个Button进去就可以了。
虽说现在Button已经添加到界面上了,但是由于我们还没有给Button添加任何的约束,因此Button并不知道自己应该出现在什么位置。现在我们在预览界面上看到的Button位置并不是它最终运行后的实际位置。如果一个控件没有添加任何约束,它在运行之后就会自动位于界面的左上角。
下面我们就来给Button添加约束。每个控件的约束都分为垂直和水平两类,一共可以在四个方向上给控件添加约束。Button的上下左右各有一个圆圈,这些圆圈就是用来添加约束的。我们可以将约束添加到ConstraintLayout,也可以将约束添加到另一个控件。
例如,想让Button位于布局的右下角,我们给Button的右边和下边添加约束,因此Button就会将自己定位到布局的右下角了。类似地,如果我们想让Button居中显示,那么就需要给它的上下左右都添加约束。这就是添加约束最基本的用法了。
除此之外,我们还可以使用约束让一个控件相对于另一个控件进行定位。例如,我们希望再添加一个Button,让它位于第一个Button的正下方,并且间距为64dp。
删除添加的约束也很简单,删除约束的方式一共有三种:第一种用于删除一个单独的约束,将鼠标悬浮在某个约束的圆圈上,然后该圆圈会变成红色,这个时候单击一下就能删除了;第二种用于删除某一个控件的所有约束,选中一个控件,然后它的左下角会出现一个删除约束的图标,单击该图标就能删除当前控件的所有约束了;第三种用于删除当前界面中的所有约束,单击工具栏中的删除约束图标即可。
当选中任意一个控件的时候,在右侧的Attributes区域就会出现很多的属性选项。在这里我们可以设置当前控件的所有属性,例如文本内容、颜色、单击事件等。
需要我们重点掌握的是Attributes区域的上半部分,这部分也被称为Inspector。首先可以看到,在Inspector中有一个纵向的轴和一个横向的轴,这两个轴也是用于确定控件位置的。我们前面给Button的上下左右各添加了一个约束,然后Button就能居中显示了,其实就是因为这里纵横轴的值都是50。如果调整了纵横轴的比例,那么Button的位置也会随之改变。
不过,虽然我们将横轴的值拖动到了100,但是Button并没有紧贴到布局的最右侧,这是为什么呢?实际上,Android Studio给控件的每个方向上的约束都默认添加了一个16dp的间距,从Inspector上面也可以明显地看出来这些间距的值。如果这些默认值并不是你想要的,可以直接在Inspector上进行修改。可以看到,修改成0之后Button右侧的间距就消失了。
接下来了解一下位于Inspector中间的那个正方形区域,它是用来控制控件大小的。一共有三种模式可选,每种模式都使用了一种不同的符号表示,单击符号即可进行切换。
表示wrap_content。
表示固定值,也就是给控件指定了一个固定的长度或者宽度值。
表示any size,它有点类似于match_parent,但和match_parent并不完全一样,是属于ConstraintLayout中特有的一种大小控制方式,下面我们来重点讲解一下。
首先需要说明,在ConstraintLayout中是有match_parent的,只不过用得比较少,因为ConstraintLayout的一大特点就是为了解决布局嵌套。既然没有了布局嵌套,那么match_parent也就没有多大意义了。
而any size就是用在ConstraintLayout中顶替match_parent的,先看一下我们怎样使用any size实现和match_parent同样的效果吧。例如我想让Button的宽度充满整个布局,我们将Button的宽度指定成any size,它就会自动充满整个布局了。当然还要记得将Button左侧的间距设置成0才行。这和match_parent有什么区别呢?其实最大的区别在于,match_parent是用于填充满当前控件的父布局,而any size是用于填充满当前控件的约束规则。
(2)Guidelines。
如果想让两个按钮共同居中对齐该怎么实现呢?其实这个需求很常见,例如在应用的登录界面,都会有一个【登录】按钮和一个【注册】按钮,不管它们是水平居中还是垂直居中,肯定都是两个按钮共同居中的。
要想实现这个功能,要用到ConstraintLayout中的一个新功能——Guidelines。
以下通过实际操作来学习Guidelines的用法。例如,现在已经向界面中添加了【登录】和【注册】这两个按钮,然后希望让这两个按钮在水平方向上居中显示,在垂直方向上都距离底部64dp,那么就需要先添加一个垂直方向上的Guideline。
首先单击【Layouts】面板中的Guidelines图标,添加一个垂直或水平方向上的Guideline,这里我们需要的是垂直方向上的。而Guideline默认是使用的dp尺,我们需要选中Guideline,并单击最上面的箭头图标将它改成百分比尺,然后将垂直方向上的Guideline调整到50%的位置,这样就将准备工作做好了。
接下来我们开始实现让两个按钮在水平方向上居中显示,并距离底部64dp的功能。可以看到,我们为【登录】按钮的右边向Guideline添加约束,【登录】按钮的下边向底部添加约束,并拖动按钮让其距离底部64dp。然后为【注册】按钮的左边向Guideline添加约束,【注册】按钮的下边向【登录】按钮的下边添加约束。这样就实现了让两个按钮在水平方向上居中显示,在垂直方向上都距离底部64dp的功能了。
(3)自动添加约束。
如果界面中的内容变得复杂起来,给每个控件逐个地添加约束也是一件非常烦琐的事情。为此,ConstraintLayout中支持自动添加约束的功能,可以极大程度上简化那些烦琐的操作。自动添加约束的方式主要有两种:一种叫Autoconnect,另一种叫Inference。
想要使用Autoconnect,首先需要在工具栏中启用这个功能,默认情况下Autoconnect是不启用的,如图2-2所示。
图2-2 工具栏中启用Autoconnect功能
Autoconnect可以根据我们拖放控件的状态自动判断应该如何添加约束,例如将Button放到界面的正中央,那么它的上下左右都会自动地添加上约束。然后我们在这个Button的下方再放置一个Button,可以看到,只需将Button拖放到界面上,Autoconnect会判断我们的意图,并自动给控件添加约束。不过Autoconnect是无法保证百分百准确判断出我们的意图的,如果自动添加的约束并不符合要求,还可以在任何时候进行手动修改。总之,可以把它当成一个辅助工具,但不能完全靠它去添加控件的约束。
接下来我们看一下Inference的用法。Inference也是用于自动添加约束的,但它比Autoconnect的功能更为强大,因为Autoconnect只能给当前操作的控件自动添加约束,而Inference会给当前界面中的所有元素自动添加约束。因而Inference比较适合用来实现复杂度比较高的界面,它可以一键自动生成所有的约束。
下面通过一个实例来演示Inference的用法。例如,界面上现在有两个TextView、两个EditText和两个Button,接下来我们先将各个控件按照界面设计的位置进行摆放,摆放完成之后单击工具栏上的“Infer Constraints”按钮,如图2-3所示,就能为所有控件自动添加约束了。
图2-3 工具栏上的“Infer Constraints”按钮
2.LinearLayout布局
LinearLayout是一个最常用的基础布局对象,它以单一方向对其中的显示对象进行排列显示,如以垂直排列显示,则布局管理器中将只有一列;如以水平排列显示,则布局管理器中将只有一行。同时,它还可以对个别的显示对象设置显示比例。
LinearLayout布局可以实现水平布局和垂直布局,它通过设置android:orientation属性将内部的所有子视图以横向或纵向进行排列。如果将布局方向设置为“vertical”,则表示垂直方向布局;设置为“horizontal”则表示水平方向布局。在LinearLayout中还可以设置内部子视图的方位(gravity), gravity是一个排列属性,可以通过设置它的值为left、center或right而将视图放在父视图的左边、中间或右边。
将多个横向的LinearLayout当成子视图放进一个纵向的LinearLayout中,就形成一个类似Table的布局,如果是这种情况,还是用TableLayout比较合适。
3.TableLayout布局
TableLayout布局以拥有任意行和列的表格对显示对象进行布局,每个显示对象被分配到各自的单元格之中,但单元格的边框线不可见。
利用TableLayout布局管理对象,会将容器内部的子节点按照行列进行排序,类似HTML中的<table>节点。在TableLayout节点内部声明<TableRow>将为table添加一行,<TableRow>中的子视图将排成N列,如果<TableRow>内有N个子视图就将该行划分为N列。整个表的列是根据表格每行中最多有多少列来决定的,假设整个表格中有2行为3列,其中另有1行为4列,则表格将分为4列。TableLayout默认会将它的直接子节点当成一行,所以可以放置任意的View到TableLayout中,而且对于TableLayout的直接子节点设置layout_width="wrap_ content"属性将不会生效,因为默认它会重写成match_parent。
4.FrameLayout布局
FrameLayout是一个最简单的布局对象,其中只显示一个显示对象。在屏幕上预留一块空白的区域,所有的元素都被放置在FrameLayout区域的左上方,无法为这些元素设置确切的位置。如果有多个元素,则会重叠在前一个元素上,只有最上面的元素能被看到;如果设置最上面的元素背景为透明的,则可以透过上面的元素看到下面的元素。