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

1.5 从布局XML到视图对象

知道activity_main.xml中的XML元素是如何转换为视图对象的吗?答案就在于MainActivity类。

在创建GeoQuiz项目的同时,向导也创建了一个名为MainActivity的Activity子类。MainActivity类文件存放在项目的app/java目录下。

继续学习之前,就app/java这个目录名问题,简单说两句:这里依然使用java作为目录名是因为Android之前仅支持Java语言。新建项目时,我们虽然选了Kotlin语言(不过Kotlin可以和Java完全互操作),但Kotlin源码默认还是放在java目录里。当然,你完全可以新建一个Kotlin目录,把Kotlin代码文件都移过去。但前提是,你要明确告诉Android Studio:源码放在新文件夹里了,请帮它们添加到项目里。大多数情况下,按语言区分管理源码文件意义不大,所以绝大多数项目接受Kotlin文件存放在java目录里。

MainActivity.kt文件应该已经在编辑器窗口打开了,如果没有,在项目工具窗口中,依次展开app/java目录与com.bignerdranch.android.geoquiz包。(注意,以灰绿色显示包名的是测试包。生产包名并未加灰。)找到并打开MainActivity.kt文件,查看其中的代码,如代码清单1-4所示。

代码清单1-4 默认MainActivity类文件(MainActivity.kt)

package com.bignerdranch.android.geoquiz

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

(是不是不明白AppCompatActivity的作用?它实际就是一个Activity子类,能为Android旧版本系统提供兼容支持。第14章会详细介绍AppCompatActivity。)

如果无法看到全部类包导入语句,请点击第一行导入语句左边的+号来显示它们。

该类文件有一个Activity函数:onCreate(Bundle?)。

activity子类的实例创建后,onCreate(Bundle?)函数会被调用。activity创建后,它需要获取并管理用户界面。要获取activity的用户界面,可以调用以下Activity函数:

Activity.setContentView(layoutResID: Int)

根据传入的布局资源ID参数,该函数生成指定布局的视图并将其放置在屏幕上。布局视图生成后,布局文件包含的部件也随之以各自的属性定义完成实例化。

资源与资源ID

布局是一种资源。资源是应用非代码形式的内容,比如图像文件、音频文件以及XML文件等。

项目的所有资源文件都存放在目录app/res的子目录下。在项目工具窗口中可以看到,activity_main.xml布局资源文件存放在res/layout/目录下。strings.xml字符串资源文件存放在res/values/目录下。

可以使用资源ID在代码中获取相应的资源。activity_main.xml布局的资源ID为R.layout.activity_main。

查看GeoQuiz应用的资源ID需要切换项目视角,你必须勇闯自动生成代码的世界——Android构建工具为你编写的代码。首先,点击Android Studio窗口顶部工具栏上的锤子按钮运行编译工具。

如图1-15所示,Android Studio默认使用Android项目视角。为让开发者专注于最常用的文件和目录,默认项目视角隐藏了Android项目的真实文件目录结构。在项目工具窗口的最上部找到下拉菜单,从Android视角切换至Project视角。Project视角会显示出当前项目的所有文件和目录。

图1-15 项目工具窗口:Android视角与Project视角

在Project视角下,逐级展开GeoQuiz目录,直至看到GeoQuiz/app/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r/,再找到项目包名以及其中的R.java文件,如图1-16所示。

图1-16 查看R.java文件

双击打开R.java文件。它是在Android项目编译过程中自动生成的,所以如该文件头部的警示所述,请不要修改该文件的内容,如代码清单1-5所示。

代码清单1-5 GeoQuiz应用当前的资源ID(R.java)

/* AUTO-GENERATED FILE. DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found. It
 * should not be modified by hand.
 */

package com.bignerdranch.android.geoquiz;

public final class R {
    public static final class anim {
        ...
    }
    ...
    public static final class id {
        ...
    }
    public static final class layout {
        ...
        public static final Int activity_main=0x7f030017;
    }
    public static final class mipmap {
        public static final Int ic_launcher=0x7f030000;
    }
    public static final class string {
        ...
        public static final Int app_name=0x7f0a0010;
        public static final Int false_button=0x7f0a0012;
        public static final Int question_text=0x7f0a0014;
        public static final Int true_button=0x7f0a0015;
    }
}

顺便要说的是,修改布局或字符串等资源后,R.java文件不会实时更新。Android Studio另外还存有一份代码编译用的R.java隐藏文件。代码清单1-5中打开的R.java文件仅在应用安装至设备或模拟器前生成,因此只有在Android Studio中点击运行应用时,它才会得到更新。

R.java文件通常比较大,代码清单1-5仅展示了部分内容。

可以看到R.layout.activity_main即来自该文件。activity_main是R的内部类layout里的一个整型常量名。

GeoQuiz应用需要的字符串同样具有资源ID。目前为止,我们还未在代码中引用过字符串,如果需要,可以使用以下函数:

setTitle(R.string.app_name)

Android为整个布局文件以及各个字符串生成资源ID,但activity_main.xml布局文件中的部件除外,因为不是所有部件都需要资源ID。在本章中,我们要在代码里与两个按钮交互,因此只需为它们生成资源ID即可。

要为部件生成资源ID,请在定义部件时为其添加android:id属性。如代码清单1-6所示,在activity_main.xml文件中,分别为两个按钮添加android:id属性(需要从布局预览模式切换至XML代码模式)。

代码清单1-6 为按钮添加资源ID(res/layout/activity_main.xml)

<LinearLayout  ... >

    <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:id="@+id/true_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/true_button" />

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

    </LinearLayout>

</LinearLayout>

注意,android:id属性值前面有一个+标志,android:text属性值则没有。这是因为我们在创建资源ID,而对字符串资源只是做引用

继续学习之前,关闭R.java文件,从Project视角切回至Android视角。本书主要使用Android视角,当然,如果你就喜欢使用Project视角,也没有问题。