文章目录
- 准备工作
- 1 LinearLayout
- 2 RelativeLayout
- 3 FrameLayout
- 4 AbsoluteLayout
- 5 TableLayout
- 6 GridLayout
- 7 ConstraintLayout
准备工作
Android中的布局方式有如下七种。
线性布局 |
|
相对布局 |
|
帧布局 |
|
绝对布局 |
|
表格布局 |
|
网格布局 |
|
约束布局 |
|
为了演示这七种布局方式,可以新建7个fragment
。
按照Basic Activaty模板新建一个项目,名为layouttest
。其中有两个fragment
,在fragment_first.xml
中插入一个LinearLayout
,由于默认为约束布局,故修改其内容为
<LinearLayout
android:layout_width="200dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</LinearLayout>
然后新建7个按钮,其id分别如下,其中button_first
是其预定义的按钮,拖到LinearLayout
的最下方。
<Button android:id="@+id/btn_linear_layout"/>
<Button android:id="@+id/btn_relative_layout"/>
<Button android:id="@+id/btn_frame_layout"/>
<Button android:id="@+id/btn_absolute_layout"/>
<Button android:id="@+id/btn_table_layout"/>
<Button android:id="@+id/btn_grid_layout"/>
<Button android:id="@+id/btn_constraint_layout"/>
<Button android:id="@+id/button_first"/>
每个按钮的text
也做对应的更改,从而得到主页如图所示。其中NEXT
为其预定义的按钮,点击之后如右图。
First Fragment | Second Fragment |
在左侧的com.example.layouttest
中新建一个fragment:右键->New->Fragment->Fragment(Blank),命名为LinearFragment
。AS除了会新建一个LinearFragment.java
之外,还会在res/layout
下新建一个fragment_linear.xml
的布局文件。
在res/navigation
文件夹中有一个nav_graph.xml
文件,这里存放着“导航”信息,点进去之后,新建一个fragment
,用以指向LinearFragment
。
<fragment
android:id="@+id/linearFragment"
android:name="com.example.layouttest.LinearFragment"
tools:layout="@layout/fragment_linear"/>
将视图变为split模式,连接FirstFragment
和linearFragment
,则FirstFragment
会添加一个action
<action
android:id="@+id/action_FirstFragment_to_linearFragment4"
app:destination="@id/linearFragment" />
然后再反过来连接linearFragment
和FirstFragment
,从而为linearFragment
添加一个action
。
最后进入FirstFragment.java
,在onViewCreated
中添加
binding.btnLinearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavHostFragment.findNavController(FirstFragment.this)
.navigate(R.id.action_FirstFragment_to_linearFragment);
}
});
其中btnLinearLayout
即按钮btn_linear_layout
,action_FirstFragment_to_linearFragment
即导航。这样我们再启动程序,点击LinearLayout
,就可以进入linearFragment
了。
点击前 | 点击后 | |
➡️ |
1 LinearLayout
所谓线性布局,就是不同组块之间在单个方向存在一定的次序。可以通过orientation
来规定沿水平方向还是竖直方向进行排列。
以下图为例,最外层是一个纵向的线性布局,内部又装入4个线性布局,其中前两个为横向的,后两个为纵向的。
第一个线性布局为横向布局,layout_height
为"50dp"
,每个Button
的长宽均为"wrap_content"
,所以并未完全填充这个LinearLayout
。
第二个线性布局也是横向布局,但每个Button
代码如下
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:text="left"/>
即声明宽度为0
的同时,分配相同的权重,所以呈现出均匀分布的特征。
第三个布局为纵向布局,其layout_width
设为"match_parent"
,所以与屏幕等宽;layout_height
设为"wrap_content"
,所以高度为三个按钮之和。
这三个按钮虽然纵向排布,但分别设置了layout_gravity
参数,如下述代码。每个Button
的宽和高均为"wrap_content"
,考虑到方便阅读,所以省略了。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/cardview_dark_background">
<Button
android:layout_gravity="left"
android:text="left Top"/>
<Button
android:layout_gravity="center"
android:text="center center"/>
<Button
android:layout_gravity="right"
android:text="bottom"/>
</LinearLayout>
第四个也为纵向布局,但其layout_width
设为"wrap_content"
,而layout_height
设为"match_parent"
,所以占据了屏幕下方剩下的所有空间,但宽度却与最宽的按钮相同。
由于新建Fragment
以及导航的操作比较雷同,所以后面的几种布局方式不再赘述其创建过程。
2 RelativeLayout
相对布局,就是根据其他组件的位置来确定自身位置,所以有一组最能体现“相对”定位的功能,即根据参考组件来确定自身位置。由于这些功能均可写为layout_xxx
,所以在下表中省略layout
在参考组件的xx方向 | 对其参考组件的xx边界 | 对齐父组件的xx边界 | |
上 |
|
|
|
下 |
|
|
|
左 |
|
|
|
右 |
|
|
|
此外,还有三个重要的绝对布尔约束
-
layout_centerInParent
:为True
时要求该组件在容器内的正中心 -
layout_centerVertical
:为True
时要求该组件在容器的垂直中心 -
layout_centerHorizontal
:为True
时要求该组件在容器的水平中心
通过相对布局,可以轻而易举地得到下面的样式
最中心的白色的按钮使用了layout_centerInParent
,其代码为
<Button
android:id="@+id/centerRL"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@color/white"
android:text="centerInParent"
android:textAllCaps="false" />
四种不同颜色的按钮均参考了centerRL
的位置,例如,红色按钮的代码为
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/centerRL"
android:background="#AA0000"
android:textAllCaps="false" />
而浮在红色上面的灰色按钮则在纵向居中的同时,还与程序的左边界对齐
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"/>
3 FrameLayout
帧布局是最简单的布局,几乎没有任何用于排版的功能,只能通过layout_gravity
来指定子控件的位置。
如下图所示,左侧的布局图可分为两个部分,分别是一个子FrameLayout
,内部填充了三个按钮;在这个子Frame外同样有三个按钮,只不过声明了layout_gravity
。
布局图 | 子frame的代码 | 采用 |
4 AbsoluteLayout
绝对布局也是一种简单的布局方式,顾名思义,即根据绝对的位置坐标进行布局,正因如此,也非常简单,对于内部的控件,只需要定义4个布局参数:
-
layout_width
:控件宽 -
layout_height
:控件高 -
layout_x
:控件左上角横坐标 -
layout_y
:控件左上角纵坐标
如果用比较新的AS,那么AbsoluteLayout
会被一个横线划掉,也就是说这个布局方式已经被废弃了。
之所以还要写出来,是因为这种布局方式非常符合我们的直觉。初学者往往会固执地认为应该存在这样一种布局方式然后全网找答案,所以这里很明确地讲一下:这个布局方式已经弃用了。
5 TableLayout
TableLayout更像是列表,因为其内部的每个组件都会占一整行,所有组件正好排成了一个列表。若想让TableLayout
更像是个表格,那么就需要用到TableRow
容器。
在TableLayout
中定义stretchColumns
可以指定TableRow
中可被拉伸的列;定义shrinkColumns
可指定TableRow
中可被收缩的列;定义collapseColumns
可以指定被隐藏的列。
在上图中,最外层是一个TableLayout
,里面定义了5个组件,其中
- 最上面
独占一行
的是一个按钮 - 绿色背景为一个
TableRow
- 红色背景为一个带有
android:stretchColumns="1"
属性的Table Layout,内部封装了两个TableRow
,可以看到第一列被拉伸了 - 黑色背景为一个带有
android:shrinkColumns="1"
的Layout,可以看到第一列被收缩了。 - 最下面的蓝色背景是隐藏了第一列的TableLayout。
6 GridLayout
此为网格布局,如果不做声明,那么网格中的控件或者按照行、或者按照列按次序排列。
如下图所示
主控件是一个GridLayout
,包含了两个子控件GridLayout
,分别是上面深色部分和下面浅色部分。
浅色部分写为
<GridLayout
android:layout_width="match_parent"
android:layout_gravity="right"
android:columnCount="4"
>
<Button android:text="1"/>
<!-- 下面直到*都是雷同的,在这里省略了 -->
<Button android:text="0"
android:layout_gravity="fill_horizontal"
android:layout_columnSpan="2"/>
<Button android:text="."/>
<Button android:text="/"/>
</GridLayout>
GridLayout
默认的方向是横向,由于声明有4列,所以当排列到第4列后会填充下一行。数字0
需要横跨两行,所以有layout_columnSpan="2"
。
上面深色部分写为
<GridLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:rowCount="3"
android:orientation="vertical"
android:background="#A0A0AA">
<TextView
android:text="0"
android:textSize="40dp"
android:layout_gravity="fill"
android:layout_columnSpan="4"
android:layout_rowSpan="2"/>
<TextView
android:text="0"
android:gravity="right"
android:layout_columnSpan="4"
android:layout_gravity="fill_horizontal"
/>
</GridLayout>
上面的TextView
横向占4格,纵向占2格,并且选择了fill
;下面的则横向占4格,纵向占1格,gravity为fill_horizontal
。
7 ConstraintLayout
ConstraintLayout
是Android中最复杂的布局方式,相比之下要比Relative
更加灵活,同时占用资源更少。所以,目前也是AS默认的布局方式。
有赖于AS日益强大,ConstraintLayout
可以十分方便地在设计界面进行拖动布局,例如下面的两个按钮,可以通过点击按钮四周的圆圈拉出一条约束线,将约束线的箭头放到用于约束的物体上,即可实现约束。
从代码的角度来看,约束布局和相对布局是十分相似的,但其常用方法要比相对布局更加精确。例如相对布局一般是layout_toLeftOf
这种格式,即某物和另一物的位置关系;而约束布局则是layout_constraintLeft_toLeftOf
,即一个组件的顶部和另一个组件的顶部对其。
由于其方法均可写为layout_constraint...
,为了增加可读性,下面省略layout_constraint
,列出一些常用的方法
layout_constraint | |
Top_toTopOf | A的顶部与B的顶部对齐 |
Top_toBottomOf | A的顶部与B的底部对齐 |
Bottom_toTopOf | A的底部与B的顶部对齐 |
Bottom_toBottomOf | A的底部与B的底部对齐 |
Left_toTopOf | A的左侧与B的顶部对 |
tLeft_toBottomOf | A的左侧与B的底部对齐 |
Left_toLeftOf | A的左边与B的左边对齐 |
Left_toRightOf | A的左边与B的右边对齐 |
Right_toTopOf | A的右对齐到B的顶部 |
Right_toBottomOf | A的右对齐到B的底部 |
Right_toLeftOf | A的右边与B的左边对 |
Right_toRightOf | A的右边与B的右边对 |
在对其之后,还可以通过layout_constraintHorizontal_bias
和layout_constraintVertical_bias
来设置控件的水平与垂直偏移比例。