本节介绍以ViewGroup为基类派生的布局管理器
Android的线性布局不会换行,当组件一个挨着一个地排列到头之后,剩下的组件将不会被显示出来
LinearLayout支持的XML属性及相关方法
XML属性 | 相关方法 | 说明 |
---|---|---|
android:baselineAligned | setBaselineAligned(boolean) | 该属性设为false,将会阻止该布局管理器与它的子元素基线对齐 |
android:divider | setDividerDrawable(Drawable) | 设置垂直布局时两个按钮之间的分隔条 |
android:gravity | setGravity(int) | 设置布局管理器内组件的对齐方式,该属性支持:top、buttom、left、right、center_vertical等等 |
android:measureWithLargestChild | setMeasureWithLargestChildEnable(boolean) | 当该属性设为true,所有带权重的子元素都会具有最大子元素的最小尺寸 |
android:orientation | setOrientation(int) | 设置布局管理器内组件的排列方式,可以设置为horizontal、vertical |
LinearLayout包含的所有子元素都受LinearLayout.LayoutParams控制,因此LinearLayout所包含的子元素可以额外指定如下元素(基本上很多布局管理器都提供了相应的LayoutParams内部类)
XML属性 | 说明 |
---|---|
android:layout_gravity | 指定该子元素在LinearLayout中的对齐方式 |
android:layout_weight | 指定该子元素在LinearLayout中所占的权重 |
layout_gravity与gravity的区别
简单的来说就是:android:gravity属性用于控制它所包含的子元素的对齐方式,而android:layout_gravity属性设置的是该子元素在父容器中的对齐方式。
定义如下的XML布局管理器
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom|center_horizontal"
>
<Button android:id="@+id/bn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bn1"
/>
<Button android:id="@+id/bn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bn2"
/>
<Button android:id="@+id/bn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bn3"
/>
<Button android:id="@+id/bn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bn4"
/>
<Button android:id="@+id/bn5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bn5"
/>
LinearLayout>
如果将上面布局文件中android:gravity="bottom|center_horizontal"改为android:gravity=“right|center_vertical”——也就是说所有组件水平右对齐、垂直居中,再次使用Activity显示该布局将看到如下图
如果将线性布局的方向改为水平,再添加一个按钮6,设置android:orientation=“horizontal”,并设置android:gravity=“top”,再次使用Activity显示该布局将看到如下图
从运行结果可以看出当采用线性布局来管理6个按钮时,6个按钮无法同时显示在一行,但LinearLayout不会换行显示多余的组件,因此看不到第六个按钮,第五个按钮被压缩。
TableLayout继承了LinearLayout,因此它的本质是线性布局管理器,TableLayout不需要明确地声明包含多少行、多少列,而是通过添加TableRow、其他组件来控制表格的行数和列数。
每次向TableLayout中添加一个TableRow,该TableRow就是一个表格行,TableRow也是一个容器,因此它也可以不断的添加其他组件,每添加一个子组件该行表格就增加一列。
如果直接向TableLayout中添加组件,俺么该组件将直接占用一行。
在表格布局中,列的宽度由该列中最宽的那个单元格决定,整个表格布局的宽度则取决于父容器的宽度(默认总是占满父容器本身),重要的是要记住,列既可以收缩也可以拉伸。
TableLayout的常用XML属性及相关方法
XML属性 | 相关方法 | 说明 |
---|---|---|
android:collapseColumns | setCollapsed(int, boolean) | 设置需要被隐藏的列的序号 |
android:shrinkColumns | setShrinkAllColumns(boolean) | 设置允许被收缩的列的序号 |
android:stretchColumns | setStretchAllColumns(boolean) | 设置允许被拉伸的列的序号 |
实例:丰富的表格布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TableLayout
android:id="@+id/TableLayout01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1"
android:stretchColumns="2"
>
<Button android:id="@+id/ok1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok1"
/>
<TableRow>
<Button android:id="@+id/ok2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok2"
/>
<Button android:id="@+id/ok3"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/ok3"
/>
<Button android:id="@+id/ok4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok4"
/>
TableRow>
TableLayout>
<TableLayout
android:id="@id/TableLayout01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:collapseColumns="1"
>
<Button android:id="@+id/ok5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok5"
/>
<TableRow>
<Button android:id="@+id/ok6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok6"
/>
<Button android:id="@+id/ok7"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/ok7"
/>
<Button android:id="@+id/ok8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok8"
/>
TableRow>
TableLayout>
<TableLayout
android:id="@id/TableLayout01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
>
<Button android:id="@+id/ok9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok9"
/>
<TableRow>
<Button android:id="@+id/ok10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok10"
/>
<Button android:id="@+id/ok11"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/ok11"
/>
<Button android:id="@+id/ok12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok12"
/>
TableRow>
<TableRow>
<Button android:id="@+id/ok13"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/ok13"
/>
<Button android:id="@+id/ok14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok14"
/>
TableRow>
TableLayout>
LinearLayout>
上面布局文件中定义了三个TableLayout,垂直分布,宽度铺满
帧布局容器为每个加入其中的组件创建一个空白的区域,每个子组件占据一帧,这些帧会根据gravity属性执行自动对齐,将组件一个一个地叠加在一起。
FrameLayout的常用XMl属性及相关方法
XML属性 | 相关方法 | 说明 |
---|---|---|
android:foreground | setForeground(Drawable) | 设置该帧布局容器的前景图像 |
android:foregroundGravity | setForegroundGravity(int) | 定义绘制前景图像的gravity属性 |
实例:霓虹灯效果
利用帧布局,将7个TextView叠加在一起,上面的覆盖下面的TextView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<TextView android:id="@+id/view01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="320dp"
android:height="320dp"
android:background="#f00"
/>
<TextView android:id="@+id/view02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="280dp"
android:height="280dp"
android:background="#0f0"
/>
<TextView android:id="@+id/view03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="240dp"
android:height="240dp"
android:background="#00f"
/>
<TextView android:id="@+id/view04"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="200dp"
android:height="200dp"
android:background="#ff0"
/>
<TextView android:id="@+id/view05"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="160dp"
android:height="160dp"
android:background="#f0f"
/>
<TextView android:id="@+id/view06"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="120dp"
android:height="120dp"
android:background="#0ff"
/>
FrameLayout>
如果考虑轮换改变上面的帧布局中6个TextView的背景色,就会看见上面的颜色渐变条不断变换,就想大街上的霓虹灯一样。下面的程序使用上面的布局,只是程序启动了一条线程来控制周期性地改变这6个TextView的背景色。
public class MainActivity extends AppCompatActivity {
private int currentColor = 0;
//定义一个颜色数组
final int[] colors = new int[]{
R.color.color1,
R.color.color2,
R.color.color3,
R.color.color4,
R.color.color5,
R.color.color6
};
final int[] names = new int[]{
R.id.view01,
R.id.view02,
R.id.view03,
R.id.view04,
R.id.view05,
R.id.view06
};
TextView[] views = new TextView[names.length];
Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
//表明消息来自本程序所发送
if (msg.what == 0x123)
{
for (int i = 0; i < names.length; i++)
{
views[i].setBackgroundResource(colors[(i+currentColor) % names.length]);
}
currentColor++;
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < names.length; i++)
{
views[i] = findViewById(names[i]);
}
//定义一个线程周期性地改变currentColor变量值
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//发送一条空消息通知系统改变六个textView组件的背景色
handler.sendEmptyMessage(0x123);
}
}, 0, 200);
}
}
上面的线程定义了一个每0.2秒执行一次的任务,该任务仅仅向Handler发送一条消息,通知它更新6个TextView的背景色。
相对布局容器内子组件的位置总是相对兄弟组件、父容器来决定的。如果A组件的位置是由B组件的位置来决定的,Android要求先定义B组件,再定义A组件。
RelativeLayout的XML属性及相关方法说明
XML属性 | 相关方法 | 说明 |
---|---|---|
android:gravity | setGravity(int) | 设置该布局容器内各子组件的对齐方式 |
android:ignoreGravity | setIgnoreGravity(int) | 设置哪个组件不受gravity属性的影响 |
为了控制该布局容器中各子组件的布局分布,RelativeLayout提供了一个内部类:RelativeLayout.LayoutParams,该类提供了大量XML属性来控制RelativeLayout布局容器中子组件的布局分布。
RelativeLayout.LayoutParams里只能设为true、false的XML属性如下表:
XML属性 | 说明 |
---|---|
android:layout_centerHorizontal | 控制该子组件是否位于布局容器的水平居中 |
android:layout_centerVertical | 控制该子组件是否位于布局容器的垂直居中 |
android:layout_centerInParent | 控制该子组件是否位于布局容器的中央位置 |
android:layout_alignParentBottom | 控制该子组件是否位于布局容器底端对齐 |
android:layout_alignParentLeft | 控制该子组件是否位于布局容器左边对齐 |
android:layout_alignParentRight | 控制该子组件是否位于布局容器右边对齐 |
android:layout_alignParentTop | 控制该子组件是否位于布局容器顶端对齐 |
RelativeLayout.LayoutParams里属性值为其他UI组件ID的XML属性如下表:
XML属性 | 说明 |
---|---|
android:layout_toRightOf | 控制该子组件位于给出ID组件的右侧 |
android:layout_toLeftOf | 控制该子组件位于给出ID组件的左侧 |
android:layout_above | 控制该子组件位于给出ID组件的上方 |
android:layout_below | 控制该子组件位于给出ID组件的下方 |
android:layout_alignTop | 控制该子组件位于给出ID组件的上边界对齐 |
android:layout_alignBottom | 控制该子组件位于给出ID组件的下边界对齐 |
android:layout_alignLeft | 控制该子组件位于给出ID组件的左边界对齐 |
android:layout_alignRight | 控制该子组件位于给出ID组件的有边界对齐 |
除此之外,RelativeLayout.LayoutParams还继承了android.view.ViewGroup.MarginLayoutParams因此RelativeLayout布局容器中每个子组件也可指定android.view.ViewGroup.MarginLayoutParams所支持的各XML属性。(2.1中讲ViewGroup时提到过,用于控制组件的页边距)
实例:梅花布局效果
可以考虑先把一个组件放在相对布局容器的中间,然后以该组件为中心,将其他组件分布在该组件的四周,即可形成“梅花布局”的效果
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView android:id="@+id/view01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ye1"
android:layout_centerInParent="true"
/>
<TextView android:id="@+id/view02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ye1"
android:layout_alignStart="@+id/view01"
android:layout_alignLeft="@+id/view01"
android:layout_above="@+id/view01"
/>
<TextView android:id="@+id/view03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ye1"
android:layout_alignStart="@+id/view01"
android:layout_alignLeft="@+id/view01"
android:layout_below="@+id/view01"
/>
<TextView android:id="@+id/view04"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ye1"
android:layout_alignTop="@+id/view01"
android:layout_toStartOf="@+id/view01"
android:layout_toLeftOf="@+id/view01"
/>
<TextView android:id="@+id/view05"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ye1"
android:layout_alignTop="@+id/view01"
android:layout_toEndOf="@+id/view01"
android:layout_toRightOf="@+id/view01"
/>
RelativeLayout>
GridLayout把整个容器划分成rows×columns个网格,每个网格可以放置一个组件。除此之外,也可以设置一个组件横跨多少列,一个组件纵跨多少行。
GridLayout有三个子类:
GridLayout.Alignment(对齐方式指定视图应放置在单元组中的位置以及它的大小。)、GridLayout.LayoutParams(与 GridLayout 的每个子项关联的布局信息。)、GridLayout.Spec(规范定义了一组单元格的水平或垂直特性。)
GridLayout的XML属性及相关方法说明
XML属性 | 相关方法 | 说明 |
---|---|---|
android:alignmentMode | setAlignmentMode(int) | 设置该布局管理器采用的对齐模式 |
android:columnCount | setColumnCount(int) | 设置该网格的列数量 |
android:columnOrderPreserved | setColumnOrderPreserved(boolean) | 设置该网格容器是否保留列序号 |
android:rowCount | setRowCount(int) | 设置该网格的行数量 |
android:rowOrderPreserved | setRowOrderPreserved(boolean) | 设置该网格容器是否保留行序号 |
android:useDefaultMargins | setUseDefaultMargins(boolean) | 设置该布局管理器是否使用默认的页边距 |
为了控制GridLayout布局容器各子组件的布局分布,GridLayout提供一个内部类:GridLayout.LayoutParams,该类提供了大量的XML属性来控制GridLayout布局容器中子组件的布局分布。
GridLayout.LayoutParams常用的XML属性及相关方法
XML属性 | 相关方法 | 说明 |
---|---|---|
android:layout_column | 设置该子组件在GridLayout的第几列 | |
android:layout_columnSpan | 设置该子组件在GridLayout横向跨几列 | |
android:layout_gravity | setGravity(int) | 设置该子组件采用何种方式占据该网格的空间 |
android:layout_row | 设置该子组件在GridLayout的第几行 | |
android:layout_rowSpan | 设置该子组件在GridLayout纵向跨几行 |
实例:计算器界面
为了实现计算器界面,可以考虑将该界面分解成一个6×4的网格,其中第一个文本框横跨4列,第二个按钮横跨4列,后面每个按钮各占一格。
下面先定义如下界面文件:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="6"
android:columnCount="4"
android:id="@+id/root"
>
<TextView android:id="@+id/view01"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_columnSpan="4"
android:textSize="90sp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:padding="5dp"
android:layout_gravity="right"
android:background="#eee"
android:textColor="#000"
android:text="0"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_columnSpan="4"
android:textSize="40sp"
android:text="清除"/>
GridLayout>
接下来使用如下java代码采用循环控制添加16个按钮。
public class MainActivity extends AppCompatActivity {
GridLayout gridLayout;
//定义16个按钮的文本
String[] chars = new String[]{
"7", "8", "9", "÷",
"4", "5", "6", "×",
"1", "2", "3", "-",
".", "0", "=", "+"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gridLayout = findViewById(R.id.root);
for (int i = 0; i < chars.length; i++)
{
Button bn = new Button(this);
bn.setText(chars[i]);
//设置该按钮的字号大小
bn.setTextSize(60);
//指定该组件所在的行
GridLayout.Spec rowSpec = GridLayout.spec(i / 4 + 2);
//指定该组件所在的列
GridLayout.Spec columnSpec = GridLayout.spec(i % 4);
GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpec, columnSpec);
//指定该组件占满父容器
params.setGravity(Gravity.FILL);
gridLayout.addView(bn, params);
}
}
}
在Android中,addView(ViewGroup view, index)在指定的index处添加一个view。
void android.view.ViewGroup.addView(View child); void android.view.ViewGroup.addView(View child, LayoutParams params); void android.view.ViewGroup.addView(View child,int index, LayoutParams params);
此处使用的是第二种方法添加View——gridLayout.addView(bn, params);
params定义为GridLayout.LayoutParams类,通过公共构造函数来构造一个新的LayoutParams实例。
由文档知有六种公共构造函数,其中有
GridLayout.LayoutParams(GridLayout.Spec rowSpec, GridLayout.Spec columnSpec);
利用
rowSpec
和columnSpec
来构造一个新的 LayoutParams 实例。GridLayout.Spec继承与Object对象
使用以下静态方法创建规范:
spec(int)
spec(int, int)
spec(int, Alignment)
spec(int, int, Alignment)