这节课是 Android 开发(入门)课程 的第一部分《布局和交互》的第二节课,导师依然是 Katherine Kuan 和 Kunal Chawla,主要内容是应用 Android 学习方法与 ViewGroups。
关键词:ViewGroups,LinearLayout 样式,RelativeLayout 样式,stack overflow,布局权重 layout_weight,Views ID,内边距 padding,外边距 layout_margin
应用 Android 学习方法
- 课程介绍了 XML 可视化编辑器 (XMLV,XML Visualizer),这个工具可以直接在网页显示 XML 的显示效果,不过根据我的尝试,XMLV 存在 bugs,会出现代码正确而仿真结果不正确的情况;同时它仅支持一部分 XML 代码,所以尽量不要尝试课程以外的代码。
- 通过 Android 文档找到帮助
善用 Google 搜索和浏览器的查找 (ctrl/cmd+F) 功能可以通过 Android 文档找到帮助。例如要弄懂android:orientation
这个属性,可以 Google 搜索 "android orientation",点击 "LinearLayout | Android Developers" 直接跳到 LinearLayout 的页面,再用浏览器查找 "orientation" 就可以找到说明文档。 - 通过论坛找到帮助
遇到问题时可以直接将问题用 Google 搜索,也许有不少问答社区或论坛已经有相应的讨论。例如要知道如何将三个 TextView 在屏幕上平均分布,可以 Google 搜索 "linearlayout equally spaced children",第一个结果就是 stack overflow 问答社区的相应问题讨论,找到回答中的关键字 "layout_weight",再通过 Android 文档找到说明,看是否能解决问题。如果论坛中没有讨论,也可以尝试第一个提出问题。 - 学习阅读 Android 文档
首次阅读 Android 文档时可能会对其中的术语或概念不熟悉,这时可通过词典或 Google 搜索查询学习,争取每次回头再看时能有越来越深入的理解。 - 仔细阅读代码,想象其在设备上的结果。
- 练习,通过练习能找到知识点的理解漏洞或偏差。
ViewGroups 的使用
ViewGroups也是一种 Views,拥有 Views 的属性,如矩形、边界不可见,宽度、高度、背景色等。ViewGroups 作为根 Views (Root Views) 包含了其他 Views,并将不同的 Views 摆放在屏幕上的不同位置。
按家谱描绘方式 (the Family Language),ViewGroups 被称为父 Views (the Parent Views),其他 Views 被称为子 Views (the Child Views),子 Views 之间可互称兄弟 Views (the Sibling Views)。
课程 1B 介绍了 RelativeLayout 和 LinearLayout 两种 ViewGroups。
首先来看 LinearLayout,他可以将子 Views 垂直排列 (Vertical Column) 或水平摆放 (Horizontal Row)。以下是一段 LinearLayout 代码。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F44336">
<TextView
android:text="Hi!There."
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="34sp"
android:background="#2196F3"/>
<TextView
android:text="This is Tom."
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2"
android:textSize="34sp"
android:background="#9C27B0"/>
<TextView
android:text="Nice to meet you."
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="3"
android:textSize="34sp"
android:background="#4CAF50"/>
</LinearLayout>
在 XMLV 的仿真效果如下图。
浏览整段代码,第一行和最后一行是 LinearLayout 的单独标签,它包含了三个 TextView。
阅读第一句:
xmlns:android="http://schemas.android.com/apk/res/android"
该属性特指给 Android 的 URL 速记,指定以android:
开头的属性都属于 Android。这被称为 XML 命名空间声明 (XML NameSpace Declaration),xmlns 即 XML NameSpace 的缩写。XML 命名空间声明可自定义。有了不同的前缀,属性名相同时,XML 也不会产生冲突。阅读第二句:
android:orientation="vertical"
该属性定义了 LinearLayout 对子 Views 的布局方式,实际值可以是垂直 (vertical) 或水平 (horizontal)。阅读第三句和第四句:
android:layout_width="match_parent"
android:layout_height="match_parent"
这两个属性分别定义了 LinearLayout 的宽度和高度,实际值由指定 dp 值和 "warp_content"
之外出现了新值 "match_parent"
,对于 LinearLayout 这种根 Views 而言就将 LinearLayout 变成与设备屏幕等宽等高。
阅读第五句:
android:background="#F44336"
该属性定义了 LinearLayout 的背景色,结合第三句和第四句证明了 LinearLayout 也是一种 Views,拥有 Views 的属性。观察第一个 TextView,其中第二句
android:layout_width="match_parent"
也使用了"match_parent"
,对于这种子 Views 而言就将 TextView 变成与 LinearLayout 这种父 Views 等宽。观察三个 TextView,发现
android:layout_height=“0dp”
和android:layout_weight
属性成对出现,这是将父 Views 中的子 Views 根据权重布局。
(1)android:layout_height="0dp"
将子 Views 的初始高度设置为 0,注意不要忘记单位 dp。
(2)android:layout_weight
该属性定义了子 Views 的布局权重,真实值可以是任意自然数,默认为0,数字越大,权重越大。例如上面三个 TextView 中,权重分别是 1、2、3,仿真图中他们所占的高度分别是 1/6、2/6、3/6。父 Views 中只有一个子 Views 指定了权重时数值大小无所谓,但习惯为1。
阅读所有代码后,可总结出属性名有两种形式:
(1)单段式:text、textSize、background 这一类属性由子 Views 处理;
(2)两段式:layout_width、layout_height、layout_weight 这一类属性属于 layout 属性,由父 Views 使用来确定子 Views 的参数。
例如上述的 layout_weight 在 TextView 中并未指定是宽度的权重还是高度的,这是因为 layout_weight 是由父 Views (LinearLayout) 使用的,若 LinearLayout 对子 Views 的布局方式 (orientation) 是垂直 (vertical),那么对子 Views 进行高度上的权重布局;若为水平 (horizontal) 则对子 Views 进行宽度上的权重布局。
接下来看 RelativeLayout,它可以将子 Views 相对父 Views 摆放(上下左右中)和相对兄弟 Views(上下左右)摆放。以下是一段 RelativeLayout 代码。
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F44336">
<TextView
android:text="This is Tom."
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="34sp"
android:background="#2196F3"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:id="@+id/textview0"/>
<TextView
android:text="Hi!There."
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="34sp"
android:background="#9C27B0"
android:layout_toLeftOf="@id/textview0"/>
</RelativeLayout>
在 XMLV 的仿真效果如下图。
浏览整段代码,第一行和最后一行是 RelativeLayout 的单独标签,他包含了两个 TextView。与 LinearLayout 相比,RelativeLayout 没有布局方式 (orientation) 这个属性。
- 观察第一个 TextView,它相对父 Views 放置,属性有
相对位置 | 属性名 |
---|---|
上 | android:layout_alignParentTop |
下 | android:layout_alignParentBottom |
左 | android:layout_alignParentLeft |
右 | android:layout_alignParentRight |
水平居中 | android:layout_alignParentHorizontal |
垂直居中 | android:layout_alignParentVertical |
真实值为真 (true) 或假 (false),默认位置在父 Views 的左上角。属性可组合使用,如第一个 TextView 放置在 RelativeLayout 的右上角。
- 观察第二个 TextView,他相对第一个 TextView 这个兄弟 Views 放置,属性有
相对位置 | 属性名 |
---|---|
上 | android:layout_toLeftOf |
下 | android:layout_toRightOf |
左 | android:layout_above |
右 | android:layout_below |
真实值为兄弟 Views 的 id,这就需要首先为其声明 id。如第一个 TextView 中 android:id="@+id/textview0"
就是给其取 Views ID,其中
(1)@: 表示 Android App 资源;
(2)+: 表示首次声明;
(3)id: 表示资源类型;
(4)textview0: Views ID,不能有空格,不能以符号开头(数字可以),应保持唯一性。
android:layout_toLeftOf="@id/textview0"
第二个 TextView 放在第一个 TextView 的左边,属性的真实值为第一个 TextView 的 ID,其中 @ 与 id 之间没有 +。第一个 TextView 相对于第二个 TextView 而言也称为锚定 Views (the Anchor Views)。
Views 视觉润色
介绍了 RelativeLayout 和 LinearLayout 两类 ViewGroups 后,课程 1B 还简要介绍了为 Views 视觉润色的方法,即在 Views 之间添上空间,利用 padding 或 margin 属性。以下是一段 padding 和 margin 的代码。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#F44336">
<TextView
android:text="Hi there!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="45sp"
android:background="#009688"
android:paddingTop="16dp"
android:paddingLeft="32dp"
android:paddingBottom="16dp"
android:paddingRight="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="32dp"/>
</LinearLayout>
在 XMLV 的仿真效果如下图。
margin 属性会使子 Views 距离父 Views 中的指定位置有间隔,不会改变子 Views。属性有
android:layout_margin
,或分成上下左右四个方向,android:layout_marginLeft
、android:layout_marginRight
、android:layout_marginTop
、android:layout_marginBottom
,可组合使用。注意 margin 属于 layout 属性,由父 Views 处理。真实值默认为 0。
如上面的 TextView 距离其在 LinearLayout 中默认左上角位置左边和上边分别距离 32dp。padding 属性会使 Views 内的内容距离指定位置有间隔,例如上述 TextView 内的文字距离 TextView 上下边界有 16dp、左右边界有 32dp。属性有
android:padding
,或分成上下左右四个方向,android:paddingTop
、android:paddingBottom
、android:paddingLeft
、android:paddingRight
,可组合使用。真实值默认为0。padding 和 margin 的真实值设置一般为 8dp 的倍数,具体可参考 Material Design 。