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视角,也没有问题。