《第一行代码》读书笔记——第三章:UI开发(上)

上一章我们主要学习了四大组件中活动的基础用法, 这一章主要学习UI的开发。 前面的学习中提到Android开发视图与逻辑是分离的, 作者这里说的UI指的就是视图的开发, 狭义上指的就是xml布局文件的编写。

1.1 如何编写程序的界面

Android中编写程序的界面有多种方式, 我们可以选择拖拽控件的方式,也可以直接在xml文件中通过最基本的方式进行编写,虽然说前者编写起来比较方便和直观,但是适配性差,遇到一些复杂的界面的时候该方式难以胜任,而且不利用我们理解界面背后的实现原理,所以实际的开发中我们都不采用拖拽的方式进行编辑,而是使用最基本的方式进行编辑布局文件。

1.2 常用系统控件的使用方法

一些复杂页面其实就是一个个小的控件组合而成,所以学习界面的编写就要先从基础的知识学起。
首先,我们学习一下系统为我们提供的几种经常用控件。首先新建一个工程UiWidgetTest

1.2.1 Textview

这个控件是Android中最简单的一个控件,关于它的使用比较简单,就不编码练习了。
任何一款控件都有layout_width和layout_height这两个属性,他们分别指定了控件的宽和高。
可选的值有三种:match_parent、fill_parent、wrap_parent。

  • match_parent: 当前控件的大小和父布局的大小一样,也就是他的大小由父布局来决定
  • fill_parent: 和match_parent一样, 官方推荐使用match_parent。
  • wrap_parent: 当前控件的大小由它的内容大小了决定,它的内容有多大它就多大。

TextView其它的常用属性就是设置字体大小、颜色、内容等。
其它的一些属性:

  • gravity: 指定文字线相对于控件的对齐方式, 可用的有top、tottome、left、right、center等。 例如: 属性设置为top指文字和控件的顶部对齐。

1.2.2 Button

Button 可配值的属性和TextView差不多,这里也不编码练习了。

之前自己在写demo的时候发现了一个问题:我们在xml中Button的text属性的值明明设置为小写,但是程序运行起来后界面上显示的却全部是大写,当时自己也没有深入研究这个问题。 现在在这里看到作者对这个问题进行了解释,原来是由于系统对Button中所有的字母自动进行了大写转换。 所以我们可以在Button中添加一个属性textAllcaps,将它的值设置为false即可。

Android一些控件上显示的英文字母都被转为大写字母的原因分析及问题解决这篇博客博客通过查看源码的方式分析了为什么系统会对Button中的字母进行大小写转换。 看了这篇博客后才发现阅读源码的方式才是最好的学习方法, 有些知识点我们不仅要知其然,也要知其所以然。

在对按钮添加点击事件的时候,如果我们不想使用内部类的方式,也可以使用实现接口的方法来进行注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private View btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
btn = findViewById(R.id.button);
btn.setOnClickListener(this);
setContentView(R.layout.activity_main);
}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
break;
default:
break;
}
}
}

关于Button的其它知识点:

1.2.3 EditText

EditText控件也是用户和程序交互的另一个非常重要的控件, 它允许用户在里边输入和编辑内容,我们使用程序发送信息、聊天等操作时都不得不使用这个控件。

首先修改activity_main.xml的代码,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
...
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入内容"
/>

</LinearLayout>

这里使用hint属性指定了一段提示性文字,当我们输入任何内容的时候,这段文本就会消失。效果如图32-2

32-2-hint属性

但是从图片中可以发下一个问题, 当输入文本内容过长的时候界面会非常难看, 所以我们可以通过属性maxLines强制设置最多显示多少行。代码如下:

1
2
3
4
5
6
7
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入内容"
android:maxLines="2"
/>

重新运行程序,可以看到文本超过两行的时候会自动向上滚动,而EditText不会再自动拉伸。效果如图32-3,

32-3-maxLines属性

1.2.4 ImageView

一个页面只有文本的话看起来比较枯燥, 所以我们需要显示一些图片, 而ImageView这个控件就是用来在页面上显示图片的。
首先我们将事先准备好的两张图片img_1.jpgimg_2.jpg放入到drawable-xxxhdpi目录中。 然后修改activity_main.xml的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
...

<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/img_1"
/>

</LinearLayout>

可以看到,这里使用android:src属性给imageview指定了一张图片,由于图片的宽高都是位置的, 所以将宽高的属性都设置为wrap_content,这样就保证了不关图片的尺寸是多少,图片都可以完整的展示出来。
运行程序后,效果如图32-4所示。

32-4-imgeview的src属性

当然也可以在代码中动态修改imgeview的图片。 修改MainActivity的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private ImageView imgView;
private View btn;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
imgView = findViewById(R.id.image_view);

}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
imgView.setImageResource(R.drawable.img_2);
break;
...

通过调用imgeview的setImageResource方法, 点击按钮btton时动态将图片修改为img_2,效果如图32-5所示。

32-5-动态修改imgeview中的图片

1.2.5 ProgressBar

ProgressBar用于在界面上显示一个进度条,表示我们的应用正在加载一些数据。 我们修改activity_main.xml中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
...
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

</LinearLayout>

然后运行程序,会发现屏幕中间有一个圆形的进度条。效果如图32-6.

32-6-圆形的progressBar

当加载完数据数据的时候我们需要将其隐藏, 这里我们点击按钮将加载框隐藏掉,再点击按钮再显示加载框。 修改MainActivity的代码,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
...
private View progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
progressBar = findViewById(R.id.progress_bar);

}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
if (progressBar.getVisibility() == View.VISIBLE) {
progressBar.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}

...
}
}
}

然后运行程序,效果如32-7.

32-7-圆形的progressBar2

我们也可以修改ProgressBar的样式,上面是圆形的加载框,下面我们将它修改为水平方向。修改activity_main.xml,代码如下

1
2
3
4
5
6
7
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:max="100"
/>

将样式修改为水平方向后,我们可以在代码中动态修改进度条的进度,修改MainActivity, 代码如下。

1
2
3
4
5
6
7

case R.id.button:
int progress = progressBar.getProgress();
progress = progress + 10;
progressBar.setProgress(progress);

break;

重新运行程序,每点击一次按钮,进度条的进度在原有的基础上加10,效果如下图

32-8-水平的progressBar2

系统还提供了许多样式,自己可以尝试。

1.2.6 AlertDialog

AlertDialog是可以在当前界面弹出的一个对话框, 这个对话框覆盖在所有的元素之上,他们暂时无法与用户进行交互,一般用来向用户提示一些重要的信息。修改MainActivity的代码,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("标题");
dialog.setMessage("确定删除吗?");
dialog.setCancelable(false);

dialog.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {

}
});
dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {

}
});
dialog.show();

break;

我们首先设置dialog的标题和内容, 然后调用setPositiveButton()设置确认按钮,调用setNegativeButton()设置取消按钮,最后再调用show()将dialog显示出来。 效果如图32-9, 因为setCancelable()的参数设置为false,所以点击返回键也不可以将dialog取消。

32-9-AlertDialog

Android常用的控件还有很多,我们可以通过官方提供的文档或者别人的博客来学习。

1.3 Android的四种基本布局

上一小节我们学习了Android中的几款常用控件,这一小节来学习布局。

如果把一个页面比作一个房子的话, 控件相当于茶杯、沙发、桌子,是我们屋里的家具, 那么这些家具摆放到哪儿,如何摆放才能让家里看起来更漂亮?布局就充当这样的角色。

布局中可以摆放控件,当然它的里面也可以嵌套布局,布局和控件的关系可以用图32-9来表示。

32-10-布局和控件的关系

Android提供了四种基本的布局,我们新建一个UILayoutTest工程来学习这一小节。

1.3.1 线性布局

线性布局就是它里边的布局成线性排列,我们可以通过orientation属性将指定控件排列的方向, 有水平和竖直两个方向。
首先我们在布局文件中放三个按钮,orientation的值为vertical, 表示竖直方向排列。 activity_main.xml的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="button1"
/>

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="button2"
/>

<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="button3"
/>

</LinearLayout>

运行程序,效果如下

32-11-线性布局-竖直方向

我们将orientation的值修改为horizontal,然后再运行程序,发现三个按钮呈水平方向排列,如图32-12

32-12-线性布局-竖直方向

这里需要注意一点如果我们的按钮是水平方向排列,那么按钮的layout_width属性就不能设置为match_parent, 因为它会在水平方向占满屏幕,其它两个按钮就没有位置摆放。同理,如果我们的排列方向是数值方向,按钮的的layout_height属性就不能设置为match_parent

线性布局有一个属性叫做layout_gravity,前面我们的学习中遇到了一个gravity属性, 那么这两个属性有什么区别呢?

gravity指的是控件中的内容在控件中的对齐方式layout_gravity属性值得是控件相对于布局的对齐方式

在水平排列的情况下,我们修改activity_main.xml的布局,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:orientation="horizontal">

<Button
android:id="@+id/button1"
...
android:layout_gravity="top"

/>

<Button
android:id="@+id/button2"
...
android:layout_gravity="center_vertical"
/>

<Button
android:id="@+id/button3"
...
android:layout_gravity="bottom"
/>
</LinearLayout>

运行程序,效果如图31-13。可以看到 button1 的layout_gravity属性设置为top,它会顶部对齐布局,button3设置为bottom,它会底部对齐布局, button2设置为center_vertical,它会相对于布局竖直方向剧中对齐。

32-13-线性布局-居中对齐1

这时我们再将布局的代码作以下修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:orientation="horizontal">

<Button
android:id="@+id/button1"
...
android:layout_gravity="left"

/>

<Button
android:id="@+id/button2"
...
android:layout_gravity="center_horizontal"
/>

<Button
android:id="@+id/button3"
...
android:layout_gravity="right"
/>
</LinearLayout>

从这几个控件layout_gravity值的字面意思我们先猜一下可能会出现的结果, button1的值为left,说明控件会相对布局的左端对齐, button2会在水平方向上居中,button3会在右端,我们想象中他的对齐方式应该是下边这样

32-14-线性布局-居中对齐2

重新运行程序,如图32-15.发现实际运行和我们想想的不一样。 这是因为:如果我们排列的方式水平方向的话,只有竖直方向的对齐方式才会生效,水平方向的对齐方式如对齐屏幕的左侧、水平居中、对齐屏幕的右侧,他们都是不会生效的。 同理,如果控件的排列方向是竖直方向,那么竖直方向的对齐方式是不会生效的,例如top 、 center_vertical、bottom。

32-15-线性布局-居中对齐3

接下来我们学习线性布局中另外一个重要的属性————weight。这个控件的属性允许我们按照比例的方式来控制控件的大小。 它在手机屏幕适配性方面有非常重要的作用。例如:我们在页面上要发送一条消息,这时就需要显示一个编辑框和发送的按钮,修改activity_main.xml的代码, 如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">


<EditText
android:id="@+id/input_msg"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="48dp" />

<Button
android:id="@+id/send_msg"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="48dp"
android:textAllCaps="false"
android:text="发送"
/>
</LinearLayout>

我们分别将两个控件的高度设置为45dp,然后layout_width设置为0dp, 这时候他们会显示到屏幕吗,答案是会的,因为我们使用了layout_weight属性设置了每个控件占用的比例。原理就是系统会先将线性布局下所有的控件的layout_weight值相加,得到总值,然后每个控件的框度就是该控件的layout_weight的值除以刚才所计算的出的值。 运行程序,效果如图32-16。

32-16-线性布局-weight属性1

但是实际开发中一个发送消息的页面不可能文本输入框和发送按钮各占一半,加入我们想让文本编辑框占屏幕的4/5,按钮占1/5, 那么将EditText的layout_weigth设置为4, Button的属性设置为1即可。 重新运行程序,如图:32-17.

32-17-线性布局-weight属性2

但是上边我们是将layout_width的属性设置为了0, 如果我们再将他们都设置为了match_parent,会是什么效果呢, 运行程序,如图 32-18 。

32-18-线性布局-weight属性3

为什么改为match_parent后刚好就反过来了呢? 其实更为准确的计算方式可以参考博客关于weight属性使用的一些细节. 设置了weight属性的控件的计算方式应该是这样的(不太好理解):

1
2
3
//其中剩余空间是指屏幕宽度相对于控件总宽度剩余的空间大小

设置了weight属性的View的宽度=原有控件宽度 + 剩余空间的占比

例如这里EditText的控件的宽度应该这样计算:
1.假如屏幕的宽度为L,
2.因为EditText的宽度设置了match_parent,那么它的原有宽度为屏幕的宽度,也就是L。同理发送消息的Button原有宽度也为L
3.剩余空间:屏幕的总宽度为L, 控件的总宽度=EditText的宽度(L)+Button的宽度L = 2L.因为剩余空间是指屏幕宽度相对于控件总宽度剩余的空间大小,所以剩余空间为L-2L
4.剩余空间的占比: 因为EditText的weight设置为了4, Button设置为了1,所以剩余空间占比为(L-2L)4/5
5.根据上边的公式: EditText的宽度 = L + (L-2L)
4/5 = L*1/5 ,实际的运行效果确实EditText只占了1/5.

我们可以把`layout_width 的属性设置为wrap_content后重新运行程序,发现又是另外一种效果了, 但是计算的公式还是用的上边的那个公式。 效果如图32-19.

32-19-线性布局-weight属性4

所以谷歌官方推荐我们在设置weight属性的值的时候将对应的layout_Width或者layout_height设置为0dp。

我们在开发中也经常使用layout_weight来自动填充剩余的控件。 例如屏幕的顶部有一个固定高度的控件,底部也有一个固定高度的控件,那么想让中间这个控件填充剩余的空间。 示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/input_msg"
android:layout_width="match_parent"
android:layout_height="45dp" />

<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/colorPrimary"
android:layout_weight="1"
android:text="我是中间的一个控件"
android:textColor="@android:color/white"
android:textSize="20sp"
android:gravity="center"
/>

<Button
android:id="@+id/send_msg"
android:layout_width="match_parent"
android:layout_height="48dp"
android:textAllCaps="false"
android:text="发送"
/>
</LinearLayout>

32-20-线性布局-weight属性5

运行效果如图32-20.

需要注意的一个知识点: ScrollView嵌套LinearLayout时,同样的是上边的代码就无法自动填充剩余的空间,这时需要在ScrollView标签中将fillViewport属性设置为true。但是这时EditText会自动获取焦点,解决办法可以参考博客Android页面启动 editext获取焦点 scrollView自动上滑问题;

1.3.2 相对布局

RelativeLayout 又称为相对布局,它是相对定位的方式来让控件出现在任何位置,这里的相对所指的参照物可以是父控件也可以是和它同一层级的控件。 我们还是通过代码来体会下, 修改activity_main.xml,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button1"
android:textAllCaps="false"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
/>

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button2"
android:textAllCaps="false"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
/>

<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button3"
android:textAllCaps="false"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
/>


<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button4"
android:textAllCaps="false"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
/>


<Button
android:id="@+id/btn5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button5"
android:textAllCaps="false"
android:layout_centerInParent="true"
/>

</RelativeLayout>

上边代码中的 layout_alignParentLeft、layout_alignParentRight、layout_alignParentTop、layout_alignParentBottom、layout_centerInParent从字面的意思可以知道他们的参照物是父布局, 所以他们分表表示和父布局的左、右、上、下、中心对齐。运行效果如图32-21.

32-21-相对布局1

上边的对齐方式都是相对于父布局而言,那么如何相对于同一层级的控件进行对齐呢? 这是可以的,我们只需要将上边的 layou_alignParent***中的alignParent去掉即可, 例如和某个控件的顶部对齐,那么就可以用layout_alignTop, 在某个控件的上方可以用属性layout_above,其它的同理。同样通过代码实践一下, 修改activity_main.xml,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button1"
android:textAllCaps="false"
android:layout_above="@id/btn5"
android:layout_toLeftOf="@id/btn5"
/>

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button2"
android:textAllCaps="false"
android:layout_above="@id/btn5"
android:layout_toRightOf="@id/btn5"
/>

<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button3"
android:textAllCaps="false"
android:layout_below="@id/btn5"
android:layout_toLeftOf="@id/btn5"
/>


<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button4"
android:textAllCaps="false"
android:layout_below="@id/btn5"
android:layout_toRightOf="@id/btn5"
/>


<Button
android:id="@+id/btn5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button5"
android:textAllCaps="false"
android:layout_centerInParent="true"
/>

运行程序,效果如图32-23。

32-23-相对布局2

我们不难总结出, below指的是位于某个控件的下方,above指位于某个控件的上方, toLeftOf指位于某个控件的左边,toRightOf指位于某个控件的右方。这些都是位于某个控件的某个方向,还有一类属性可以指定和某个控件的顶部/底部/左边/右边对齐。 我们可以修改activity_main.xml的代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:text="button1"
android:textAllCaps="false"
android:layout_alignTop="@id/btn5"
android:layout_toLeftOf="@id/btn5"
/>

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:text="button2"
android:textAllCaps="false"
android:layout_alignBottom="@id/btn5"
android:layout_toRightOf="@id/btn5"
/>

<Button
android:id="@+id/btn3"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="button3"
android:textAllCaps="false"
android:layout_alignRight="@id/btn5"
android:layout_alignParentBottom="true"
/>


<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button4"
android:textAllCaps="false"
android:layout_alignParentBottom="true"
android:layout_alignRight="@id/btn2"
/>


<Button
android:id="@+id/btn5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button5"
android:textAllCaps="false"
android:layout_centerInParent="true"
/>

</RelativeLayout>

运行效果如图32-24。

32-24-相对布局3

1.3.3 帧布局

FrameLayut 称为帧布局, 它的布局方式就比较简单。因为定位不太灵活,所以用的也比较少。
它的摆放方式是:默认都是位于左上角, 后添加的位于前一个添加的上方。 我们修改activity.xml的代码,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<Button
android:id="@+id/btn1"
android:layout_width="400dp"
android:layout_height="400dp"
android:text="button1"
android:textAllCaps="false"
android:background="@color/colorPrimary"
android:textAlignment="textEnd"
/>

<Button
android:id="@+id/btn2"
android:layout_width="250dp"
android:layout_height="250dp"
android:text="button2"
android:textAllCaps="false"
android:background="@android:color/holo_red_dark"
/>

<Button
android:id="@+id/btn3"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="button3"
android:textAllCaps="false"
android:background="@android:color/holo_blue_dark"
/>

</FrameLayout>

重新运行程序,效果如图32-25.

32-25-帧布局

可以看到,后添加的button2覆盖了button1, 后添加的button3覆盖了button2.

1.3.4 百分比布局

前面学习的三种布局在android10中开始支持, 基本上可以满足大部分的场景。 但是只有前面学到的线性布局LinearLayout支持按照比例来指定控件大小, 都不支持按照一定的比例来摆放控件,
例如我们想用RelativeLayout来实现两个按钮平分布局就比较困难,所以Anroid后来引入了一个新的布局来解决此问题——百分比布局,不过现在这个布局的api也已经被启用了,所以我们简单了解以下即可。
百分比布局PercentFrameLayoutPercentRelativeLayout分别继承FrameLayout和Relativelayout,所以他们是这两种布局的扩展。

在使用之前,我们首先需要在gradle文件中添加该库的依赖 。

修改app/build.gradle文件的dependencies如下:

1
implementation 'com.android.support:percent:28.0.0'

然后修改activity_main.xml, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<androidx.percentlayout.widget.PercentFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/btn1"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
android:text="button1"
android:textAllCaps="false"
android:background="@color/colorPrimary"

/>


<Button
android:id="@+id/btn2"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
android:layout_gravity="right|top"
android:text="button2"
android:textAllCaps="false"
android:background="@android:color/holo_red_dark" />

<Button
android:id="@+id/btn3"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
android:layout_gravity="left|bottom"
android:text="button3"
android:textAllCaps="false"
android:background="@android:color/holo_blue_dark" />

<Button
android:id="@+id/btn4"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
android:layout_gravity="right|bottom"
android:text="button4"
android:textAllCaps="false"
android:background="@android:color/holo_green_dark" />

</androidx.percentlayout.widget.PercentFrameLayout>

重新运行程序,效果如图32-26

32-26-百分比布局1

我们将上图的50%修改为30%后,再运行程序,效果入 32-27 。

32-27-百分比布局2

PercentRelativeLayout的用法和Relativelayout的用法类似,只不过是将其中的layout_width和layout_height属性换为layout_widthPercentlayout_heightPercent

除了上边的布局,还有一些不常用的布局,例如AbsoluteLayoutTablelayout等,不要求我们会用,但还是需要我们作以了解,因为一些老的代码中还会出现这些布局。

1.4 小结

这一篇博客主要学习了Android中的一些常用的控件,例如TextView、Button、EditText、ProgressBar、AlertDialog, 接着又学习了Android的四种基本布局(LinearLayout、RelativeLayout、FrameLayout、百分比布局)。 这些都是一些基础的,相对比较简单的控件, 下一篇博客学习如何自定义控件和使用一些复杂的列表控件。

参考书籍《第一行代码》