3.3 排版姿方法之ConstraintLayout

在我作此书时,ConstraintLayout还是非常新的东西。但是这个东西的确好用,是Android极力推荐的一个排版控件。

所有叫“Layout”的控件都是用于排版的,就是它能决定它所包含的子控件的位置。这些Layout控件有个特点:可以包含多个子控件。不同的Layout控件,它们排列子控件的方式不一样。ConstraintLayout是既好用又强大的一个,能够应付复杂的要求,而且运行效率很高,一些由多个简单Layout组合实现的界面,应该改由一个ConstraintLayout来实现,当然它也不是万能的。

我们现在的界面就是采用了ConstraintLayout,如图3.3.1所示。

图3.3.1

实际上你创建一个新的Activity时,它的layout文件默认就使用ConstraintLayout排版。

3.3.1 ConstraintLayout的原理

Constraint是“约束”的意思,我们可以为ConstraintLayout的子控件添加什么约束呢?位置上的约束。你的App要面对的设备,屏幕有大有小、有方有圆、有宽有窄,要想设计一套界面来适应不同的屏幕就非常难。比如你不可能用固定距离的方式来保持一个控件在横向上居中。有了ConstraintLayout就可以克服这种困难,你可以为一个控件添加一个“保持横向居中”的约束,于是它就横向居中了,不论在任何屏幕上。

可以设置什么样的约束呢?跟你想要的差不多,比如你可以这样设置控件之间的位置关系:


• 设置子控件左边或右边与ConstraintLayout的左边或右边对齐,这样就可以保持子控件居左或居右。

• 设置子控件下边或上边与ConstraintLayout的上边或下边对齐,以此保持子控件居上或居下。

• 设置子控件在ConstraintLayout中横向居中还是纵向居中,或者是横向纵向都居中。

• 设置子控件在ConstraintLayout中居中偏左或偏右或偏上或偏下。

• 设置同属于一个ConstraintLayout的子控件A在子控件B的上面或下面或左边或右边。

• 设置同属于一个ConstraintLayout的子控件A与子控件B左边对齐或右边对齐或上边对齐或下边对齐。


你还可以设置子控件本身的约束,比如:


• 宽和高保持n:m的比率。

• 宽或高为某个固定的值。

• 宽或高由内容决定,比如文本控件的大小由文本中文字的个数决定,图像控件的大小由图像的实际大小决定。


下面,让我们把约束的各种知识都体验一下。

3.3.2 子控件在ConstraintLayout中居左或居右

当前的页面中,TextView控件已经居中了。我们把它删掉,用ImageView来做一下。删除一个控件很简单,选中它,点鼠标右键,在出现的菜单中点“Delete”,也可以选中它直接按“Delete”键。但是,有时可能因为种种原因,不好选中它,那么你可以在控件树中选中它,如图3.3.2.1所示。

图3.3.2.1

删掉它之后,只剩下图像了。现在选中图像,这时未给图像控件加任何约束,于是它默认就在左上角。我们可以为图像添加靠左的限制,但这样其实没效果,那么我们就为它添加靠右的限制吧,如图3.3.2.2所示。

图3.3.2.2

当鼠标进入控件范围内,就会出现一个边框,这个边框的四个边的中间都有一个小圈圈,当鼠标进入这个圈圈时,它会变大变绿,此时你就可以从这个小圈圈中拖出一条线。这条线就代表了约束。我要靠右,所以我把这条线往Layout控件的右边界拖,当拖到右边界时,图像的边框竟然动了!虽然很诡异,但是你不要惊慌,只需松手即可,出现如图3.3.2.3所示效果。

图3.3.2.3

图像右边到Layout右边的约束已被添加。注意在属性栏中也可以看到约束。属性栏中被矩形框起来的代表了Layout的边,被圆形框起来的代表了一个约束。“8”这个值表示两个控件的边之间的空白的距离,它其实是图像控件的“layout_marginRight”属性,如图3.3.2.4所示。

图3.3.2.4

现在运行App看看,是不是靠右了?真的很简单又好玩!怎样让图像靠下呢?我就不讲了,你自己想吧,以你的智慧肯定能做到,我看好你哦。

3.3.3 子控件在ConstraintLayout中横向居中

其实很简单,我只要在上面的基础上再添加一个靠左的约束就行了,如图3.3.3.1所示。

图3.3.3.1

看到这个效果你是不是感觉很直观?Constraint就像弹簧,如果左右都有弹簧拉它并且左右受力相等的话,它就位于中央了。

上下居中我就不用再讲了吧?我相信以看官你的机智肯定能搞出来。

3.3.4 子控件在ConstraintLayout中居中偏左

现在图像左右居中了,但是我们还不满足,我希望它居中再偏左一点,最好在四分之一处居中而不是在二分之一处居中,这样没问题,这就用到这个东西,如图3.3.4.1所示。

图3.3.4.1

它中间有个值“50”表示了左右两个约束的力量,现在是50:50,拖动它试试吧。比如我拖到了25的位置上,如图3.3.4.2所示。

图3.3.4.2

你可以把这个值理解成左右弹簧的力量对比,哪边力大,就偏向哪边。但是,你是否发现一个问题,纵向上没有这个东西。其实是有的,当你在纵向上增加约束之后,它就现身了。

3.3.5 子控件A在子控件B的上面

为了演示两个控件之间的相对位置约束,我们需要再添加一个新的控件,就添加一个按钮吧,我们最终让按钮位于图像的上面。但在此之前,我需要为图像控件添加纵向的约束,我先让它横向纵向都居中,如图3.3.5.1所示。

图3.3.5.1

下面再拖一个按钮进来,如图3.3.5.2所示。

图3.3.5.2

拖进来后,这个按钮由于没有约束,所以会跑到左上角去。下面我们为它添加约束。要想让它在图像的上面,那么就从按钮的下边界拖约束到图像的上边界,如图3.3.5.3所示。

图3.3.5.3

3.3.6 子控件A与子控件B左边对齐

上一节的页面中,按钮在横向上没有约束,所以它默认靠左了,看着不舒服,我们让按钮的左边与图像的左边对齐吧,如图3.3.6.1所示。

图3.3.6.1

可以看到两条优美的曲线揭示了约束的存在。不过,仔细看的话,会发现按钮的左边与图像的左边还有一点差距,没有完全对齐,这其实是按钮的margin在起作用,你要把它的左margin改为0dp,如图3.3.6.2所示。

图3.3.6.2

3.3.7 设置子控件的宽和高

以图像控件为例,当前其宽和高为固定值,在属性栏中可以看出来(图3.3.7.1)。注意红线框出的图形,这个样子就表示固定值,值是多少呢?下面的“layout_width”和“layout_height”的值就是。当你在红框中的图形上点一下鼠标时,会发现图形发生了变化,如图3.3.7.2所示。

图3.3.7.1

图3.3.7.2

图形变成了弹簧的样子,这表示宽度变成了弹性值,即宽度是可变的,同时可以看到layout_width的值变成了“0dp”,此时只要两边没其他控件来挤占它的空间,它就会充满整个控件,此时在预览图中可以看到图像的宽度充满了整个父控件,如图3.3.7.3所示。

图3.3.7.3

3.3.8 子控件的宽和高保持一定比例

我们把图像控件搞成宽高按2:1固定吧。

为了更容易看出效果,我们给图像控件设置一下背景。设置背景就是设置控件的background属性。可以设置一种颜色,也可以设置一个图像。先选中图像控件,再在属性栏中点图3.3.8.1所示的按钮。

图3.3.8.1

出现资源选择对话框,选择一种颜色即可,如图3.3.8.2所示。

图3.3.8.2

设置背景后,再来做一下图像控件的宽高比。还是先选中图像控件,然后在属性栏中点红色箭头所指的位置,如图3.3.8.3所示。于是在下面的红框位置出现了叫作ratio(比率)的输入控件,可以看到默认是1比1,现在预览图中出现图3.3.8.4所示的效果。

图3.3.8.3

图3.3.8.4

可以看到图像的宽度收回来了,与高度保持了1:1,注意此时的layout_width和layout_height,对它们的值是有一定要求的,比如如果这两个值全都不是0dp,那么比率就不起作用了,这里面其实是个优选级的问题,就是有了冲突谁优先起作用。所以要使比例起作用,宽和高必须有一个为0dp,而另一个不能为0dp,可以为match_parent,可以为wrap_content,也可以是大于0dp的固定的值。让我们改成2:1,表示宽是2,高是1,出现如图3.3.8.5所示效果,实际上由于图像太宽,已超出了显示区,我们把高度改小一些,比如改成100dp,效果如图3.3.8.6所示。

图3.3.8.5

图3.3.8.6

搞了这么多,layout文件的源码变成什么样了呢?下面就是:

3.4 排版方法之RelativeLayout

其实在ConstraintLayout出来之前,Android推荐的排版控件是RelativeLayout。它的能力与ConstraintLayout差不多,也是专用于设计复杂的排版。它与ConstraintLayout的区别是,它对于鼠标拖放的方式来布局控件支持得不好,比如我用它时更喜欢直接在属性栏中设置与位置相关的各种属性来对子控件进行排版,非常麻烦。

虽然Android现在推荐的排版控件是ConstraintLayout,但是RelativeLayout依然可用。因为你有可能要面对一些旧代码,所以有必要把它搞清楚,而且后面我用RelativeLayout实现了一个登录界面,其实现过程与ConstraintLayout差不多,所以你在用ConstraintLayout实现相同的界面时,可以参考这里的做法。

Relative的意思是关系,也就是它里面的子控件之间可以设置相对位置关系,其实这跟ConstraintLayout的作用差不多。可以在这里找到RelativeLayout,如图3.4.1所示。

图3.4.1

3.4.1 把ConstraintLayout改为RelativeLayout

新建的Activity默认都用ConstraintLayout作为内容区的最外层控件,所以我们要使用RelativeLayout时有两种办法:一是将一个RelativeLayout放在ConstraintLayout中作为儿子,二是将ConstraintLayout改为RelativeLayout。显然第二种方式更干净,不易受干扰,所以我选择第二种方式玩RelativeLayout。

首先把现在layout文件中的控件都删掉,删除的方法嘛,选中控件点删除键即可。最后只剩下ConstraintLayout。把ConstraintLayout改为RelativeLayout,需要改源码。点“Text”打开源码,如图3.4.1.1所示。

图3.4.1.1

把android.support.constraint.ConstraintLayout改为RelativeLayout,现在整个Layout文件的源码变成了这样:

现在界面空了,我们拖一个图像控件进来,然后为它设置图像,最终效果如图3.4.1.2所示。

图3.4.1.2

我把图像控件放到了左上角。现在属性栏中看不到使用ConstraintLayout时的那些控件了,要了解一个控件的layout位置,只能去属性栏中看那些与layout有关的属性的值。现在进入“View all properties”视图,确保选中了图像,可以看到其属性设置如图3.4.1.3所示。

图3.4.1.3

红框框起来的是三个与位置有关的属性:第一个是与父控件的左边对齐,第二个是与父控件的开始对齐,第三个是与父控件的顶部对齐。其中alignParentLeft与alignParentStart作用完全一样,都是表示左边,但是Left是旧的叫法,新版API中都改叫Start了。你可以只设置Start,而这两个都设置的话,带来的好处是,可以让你的代码在旧的编译工具中被正确编译。

不知你是否注意到,当你把一个控件拖进RelativeLayout中时,会出现箭头指示,它表示被拖的控件与谁发生了相对位置关系。可以为控件设置很多种layout属性,这些属性都以“layout_”开头,在属性栏中向下滚动才能看到。看图3.4.1.4所示的这一堆layout属性吧,要理解它们的作用其实也不难,查一下各单词的意思就行了。

图3.4.1.4

此时的layout文件的源码如下:

RelativeLayout也可以玩出与ConstraintLayout差不多的知识,下面就让我们玩一玩。

3.4.2 左右对齐与居中

图像控件现在位于最左上角。如果要靠近左上解并保持一定的距离,请设置layout_Margin属性。其实在ConstaintLayout中控件之间的空白也是通过这个属性设置的。

让图像居中,选哪些属性呢?直接上图(见图3.4.2.1)。

图3.4.2.1

我选中了center horizontal(横向居中)和center vertical(纵向居中)。其实你也可以不选这两个,而是只选center in parent(在老爸中居中)。

但是现在看起来并没居中。这是因为在拖入时,设置了靠上和靠左,它们之间是有冲突的!一个控件不能既靠上靠左又要居中吧?把冲突去掉吧,我们要居中,只能把靠左靠上去掉了。怎么去掉就不用我再演示了吧?

在排版上设置正确了,你的App的界面就可以放之四海而皆可居中了。比如我们把虚拟机旋转一下,看看横屏时是不是还会居中?如图3.4.2.2所示。

图3.4.2.2

图3.4.2.2中红框内的两个图标就是旋转虚拟机的,很好玩哦。

下面是几种对齐的方案,都试一下吧:


• 上下居中,横向靠右:


layout_centerVertical + layout_alignParentRight。


• 上下居中,横向靠左:


layout_centerVertical + layout_alignParentLeft。


• 纵向靠下,横向居中:


Layout_centerHorizontal+layout_alignParentBottom。


其实你只要认识几个单词,即align(对齐)、parent(父母,就是包含所操作控件的容器)、left(左边)、right(右边)、top(顶部)、bottom(底部)、width(宽)、height(高)等,就可以知道那些layout属性的作用了。

3.4.3 充满整个父控件

需这样设置:layout_width="match_parent",并且layout_height="match_parent"。大家可以看到充满整个父控件后,图像被拉伸变模糊了。为了更能清楚地看到图像控件的大小,我们可以把控件的背景(background)设置为一种颜色(默认是透明的),如图3.4.3.1所示。

图3.4.3.1

选中图像控件,然后在background属性行靠右的“…”图标上点一下,就会出现Drawable选择对话框,如图3.4.3.2所示。

图3.4.3.2

你可以选择一个图像作为背景,也可以选择一个颜色。为了容易观察,我们选择一个颜色吧。现在界面变成了下面这个样子,如图3.4.3.3所示。

图3.4.3.3

3.4.4 兄弟之间相对排

我想这样玩一下:把图像的放在屏幕中间,然后弄一个文本控件显示在图像的上方。图像放中间,我们搞过了,所以先把图像放到中间去,选中以下两个:

但是,你还要把下面三条删掉(如果被选中的话):

现在拖一个文本控件(TextView)进来,放到图3.4.4.1所示的位置。可以看到在拖的过程中,会出现一些虚线和箭头,橘黄色虚线框表示控件拖到的位置,蓝色虚线和箭头表示与谁发生了关系。我们可以看到上图中,被拖动的控件右边与图像控件右边对齐了,向下的箭头表示被拖动的控件的底部与图像的顶部有一个相对距离。在我们拖动完成后,界面设计器会自动为我们设置一些layout相关的属性,让我们看一看都设置了哪些。选中TextView控件,就可以在属性栏看到如图3.4.4.2所示的项。

图3.4.4.1

图3.4.4.2

有四个Layout相关的属性被设置:


• layout_marginBottom:底部空白。

• layout_above:在谁之上。

• layout_alignEnd:与谁右边对齐。

• layout_alignRight:与谁右边对齐。


注意这些项可能不靠在一起,那么你就需要挨个找找,甚至可能会发现已被设置的layout属性要比上面的多。

layout_abouve的值是“@+id/imageView2”指向了图像控件,在图3.4.4.3中我们可以看到图像控件的ID的确是“imageView2”(此时选中了图像控件)。

图3.4.4.3

再说一下padding属性。padding是内部空白的意思,是控件的边到其内容之间的空白,与margin效果看起来差不多但实际很不一样。现在文本控件的layout_marginBottom的值是17dp,就是说文本控件的底边与其他控件之间要空出17dp,于是我们就看到了文本控件与图像控件之间有一定的距离。你可以改个其他值试试。文本控件的layout_above的值是图像控件的id,表示文本控件要在图像控件的上面。

运行App,旋转屏幕看看,是不是它们的相对位置是不变的?现在的layout源码是这样的:

3.4.5 dp是什么

dp是一个表示距离的单位。我并不想说清楚它是怎么计算的,网上有的是文章。我只想说它是与像素无关的单位,它几乎等同于实际的物理距离单位。

试想,一个像素是100×100的图像,在不同的屏幕上按像素显示时会是什么样的?如果一个5寸老屏幕,其宽度上像素数是480,那么这个图像在宽度上占约五分之一。而如果在一个5寸的高分屏上显示的话,假设这个屏幕的宽是1080个像素,那么这个图像只占到十分之一,别忘了这两个都是五寸,实际大小一样,但看到的图像的实际大小差一倍,有可能小到手指头很难点到它了。

所以不能用像素为单位指定控件的大小,而使用“dp”,指定一个与分辩率无关的实际尺寸。在指定距离和大小的地方,千万不要忘记这个单位。

3.4.6 使用RelativeLayout设计登录页面

下面我们玩点复杂的:设计一个登录页面。这个登录页面大体上是这样:最上面是一个头像,中间是用户名输入框,其下是密码输入框,最下面的登录按钮。

先想一下怎么设计。为了美观一些,我们希望这些内容整体居中显示,这里指的是纵向上的居中。因为屏幕一般都是竖着看的。文本输入控件和按钮控件都可以把高度设置为“wrap_content”,这样它们的高就由其文本的字体大小决定,这个值不会太大。图像控件的大小也由内容(也就是图像)来决定的话,就不合适了,可能很小,也可能很大。所以我们应该把图像控件设置成合适的固定大小,然后让图像以保持比例缩放来自适应地填充到图像控件中。总之,一般情况下,我们都是为图像控件指定固定的大小。而对于文本输入控件我也不想让它们在横向上充满整个父控件,所以我对它们的宽也设置固定值,而高就由其内容决定。

纵向上的居中怎么搞才好看呢?如果让图像在纵向上居中,其他控件以它为基准往下摆的话,整体内容看起来就会偏下,不如以图像下面的用户名输入框为基准。把用户名输入框设置为在容器控件中纵向居中,其他控件都以它为基准,在它上面或下面摆放。从上到下依次为:


• 图像控件

• 用户名输入框

• 密码输入框

• 登录按钮


其中用户名输入框纵向居中,其余控件在纵向上以它为基准摆放。

下面让我们一步一步设计出这个登录界面。

3.4.6.1 添加用户名输入控件

还是修改当前的Activity的界面(res/layout/activity_main.xml,如图3.4.4.1所示),在当前的基础上改造一下。我们还是先把“Hello World”这个文本控件删掉吧,用不着它了。

当前,图像控件处于纵向居中,我们先把它移到左上角,等摆好了用户名输入框再摆放它的位置。很简单,在源码中把图像控件的位置相关的属性删掉:

下面,拖一个文本输入控件到页面内,在“Text”组中拖了一个“Plain Text”控件到页面中,当看到横向和纵向上的对齐线都出现时,放开它,如图3.4.6.1.1所示。

图3.4.6.1.1

当然你可以不用拖到合适的位置就放开它,但之后需要手动设置其layout相关属性进行位置调整。我们不想让这个输入控件在横向上充满整个空间,所以为它设置一个固定的宽度:300dp,现在,这个文本输入控件与layout有关的属性如图3.4.6.1.2所示。

图3.4.6.1.2

注意,“Text”这个组下有很多控件,比如“Email”“Phone”等。这些控件用于输入不同的文本格式,“Email”是专门输入邮箱地址的控件,“Phone”是专门输入电话号码的控件。但是,其实它们是同一个Java类(这个控件的类叫作“EditText”),只是把EditText的某些属性预设成了不同的值,我们完全可以自己改变这些值。我们现在使用了最通用的一种:“Plain Text”,对输入文本的格式没什么限制,因为用户名一般都没限制。

只有文本输入控件还不行,我们还要有提示性文字,以告诉用户这个地方应输入什么,以前都是弄一个文本显示控件(比如TextView),放在输入框的左边或上边,提示应输入什么,现在的做法变了,直接在输入框中提示。在Android中很容易做到,只需设置输入控件的“hint(提示)”属性(请仔细寻找):

你还需要把输入控件的默认内容清除掉,找到它的“text”属性,把里面的内容清掉:

现在这个控件的样子是这样的:

因为其他控件要相对它的位置摆放,需要要引用它,所以我们还要设置它的ID,为它的ID设置一个有意义的名字:

3.4.6.2 添加密码输入控件

拖一个“Password”控件到界面上,如图3.4.6.2.1所示(注意指示相对位置的箭头)。

图3.4.6.2.1

设置其layout属性为左右边界都与用户名输入框的左右边界对齐(这样就与用户名输入框宽度保持一致了),纵向上位于用户名输入框下面24dp;并为它设置有意义的ID,如图3.4.6.2.2所示。

图3.4.6.2.2

现在layout源码看起来这样子:

3.4.6.3 添加登录按钮

拖一个按钮进来,放到密码框下面,如图3.4.6.3.1所示。设置属性使它与用户名框左右边界对齐,并改变其显示的标题为“登录”,如图3.4.6.3.2所示。

图3.4.6.3.1

图3.4.6.3.2

给它一个有意义的ID:buttonLogin。

3.4.6.4 设置头像

我们依然利用现有的图像控件,把它的宽和高都设置成100dp。把它拖到左右居中并在用户名框上面一定距离,见图3.4.6.4.1,然后稍微设置一下属性,如图3.4.6.4.2所示。

图3.4.6.4.1

图3.4.6.4.2

最终得到的界面如图3.4.6.4.3所示。

图3.4.6.4.3

虽然不漂亮,但也算小清新了。运行起来看看真实效果吧。

这个页面(activity_main.xml)的源码是:

3.5 让内容“滚”

让内容“滚”不是让内容滚蛋的意思,而是让内容滚起来!

首先把上一节做的登录页面上再增加一个按钮“注册”,把它的ID设为“buttonRegister”,把它放到登录按钮的下面。效果如图3.5.1所示。然后运行App,旋转一下屏幕看看效果。我的运行效果如图3.5.2所示。

图3.5.1

图3.5.2

注册按钮看不到了!为什么?显然屏幕的高不够了,内容在纵向上超出了屏幕。于是问题来了:如果屏幕显示不了整个内容怎么办?答案很简单:“快使用滚动条!吼吼哈嘿!”然而,Layout是没有滚动功能的,要想提供滚动功能,需要使用控件:ScrollView。

ScrollView可以在其儿子的高度超出自己的范围时,在纵向上提供滚动功能。如果想横向滚动的话,请使用另一个View:HorizontalScrollView。但是ScrollView也有自己的要求:只能容纳一个孩子(只生一个好)。

注意ScrollView不同于Layout,所以我们不能用ScrollView代替现在的容器RelativeLayout。其实我们应该让RelativeLayout成为ScrollView的儿子,然后再让RelativeLayout的高度由其内容决定,也就是由组成登录界面的各子控件来共同决定。

RelativeLayout被放在ScrollView中后,其高度不能再设为match_parent了,因为ScrollView需要根据其儿子的高度决定是否滚动,如果其儿子的高度永远与它的高一样的话,那永远不可能需要滚动。其儿子应体现出内容的高度,这里也就是组成登录功能的控件们所占的高度,所以RelativeLayout的layout_height的值必须为wrap_content。下面我们继续一步步改造。

3.5.1 添加ScrollView作为最外层容器

可以拖一个ScrollView到页面中,如图3.5.1.1所示。

图3.5.1.1

但是,如果你试过了,你会发现这样做不行,呵呵。因为你无法将一个控件拖到页面中作为最外层的控件。此时手动改源码更简单,把页面切换到源码模式,在最外层的元素“<RelativeLayout>”的外面添加标记“<ScrollView>”,在RelativeLayout的结束标记“</RelativeLayout>”下面添加ScrollView的结束标记:“</ScrollView>”,也就是让ScrollView元素包着RelativeLayout元素。

然后,你还需要把RelativeLayout标记中的一些属性移动到ScrollView标记中。移动哪些呢?看这里(这些属性必须放在最外层的元素中):

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="niuedu.com.andfirststep.MainActivity"

你还要为ScrollView设置宽和高,它既然是最外层的控件,那么就让它充满它的整个父控件吧(就是Activity了)。现在layout文件的源码变成了这样:

你可以切换回预览模式,你会惊奇的发现现在控件的摆放出问题了,如图3.5.1.2所示。

图3.5.1.2

图像跑到上面去了,控件之间的间距也出了问题。如何修复这些错误呢?请看下节。

3.5.2 改正在ScrollView下的排版

现在选中“RelativeLayout”看一下,你会看到奇怪的现像:虽然它的宽和高都设置成了match_parent,但是它却是“臣妾做不到啊”。注意现在可能在预览中很难选中RelativeLayout,那么就在控件树面板中选吧,如图3.5.2.1所示。

图3.5.2.1

下面是RelativeLayout的宽和高的设置,如图3.5.2.2所示。

图3.5.2.2

ScrollView的内容必须有具体的高度,这样它才能决定是否需要滚动。所以把RelativeLayout的高设置为match_parent不再起作用,其实RelativeLayout的高暗中变成了“wrap_content”。

注意横向上是没问题的,因为ScrollView并不提供横向滚动,所以它的子控件横向上的排版方式跟以前一样。下面我们就把登录界面调整好。怎样调整呢?现在要让RelativeLayout恰好包着整个内容,那么再让用户名输入框纵向居中就没意义了,我们在设计登录界面时应该改为遵守从上住下依次摆放各控件的原则。

图像在最上面,首先改图像。不再让图像相对于用户名输入框摆放位置,而是让图像位于父控件的顶端,所以把图像控件的属性改成这样,如图3.5.2.3所示。

图3.5.2.3

注意设置了layout_alignParentTop,使得图像控件对齐到了父控件的顶端。用户名输入框应相对于图像控件摆放,位于它下面24dp,所以其属性改成这样,如图3.5.2.4所示。

图3.5.2.4

注意设置了layout_below,取消了layout_centerVertical,密码框和按钮的相对位置没变,不用动。现在页面看起来是图3.5.2.5所示这样子。控件之间的位置关系终于正常了。此时运行一下App,旋转到横屏,你会发现界面可以被上下拖动了,右边还出现了滚动条,如图3.5.2.6所示。

图3.5.2.5

图3.5.2.6

其实除了使用ScrollView外,还有一个办法可以解决横屏显示不全的问题,那就是不支持横屏!即固定Activity的方向,这只需要在Manifest文件中做一下下,如图3.5.2.7所示。

图3.5.2.7

其实还有一个办法,就是专门创建横屏Layout,如图3.5.2.8所示。

图3.5.2.8

选择“Create Landscape Variation(创建风景画变体)”,会当前Layout添加一个新的资源文件,当屏幕改为横屏时,App会自动加载这个横屏的Layout资源。

Landscape是风景画的意思。油画中风景画都是宽的,用来代表横屏。竖屏是portrait,肖像画。肖像画都是长的,用来代表竖屏。

贴一下源码吧:

3.6 添加新的Layout资源

添加新的Layout资源,其实就是往合适的文件夹下添加一个XML文件,当然我们应该借助Android Studio提供的工具而尽量不要手动去做。具体做法是:在res/layout组上点出右键菜单,如图3.6.1所示。

图3.6.1

然后选择New→Layout resource file,出现新建资源对话框,如图3.6.2所示。

图3.6.2

在“File name”项中输入layout文件的名字,将来也是这个资源的ID,所以要注意其规则,不能以数字开头,单词之间推荐用下划线分隔(非必须,但最好遵守)。

“Root element”项中输入这个Layout的根控件,即某个Layout控件。在这里我们使用一个新的Layout:FrameLayout。

“Source set”有三个选项:main、release、debug。debug指的是带有调试信息的App版本。release是没有调试信息的App版本。这里指的是分别包含在debug、release版中的代码和资源,即可以指定某些文件只在release版中起作用,有些只在debug版中起作用。而属于main的文件在两者中都起作用。这里一般就选main。

“Directory name”是所在文件夹的名字,这个不要变了,必须在layout下。

下面的不用选,点OK即可。