Android编程权威指南(第4版)
上QQ阅读APP看书,第一时间看更新

1.4 用户界面设计

点击activity_main.xml布局文件页,会在编辑工具窗口打开布局编辑器,如图1-9所示。如果看不到布局文件,请在项目工具窗口展开app/res/layout/找到它并双击打开。如果看到的是activity_main.xml文件的XML代码,请点击底部的Design页,切换显示布局预览。

图1-9 布局编辑器

按照约定,布局文件的命名基于其关联的activity:activity_作为前缀,activity子类名的其余部分全部转小写并紧随其后,单词之间以下划线隔开。例如,当前新建项目的布局文件名为activity_main.xml,或者说你有个activity名为SplashScreenActivity,那么对应的布局就命名为activity_splash_screen。对于后续章节中的所有布局以及将要学习的其他资源,都建议采用这种命名风格。

布局编辑器展示的是文件的图形化预览界面,你可以点击底部的Text页切换显示布局的XML代码。

当前,activity_main.xml文件定义了默认的activity布局。默认的XML布局文件内容经常有变,但相比代码清单1-1,一般不会有很大出入。

代码清单1-1 默认的activity布局(res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

应用activity的默认布局定义了两个视图(view):ConstraintLayout和TextView。

视图是用户界面的构造模块。显示在屏幕上的一切都是视图。用户能看到并与之交互的视图称为部件(widget)。有些部件可以用来显示文字或图像,有些部件(比如按钮)可以点击以触发事件任务。

Android SDK内置了多种部件,通过配置各种部件可获得应用所需的外观及行为。每一个部件都是View类或其子类(比如TextView或Button)的一个具体实例。

我们得想办法告诉部件它们在屏幕上该位于哪里。ViewGroup就是这样一种特殊的View,它包含并布置其他视图。ViewGroup视图本身不显示内容,它规划其他视图内容应该显示在哪里。ViewGroup通常又称为布局。

在当前默认布局里,ConstraintLayout这个ViewGroup布置了一个TextView部件,这是它唯一的子部件。有关布局和部件的知识,以及如何使用ConstraintLayout,第10章将详述。

图1-10展示了代码清单1-1中定义的ConstraintLayout和TextView是如何在屏幕上显示的。

图1-10 显示在屏幕上的默认视图

不过,图1-10所示的默认部件并不是我们需要的,MainActivity的用户界面需要以下五个部件:

  • 一个垂直LinearLayout部件;
  • 一个TextView部件;
  • 一个水平LinearLayout部件;
  • 两个Button部件。

图1-11展示了以上部件是如何构成MainActivity用户界面的。

图1-11 布置并显示在屏幕上的部件

下面我们在布局XML文件中定义这些部件。对照代码清单1-2,修改activity_main.xml文件内容。注意,需删除的XML代码已打上删除线,需添加的XML以粗体显示。本书统一使用这样的代码增删处理模式。

代码清单1-2 在XML文件中定义部件(res/layout/activity_main.xml)

<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="@string/question_text" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

      <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/true_button" />

      <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/false_button" />

    </LinearLayout>

</LinearLayout>

参照代码清单输入代码,暂时不理解这些代码也没关系,你会在后续学习中逐渐弄明白的。注意,开发工具无法校验布局XML内容,拼写错误早晚会出问题,应尽量避免。

可以看到,有三行以android:text开头的代码出现了错误信息。暂时忽略它们,稍后会处理。

对照图1-11所示的用户界面查看XML文件,可以看出部件与XML元素一一对应。元素名称就是部件的类型。

各元素均有一组XML属性。属性可以看作关于如何配置部件的指令。

为方便理解元素与属性的工作原理,接下来我们将以层级视角来研究布局。

1.4.1 视图层级结构

部件包含在视图对象的层级结构中,这种结构又称作视图层级结构(view hierarchy)。图1-12展示了代码清单1-2所示的XML布局对应的视图层级结构。

图1-12 布局部件的层级结构

从布局的视图层级结构可以看到,其根元素是一个LinearLayout部件。作为根元素,LinearLayout部件必须指定Android XML资源文件的命名空间属性。

LinearLayout部件继承自ViewGroup部件(也是一个View子类)。ViewGroup部件是包含并布置其他视图的特殊视图。想要以一列或一排的样式布置部件,就可以使用LinearLayout部件。其他ViewGroup子类还有ConstraintLayout和FrameLayout。

如果某个视图包含在一个ViewGroup中,该视图与ViewGroup即构成父子关系。根LinearLayout有两个子部件:TextView和另一个LinearLayout。作为子部件的LinearLayout自己还有两个Button子部件。

1.4.2 部件属性

下面来看看配置部件时常用的一些属性。

  1. android:layout_width和android:layout_height属性

    几乎每类部件都需要android:layout_width和android:layout_height属性。以下是它们的两个常见属性值(二选一)。

    • match_parent:视图与其父视图大小相同。
    • wrap_content:视图将根据其显示内容自动调整大小。

    根LinearLayout部件的高度与宽度属性值均为match_parent。LinearLayout虽然是根元素,但它也有父视图——Android提供该父视图来容纳应用的整个视图层级结构。

    其他包含在界面布局中的部件,其高度与宽度属性值均被设置为wrap_content。请参照图1-11理解该属性值定义尺寸大小的作用。

    TextView部件比其包含的文字内容区域稍大一些,这主要是android:padding="24dp"(dp即density-independent pixel,指与密度无关的像素,详见第10章)属性的作用。该属性告诉部件在决定大小时,除内容本身外,还需增加额外指定量的空间。这样屏幕上显示的问题与按钮之间便会留有一定的空间,使整体显得更为美观。

  2. android:orientation属性

    android:orientation属性是两个LinearLayout部件都具有的属性,它决定两者的子部件是水平放置还是垂直放置。根LinearLayout是垂直的,子LinearLayout是水平的。

    子部件的定义顺序决定其在屏幕上显示的顺序。在垂直的LinearLayout中,第一个定义的子部件出现在屏幕的最上端;而在水平的LinearLayout中,第一个定义的子部件出现在屏幕的最左端。(如果设备文字从右至左显示,比如阿拉伯语或者希伯来语,则第一个定义的子部件出现在屏幕的最右端。)

  3. android:text属性

    TextView与Button部件具有android:text属性。该属性指定部件要显示的文字内容。

    请注意,android:text属性值不是字符串值,而是以@string/语法形式对字符串资源(string resource)的引用。

    字符串资源包含在一个独立的名叫strings的XML文件中(strings.xml),虽然可以硬编码设置部件的文本属性值,比如android:text="True",但这通常不是个好办法。比较好的做法是将文字内容放置在独立的字符串资源XML文件中,然后引用它们。这样会方便应用的本地化(详见第17章)。

    需要在activity_main.xml文件中引用的字符串资源还没添加,现在就来处理。

1.4.3 创建字符串资源

每个项目都包含一个默认字符串资源文件res/values/strings.xml。

打开res/values/strings.xml文件,可以看到,项目模板已经添加了一个字符串资源。如代码清单1-3所示,添加应用布局需要的三个新字符串。

代码清单1-3 添加字符串资源(res/values/strings.xml)

<resources>
    <string name="app_name">GeoQuiz</string>
    <string name="question_text">Canberra is the capital of Australia.</string>
    <string name="true_button">True</string>
    <string name="false_button">False</string>
</resources>

(Android Studio某些版本的strings.xml默认带有其他字符串,这些字符串可能与其他文件有关联,请勿随意删除。)

现在,在GeoQuiz项目的任何XML文件中,只要引用到@string/false_button,应用运行时,就会得到“False”文本。

保存strings.xml文件。这时,activity_main.xml布局缺少字符串资源的提示信息应该消失了。(如仍有错误提示,请检查一下这两个文件,确认没有拼写错误。)

默认的字符串文件虽然已命名为strings.xml,但你仍可以按个人喜好重新命名。一个项目也可以有多个字符串文件。只要这些文件都放在res/values/目录下,含有一个resources根元素,以及多个string子元素,应用就能找到并正确使用它们。

1.4.4 预览布局

至此,应用的界面布局已经完成,可以使用图形布局工具实时预览了。回到activity_main.xml文件,在编辑器工具窗口的底部点击Design页进行布局预览,结果如图1-13所示。

图1-13 在Design页预览activity_main.xml布局

图1-13展示了两种布局预览模式。在工具栏左上角,有个钻石按钮,我们可以通过它的下拉菜单切换显示不同的布局预览模式——设计(Design)预览或蓝图(Blueprint)预览,或者并排显示设计预览和蓝图预览。

在图1-13中,左边是设计预览模式,用来展示布局在设备上的效果,也包括主题样式;右边是蓝图预览模式,用来展示部件的尺寸以及它们之间的位置关系。

在设计预览模式下,你还可以查看布局在不同的设备配置下的样子。通过预览窗口上方的面板,可以指定设备类型、Android模拟器版本、设备主题以及设备使用区域,查看布局的不同渲染结果。你甚至可以模拟某个语言区域的自右到左的文字显示模式。

除了预览,你也可以直接使用布局编辑器摆放部件,布置布局。如图1-14所示,项目窗口左边有个面板,包括了Android所有的内置部件。你可以将它们从面板拖曳到视图上,或者拖到左下方的部件树上,更精准地控制如何摆放部件。

图1-14 图形化布局编辑器

图1-14展示了带布局装饰(layout decoration)的布局预览。这些装饰元素有设备状态栏、带GeoQuiz标签的应用栏,以及虚拟设备按钮栏。要添加这些装饰,点击预览窗口上方工具栏中的眼睛图标,选择Show Layout Decorations菜单项即可。

图形化布局编辑器非常有用,尤其是在使用ConstraintLayout时,后面学习第10章内容时,你将有所体会。