7 线性布局——LinearLayout

在上一节中,我们讲到了所有的 Layout 都是从 ViewGroup 继承而来,它可以包含若干 View 并按照指定的规则将这个 View 摆放到屏幕上。那么接下来的章节我们就来学习一下 Android 的 UI 布局,Android 原生有六大布局,分别是: LinearLayout(线性布局)、RelativeLayout(相对布局)、TableLayout(表格布局)、FrameLayout(帧布局)、AbsoluteLayout(绝对布局)、GridLayout(网格布局),我们从最简单实用的一个布局开始:LinearLayout。

1. LinearLayout 的特性

LinearLayout 继承自 ViewGroup,可以将所包含的 View 按照线性方式一个一个的排列起来,即将 View 排列成一行(水平布局)或者排列成一列(垂直布局)。LinearLayout 有一个很关键的属性:android:orientation,可以用它来设置布局的方向,默认是横向。

2. 常用设置

在编写布局代码之前,我们先来了解一下 LinearLayout 常用的设置项

2.1 基本属性:

  • id: 布局唯一 id,用来在代码中通过 findViewById 查找该布局,获取布局对象
  • layout_height: 设置布局高度,有三种可选值:
    • 具体高度(dp、px)
    • wrap_content: 布局高度由子 View 的高度而定
    • match_parent: 布局高度占满父布局(等同于 fill_parent,后者已被废弃,后文将直接使用 match_parent 替代 fill_parent)
  • layout_width: 设置布局宽度,同 layout_height
  • layout_gravity: 设置布局在其父布局中的对齐方式,有以下几种常用值:
    • top: 顶端对齐
    • bottom: 底部对齐
    • left: 居左对齐
    • right: 居右对齐
    • center: 居中对齐
      可以组合使用,比如left|top表示左上对齐
  • gravity: 设置布局内的各个 View / Viewgroup 的对齐方式,使用方法同 layout_gravity
  • background: 设置布局的背景样式,可以用图片或者颜色作为背景
  • layout_margin: 设置元素与周围其他元素的间距,类似的还可以设置单边的间距:
    • layout_marginRight
    • layout_marginTop
    • layout_marginLeft
    • layout_marginBottom

以上是大多数布局都会有的属性,在这一节讲的相对详细,后续出现可参考本节内容

2.2 特殊属性

  • orientation: 线性布局的方向,前面提到过可以用它决定内部 View 的排列方向。
  • layout_weight: 内部 View 的大小权重,这个是 LinearLayout 里很重要的一个设置,它将 LinearLayout 内部的 View 按照一定比例分配大小,具体使用后面会详细介绍
  • divider: 设置布局之间的分割线,可以通过图片指定样式
  • dividerPadding: 分割线之间的间距
  • showDividers: 设置分割线的位置,有以下可选值:
    • beginning: 在元素之前展示分割线
    • end: 在元素之后展示分割线
    • middle: 在每个元素之间展示分割线
    • none: 不展示

3. 编写垂直样式布局

线性布局分为垂直和水平布局两种方式,在使用过程中除了方向不同,其余类似。本节仅演示垂直样式,水平样式相信你能够触类旁通。
顾名思义,垂直布局就是将内部 View 从上到下依次排成一列,为了便于理解,直接上代码,在我们新建的工程中,找到“res->layout->activity_main.xml”,编写代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="Here"
        android:background="#E71B0C"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="Is"
        android:background="#E7430F"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="Imooc"
        android:background="#E6D11B"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:background="#88F10D"
        android:text="Android"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:background="#03A9F4"
        android:text="Study"/>

</LinearLayout>

直接编译,效果如下:

垂直线性布局

如图,在屏幕中有 5 个 TextView 按照垂直方向依次排成一列。注意,Layout 都是继承自 ViewGroup 的,在上一节我们说过 ViewGroup 也是 View,所以我们可以推理出 Layout 里面也是可以放 Layout 的。按照这个逻辑我们还可以在垂直布局中嵌套水平布局,比如我们希望把“Here Is”和“Android Study”这两个短语写到一排:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E71B0C"
            android:text="Here"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E7430F"
            android:text="Is"
            android:textSize="30sp" />
    </LinearLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#E6D11B"
        android:text="Imooc"
        android:textSize="30sp" />

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#88F10D"
            android:text="Android"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#03A9F4"
            android:text="Study"
            android:textSize="30sp" />
    </LinearLayout>

</LinearLayout>

直接运行:


LinearLayout嵌套使用

我们将“Here”和“Is”、“Android”和“Study”这四个 TextView 两两一组分别放到了一个水平样式的 LinearLayout 中,这样验证了,Layout 是可以嵌套使用的。

4. weight 的使用

以上是 LinearLayout 排列方式的使用,接下来讲到一个很关键的属性——weight,它可以使内部 View 按照一定的比例配置尺寸,有同学可能会有疑问,前面不是有layout_heightlayout_width用来设置尺寸吗?那它和layout_weight有什么关系,会不会有什么冲突?带着这个疑问,一起学习 weight 的用法吧
layout_weight是 LinearLayout 特有的一个属性,它很好的利用了线性布局的特点,让系统自适应的帮我们完成比例缩放。和你想的一样,它和layout_widthlayout_height密不可分,他们的相互影响,最终的尺寸有很多种计算方法。这里提供一种我认为最简单的理解:

先按照 layout_height / layout_width 的设置分配所需大小,然后剩下的空间按照 weight 的比例分配,最终加起来的尺寸就是各个 View 的最终尺寸。

关于 layout_height / layout_width 可以大致分为3种情况:

  • 高度 / 宽度设置为 0
  • 高度 / 宽度为 wrap_content
  • 高度 / 宽度为 match_parent
    以下就针对这三种情况详细说明。

4.1 设置成 0 dp(重点)

这个是最直接,最常用的设置方式,也是我们需要掌握的重中之重。如果我们将高度设置成 0 dp,那么系统就会直接使用 weight 的比值作为尺寸比例分配给各个子 View。我们直接在上面的代码中进一步修改,不考虑内嵌的 LinearLayout,对 3 个子 View 添加 weight 属性,并加上背景色方便区分:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:background="#EBA2A2"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E71B0C"
            android:text="Here"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#E7430F"
            android:text="Is"
            android:textSize="30sp" />
    </LinearLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:background="#E6D11B"
        android:text="Imooc"
        android:textSize="30sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:background="#AACCE7"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#88F10D"
            android:text="Android"
            android:textSize="30sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#03A9F4"
            android:text="Study"
            android:textSize="30sp" />
    </LinearLayout>

</LinearLayout>

效果如下:

layout_height="0dp"

可以看到,3 个子 View 的高度正好是按照 1:2:3 排列。按照上面给出的计算方法,各个View的高度是 0,所以直接就是按照比例排列。
将高度/宽度写成 0 再使用 weight 属性是最直接最简单的方法,也是最常用最重要的方法,大家今后会经常用到,务必掌握!

4.2 设置成 wrap_content

我们将上面的代码中0dp直接修改成wrap_content再编译,会发现样式好像没有变,比例貌似也是 1:2:3。注意,很多地方会解释成wrap_content也是直接按照weight比例来分配,其实这是大错特错的
我们在截屏上加上标志,仔细看看尺寸:

layout_height="wrap_content"

三个View的高度大约是 169、285、400,这个比例明显不符合 1:2:3 ,那这个比例是如何计算的呢?
我们再来回顾一下weight计算方式的定义,首先我们根据wrap_content计算高度,那么 3 个子 View 都是单行 size 相同的文字,所以本身高度一样,剩下部分按照 1:2:3 来分配。那么经过测量,单行高度是 54,我们将每个 View 的高度减去 54,得到剩余高度:

第一个View的剩余高度:169 - 54 = 115
第二个View的剩余高度:285 - 54 = 231
第三个View的剩余高度:400 - 54 = 346

这样一来,剩余的尺寸就刚好符合 1:2:3 了。

4.3 设置成 match_parent

match_parent的行为是最诡异的,但是如果理解了wrap_contentmatch_parent也就不难解释,先来看看效果,我们将代码中的wrap_content替换成match_parent再来看看效果:

layout_height="match_parent"

我们会发现第三块直接消失了,这又是为什么呢?不要慌,我们还是套用一下定义。首先假定父布局高度是 X,那么 match_parent之后每个子View的高度都是 X,这样再按照比例分割剩下的 X - 3X。所以可以得到 3 个子 View 的高度分别是:

第一个View的高度:X + 1/6 * (X-3X) = (2/3)X
第二个View的高度:X + 2/6 * (X-3X) = (1/3)X
第三个View的高度:X + 3/6 * (X-3X) = 0

经过计算,非常合理!

5. 小结

这是大家学习的第一个 Layout,所以对属性的讲解会多也更详细,大家完全不必死记硬背,在今后熟悉了之后会发现其实大部分属性都大同小异。对于 LinearLayout 还有一些其他属性,比如前门提到过的 divider 等等,这个比较简单也比较容易理解,大家完全可以作为课后练习编写代码自行测试。
其实对于 LinearLayout 有很多的局限性,比如它只能按照一行或者一列排列,如果我希望从多个方向去实现布局, LinearLayout 就显得很蹩脚了,接下来一章我们会介绍一种非常灵活的布局,拭目以待。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容