登录后台

页面导航

本文编写于 2407 天前,最后修改于 1667 天前,其中某些信息可能已经过时。

0x0 前言

一个炫丽的界面往往都是由很多的小控件组成的。队伍整齐划一,往往是因为他们按队列站。一个程序的界面想做好,就要依赖布局。布局描述了控件在屏幕中的位置和定位方式。布局有很多种,我们今天先来介绍相对简单的线性布局。

1x0 线性布局(LinearLayout)

顾名思义,「线性布局」可以把其中包含的视图按线性排列。换句话说,「线性布局」可以将其他视图在单个列中水平排列,也可以垂直排列在单个行中。 之前我们都是把控件放在他里面,因为这种布局通常情况下控件之间不会重叠。现在来看一下他的继承结构:

可以看到他是一种 ViewGroup(混眼熟),以下代码展示了如何在布局XML文件中使用线性布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="horizontal">

   <!-- 其他的布局或控件 -->

 </LinearLayout>

1x1 orientation

这里有一个新属性「orientation」,可以通过他指定是否在行或列中显示子视图 :

属性解释
orientationhorizontal水平排列
vertical竖直排列

我们回顾一下细节,我们以前的项目结构就是「vertical」,也就是竖直排列。新建一个项目,放三个「Button」,代码如下:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="1"/>
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="2"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="2"/>

</LinearLayout>

这里通过「android:orientation="vertical"」指定了根布局竖直方向排列,里面有三个「Button」,长和宽都是「wrap_content」,效果图如下:

现在我们试试改成水平方向:「android:orientation="horizontal "」:

果真和我们预想的一样,但是要注意几点:

  • 如果不指定「orientation」的值,默认就是水平
  • 如果「LinearLayout」的排列方式是水平的话,内部的控件的宽就绝对不能是「match_parent」,因为这样那个控件在充满屏幕的同时就会把别的控件挤出屏幕,别的控件就没有可放置的位置了。
  • 如果「LinearLayout」的排列方式是竖直的话,内部的控件的高就绝对不能是「match_parent」,道理同上。

1x2 布局中的对齐方式

我们再来看一个新属性「android:layout_gravity」,看起来有没有一点眼熟?没错。我们之前学过「android:gravity」这个属性,也应用过许多次了,他可以指定文字在控件中的对齐方式。我们来打量打量这个新属性「layout_gravity」比之前的多了一个「layout」。「layout」是布局的意思,联想起来就是控件在布局中的对齐方式。

说白了,「gravity」是控件内部的元素(像文字啥的)相对于控件的对齐方式;
而「layout_gravity」是「LinearLayout」内部的控件相对于「LinearLayout」的对齐方式。

「layout_gravity」和「gravity」的可选值几乎一样,而且水平和竖直都能用。需要注意的是当「LinearLayout」的排列方式。需要注意的是当「LinearLayout」的排列方式是「horizontal」时只有垂直方向的对齐方式才会生效,因为此时水平方向上的长度是不固定的,每添加一个控件,水平方向的长度都会改变,因而无法指定水平方向上的对齐方式。同理,当「LinearLayout」的排列方式是「vertical」时只有水平方向的对齐方式才会生效。继续修改上面的代码:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:text="1"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:text="2"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="2"/>

</LinearLayout>

这里「LinearLayout」的排列方向是「horizontal」,也就是说只能指定在垂直方向上的排列方式:将「Button」的「layout_gravity」分别指定为「top」,「center_vertical」,「bottom」,效果图如下:

1x3 按比例调整大小

有的时候我们主要几个控件按比例填充屏幕,比如,聊天app的输入框。下面我来介绍一个新属性:「layout_weight」(权重)。总的来说就是屏幕的剩余空间按比例分配 。注意的是:只有在Linearlayout中,该属性才有效 。

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:text="在此处输入内容...."/>

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="发送"/>

    </LinearLayout>

先来张效果图吧:

如果要在水平方向使用「layout_weight」(权重)来确定控件的宽,就要先把控件的宽设置为「0dp」,这是一种标准写法,因为这时控件的宽度不应该由「layout_width」来决定【文末会作为进阶内容给大家分析】。

其中「dp」是 Android 中一种描述控件大小
px : 其实就是像素单位,比如我们通常说的手机分辨列表1920*1980都是px的单位
sp : 同dp相似,还会根据用户的字体大小偏好来缩放 
dp : 虚拟像素,在不同的像素密度的设备上会自动适配 
dip: 同dp

然后我们再把 EditText 的「android:layout_weight」设置为 3 ,把 Button 的设置为 1。意思就是把屏幕水平方向上按 3:1 分配,其中 EditText 占 3 份,Button 占 1 份。再举一个例子,如果 EditText 的「android:layout_weight」属性的值相等,那么就会平分屏幕的水平方向。

1x4 一个固定,另一个按比例调整大小

有的时候,我们可能还需要一个控件的尺寸固定,然后用别的控件来填充剩余空间。换句话说就是一个空间假设是 3 dp ,不管设备屏幕多大,他都是 3 dp ,至于剩的空间怎么办,那是别的控件的事,他们去适应,反正我就 3 dp。继续修改布局如下:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="在此处输入内容...."/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送"/>

    </LinearLayout>

这里把「Button」的「layout_width」指定为「wrap_content」,「Button」的宽度就由他自己决定。此时「EditText」依旧是宽 0dp ,weight 为1。也可以理解为 Button 独立,EditText 的宽度按照剩余空间的百分比计算,一方面按百分比算会填充剩余的空间,另一方面只有 EditText 这一个控件设置了 weight ,没人跟他抢,所以他的权重自然就是100%。综上的结果就是 Button 适应自己的大小,而 EditText 会占满屏幕剩余的空间。这是效果图:

2x0 总结

今天我们学习了最常用的一个布局「LinearLayout」,那么作业来了,做一个带地址栏的浏览器:

提示:布局是可以嵌套的。

2x1 进阶

还记得我说如果要在水平方向使用「layout_weight」(权重)来确定控件的宽,就要先把控件的宽设置为「0dp」吧。这是因为实际上控件最终的宽度 = 「layout_width」的宽度 + (剩余空间 * 占比)。
假设一个水平的 LinearLayout 里有两个控件,宽度都为 match_parent 的情况下,原有宽度为L,两个的View的宽度都为L,那么剩余宽度为L-(L+L) = -L, 左边的View占比四分之三,左边的总宽度是L+(-L)*1/4 = (3/4)L。

TIPS: Google官方推荐,当使用weight属性时,将width设为0dp即可。这样weight就可以理解为占比了。不要给自己挖坑找麻烦,切记。我希望你永远也不要像上面一样去计算。代码可读性很重要。

2020/5/24 注:
这是我 2018 年高中毕业的假期写的,当时并没有自己搭建博客,近期我给这一系列的文章都放到我自己的博客上。
当时写这个系列文章的想法就是扎实下基础,然后能帮到更多的人就更好了,但是大学开学之后就开始了新生活,没有大块的时间去编撰,系列文章也没有再更新过。但是我其实并不想放弃这个系列,那就,有缘再见吧,拜拜ヾ(•ω•`)o