2.2 第1组UI组件:布局管理器

2.2 第1组UI组件:布局管理器

本节介绍以ViewGroup为基类派生的布局管理器

  • 意义:为了让组件在不同的手机屏幕上都能运行良好,适应不同手机屏幕的分辨率、尺寸。
  • 下图给出了Android布局管理器的类图,由图可知所有布局管理器为ViewGroup的子类,都可作为容器类使用,因此可以调用多个重载的addView()向布局管理器中添加组件。我们完全可以用一个布局管理器嵌套到其他布局管理器中——因为布局管理器继承了View,也可以作为普通UI组件使用。

2.2 第1组UI组件:布局管理器_第1张图片

2.2.1 线性布局 LinearLayout

  • 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>

2.2 第1组UI组件:布局管理器_第2张图片

如果将上面布局文件中android:gravity="bottom|center_horizontal"改为android:gravity=“right|center_vertical”——也就是说所有组件水平右对齐、垂直居中,再次使用Activity显示该布局将看到如下图

2.2 第1组UI组件:布局管理器_第3张图片

如果将线性布局的方向改为水平,再添加一个按钮6,设置android:orientation=“horizontal”,并设置android:gravity=“top”,再次使用Activity显示该布局将看到如下图

2.2 第1组UI组件:布局管理器_第4张图片

从运行结果可以看出当采用线性布局来管理6个按钮时,6个按钮无法同时显示在一行,但LinearLayout不会换行显示多余的组件,因此看不到第六个按钮,第五个按钮被压缩。

2.2.2 表格布局 TableLayout

TableLayout继承了LinearLayout,因此它的本质是线性布局管理器,TableLayout不需要明确地声明包含多少行、多少列,而是通过添加TableRow、其他组件来控制表格的行数和列数。

  1. 每次向TableLayout中添加一个TableRow,该TableRow就是一个表格行,TableRow也是一个容器,因此它也可以不断的添加其他组件,每添加一个子组件该行表格就增加一列。

  2. 如果直接向TableLayout中添加组件,俺么该组件将直接占用一行。

  3. 在表格布局中,列的宽度由该列中最宽的那个单元格决定,整个表格布局的宽度则取决于父容器的宽度(默认总是占满父容器本身),重要的是要记住,列既可以收缩也可以拉伸。

    ​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,垂直分布,宽度铺满

  • 第一个TableLayout,指定第2列允许收缩,第3列允许拉伸
    • 第一行不使用TableRow,直接添加Button占用整行
    • 第二行先添加一个TableRow,并为TableRow添加三个Button
  • 第二个TableLayout,指定第2列被隐藏
    • 第一行不使用TableRow,直接添加Button占用整行
    • 第二行先添加一个TableRow,并为TableRow添加三个Button,由于添加了android:collapseColumns=“1”,中间的按钮会被隐藏
  • 第三个TableLayout,指定第2列和第3列允许拉伸
    • 第一行不使用TableRow,直接添加Button占用整行
    • 第二行先添加一个TableRow,并为TableRow添加三个Button
    • 第三行也先添加一个TableRow,并为TableRow添加两个按钮

2.2 第1组UI组件:布局管理器_第5张图片

2.2.3 帧布局 FrameLayout

帧布局容器为每个加入其中的组件创建一个空白的区域,每个子组件占据一帧,这些帧会根据gravity属性执行自动对齐,将组件一个一个地叠加在一起。

​ FrameLayout的常用XMl属性及相关方法

XML属性 相关方法 说明
android:foreground setForeground(Drawable) 设置该帧布局容器的前景图像
android:foregroundGravity setForegroundGravity(int) 定义绘制前景图像的gravity属性
  • FrameLayout包含的子元素也受FrameLayout.LayoutParams控制,因此它所包含的子元素也可指定android:layout_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的背景色。

2.2 第1组UI组件:布局管理器_第6张图片

2.2.4 相对布局 RelativeLayout

相对布局容器内子组件的位置总是相对兄弟组件、父容器来决定的。如果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>
    

2.2 第1组UI组件:布局管理器_第7张图片

2.2.5 网格布局 GridLayout

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列,后面每个按钮各占一格。

  1. 在布局管理器中定义一个GridLayout,并在该GridLayout中依次定义文本框、按钮,各横跨4列。
  2. 在java代码中循环16次,依次添加16个按钮。

下面先定义如下界面文件:


<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)

你可能感兴趣的:(Android,android)