第3章 Android的Activity组件 Activity(活动)组件是Android的四大组件之一,是Android移动应用的基本组件,用 于设计移动应用界面的屏幕显示。Android移动应用往往需要多个界面,因此移动应用中 必须定义一个或多个Activity以实现界面的交互。在Android3.0以后的版本中出现的 Fragment(碎片)可以嵌入Activity的图形用户界面中。目前,采用单个Activity和多个 Fragment结合的方式已成为移动应用界面定义的首选。本章将对Activity组件以及嵌入 Activity组件的Fragment进行介绍。 3.1 Activity的创建 Activity用于创建移动界面的屏幕。它的主要作用就是实现移动界面和用户之间的交 互。Activity类用于创建和管理用户界面,一个应用程序可以有多个Activity,但在同一个 时间内只有一个Activity处于激活状态。在移动应用中,Activity之间的依赖关系低。 创建Activity时,需要完成如下几个步骤。 (1)定义Activity类。 (2)定义Activity对应的布局文件,使得布局文件成为Activity的界面定义。 (3)在配置清单文件AndroidManifest.xml中声明并配置Activity的相应属性。 1.定义Activity类 Activity必须是自己的子类或扩展子类的子类。因为Activity 的一个子类为 AppCompatActivity,它可以定义MaterialDesign风格的界面,所以在当前Android中创建 Activity一般是定义为AppComaptActivity的子类。形式如下: class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } 在MainActivity(主活动)中定义了onCreate()函数,该函数是必须定义的回调函数,它 会在系统创建Activity时被调用。在Activity中必须使用setContentView()函数来指定 Activity的界面布局。这个布局通过资源引用R.layout.activity_main来引用资源目录下的 res/layout/activity_main.xml的布局文件。布局文件(如activity_main.xml)用于定义 Activity的外观,Activity可以显示并对布局文件的界面组件进行控制,实现交互行为,如 图3-1所示。 2.创建Activity的布局文件 布局配置文件保存在res/layout目录下,是XML文件。通过配置布局的相关属性和 ·44· 图3-1 Activity和布局之间的关系示意 元素可实现布局。布局文件中所有的元素必须配置android:layout_width和android:layout_ height属性,它们分别表示元素在屏幕的宽度和高度,一般取值如下。 (1)match_parent:表示使视图扩展至父元素大小。 (2)wrap_content:表示自适应大小,将组件元素的全部内容进行显示。 当然,也可以设置尺寸大小。Activity类通过setContentView()来引用布局资源,实现 布局在Activity中的渲染和展示,代码如下: 在上述文件中,根元素是TextView,是一个文本标签。如果希望在Activity中对整个 TextView可以进行引用和处理,可以在布局文件中使用android:id="@+id/textView" 来标识资源。其中,"@ +id"表示增加一个资源id。如果希望在布局文件中引用这个 TextView,可以通过"@id/textView"实现。但是在Activity(例如MainActivity代码)中需 要通过这个TextView的资源id来引用,形式如下: val textView: TextView=findViewById(R.id.textView) 如果在模块的构建文件build.gradle中增加插件kotlin-android-extensions,形如: apply plugin: 'kotlin-android-extensions' 则可以直接在Activity中通过资源编号引用View(视图)组件: textView.text="Hello" //直接设置布局资源编号为textView 的文本为Hello 但是由于kotlin-android-extensions插件只能对Kotlin支持,目前已经被弃用。 3.在配置清单文件AndroidManifest.xml中注册Activity 要让创建的Activity能让移动应用识别和调用,就必须在AndroidManifest.xml系统 配置清单文件中进行声明和配置。AndroidManifest.xml文件保存在项目的manifests目 ·45· 录中。代码如下: 在application元素(表示移动应用)中增加下级元素activity,并通过android:name属 性指定活动的类名。在manifest元素的package属性中指定了包名,所以在activity元素 的android:name中只需要定义成 android: name=".MainActivity" 即可。在activity元素下定义了intent-filter意图过滤器。intent-filter定义的意图过滤器 非常强大,通过它可以根据显式请求启动Activity,也可以根据隐式请求启动Activity。下 面代码用于指定MainActivity(主活动),它是整个移动应用的入口活动,表示应用程序可以 显示在程序列表中。 action android: name="android.intent.action.MAIN" category android: name="android.intent.category.LAUNCHER" 注意,在Android12及以上版本中使用intent-filter元素配置时,必须时Activity增加配置 android:exported属性。 3.2 Activity和Intent 一个应用可以定义多个Activity,这些Activity可以相互切换和跳转。要实现这些交 互功能,就必须了解什么是Intent(意图)。Intent表示封装执行操作的意图,是应用程序启 动其他组件的Intent对象启动组件。这些组件可以是Activity,也可以是其他基本组件,如 Service(服务)组件、BroadcastReceiver(广播接收器)组件。从一个基本组件导航到另一个组 件。通过Intent实现组件之间的跳转和关联。Intent分为显式Intent和隐式Intent两种。 3.2.1 显式Intent 显式Intent就是在Intent()函数启动组件时,需要明确指定的激活组件名称,例如: ·46· Intent(Context packageContext, Classc) 其中,Intent()函数包含两个参数,packageContext用于提供启动Activity的上下文,c用于 指定想要启动的目标组件类。 例3-1 显式Intent的应用。在本应用中定义3 个Activity 类:MainActivity、 FirstActivity和SecondActivity。其中,MainActivity用于定义菜单,具体的菜单项包括“第 一个活动”“第二个活动”“退出”。根据菜单项,可跳转至不同的活动或退出应用。选中“第一 个活动”菜单项,可跳转到FirstActivity,选中“第二个活动”菜单项,可跳转到SecondActivity,选 中“退出”菜单项,则从移动应用中退出。 在新建移动应用模块时,因为本例涉及菜单的应用,所以需要先定义相关的资源文件。 (1)定义相关资源。 移动应用往往需要大量的字符串来配置相关的组件,因此可以将字符串统一定义在 res/values目录下的strings.xml配置文件中,通过设置string元素配置字符串。string元 素的name属性表示字符串的名字,string元素的内容表示字符串的取值,代码如下: Ch03_02 第一个活动 第二个活动 退出 在Activity中创建菜单时,必须依赖菜单资源。在res目录下创建资源目录menu,然 后在res/menu目录下创建menu.xml文件,可以直接利用编辑器在menu.xml中编辑菜单 的内容,也可以通过编辑器的Design界面来实现。在这个例题中,res/menu目录下的 menu.xml的内容如下: 在menu.xml文件中,menu元素定义菜单,item 元素定义菜单项。在菜单项中使用 android:id指定菜单的id资源编号,android:icon表示菜单的图标,android:title定义菜 单对应的文字标签。 ·47· 如果在移动应用中需要使用具体尺寸大小的相关资源,可以采用类似的方法创建 dimens.xml来保存尺寸资源。具体做法是,选中res/values目录并右击,在弹出的快捷菜 单中选中New|ValuesResourceFile选项,然后指定资源名,如dimens,单击OK 按钮,创 建dimens.xml的尺寸大小的资源文件,代码如下: 40sp 指定的尺寸采用了sp单位,表示比例无关的像素单位,常常表示字体的大小。表示布 局的尺寸往往使用dp单位,dp单位代表密度无关的像素,是基于屏幕的物理密度的抽象或 虚拟单元。 (2)定义MainActivity。MainActivity是当前应用的入口。它对应的布局文件activity _main.xml文件如下: 在MainActivity中不但利用activity_main.xml渲染生成显示的界面,而且定义菜单, 并根据菜单实现不同活动的跳转。代码如下: //模块Ch03_02 主活动的定义文件MainActivity.kt class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) }o verride fun onCreateOptionsMenu(menu: Menu?): Boolean { //定义菜单 menuInflater.inflate(R.menu.menu,menu) //引用res/menu 目录的menu.xml 生成菜单对象menu return true }o verride fun onOptionsItemSelected(item: MenuItem): Boolean { //根据选中的菜单项进行处理 when(item.itemId){ R.id.firstItem->turnTo(FirstActivity: : class.java) R.id.secondItem->turnTo(SecondActivity: : class.java) R.id.exitItem->{ ·48· AlertDialog.Builder(this).apply{ //定义对话框 title =resources.getString(R.string.app_name) setMessage("退出应用?") setPositiveButton("确定") { _, _-> exitProcess(0) } setNegativeButton("取消",null) }.create().show() } } return super.onOptionsItemSelected(item) } private fun turnTo(c: Class){ //通过显式意图跳转 val intent =Intent(MainActivity@this,c) startActivity(intent) } } 在MainActivity中定义两个回调函数onCreateOptionsMenu()和onOptionsItemSelected()分 别用于创建菜单和菜单项选择的处理。 onCreateOptionsMenu()函数采用menuInflater.inflate(R.menu.menu,menu)函数引 用菜单资源res/menu/menu.xml文件渲染菜单对象menu。 onOptionsItemSelected()函数对菜单项的id值进行判断并处理,并调用了通用的显式 Intent跳转的turnTo()函数,使得从当前的MainActivity跳转到指定类的Activity中。在 退出菜单项的处理中增加了AlertDialog对话框的显示,如果选中“确定”动作,则执行 exitProcess(0)退出整个移动应用。注意,在setPositiveButton处理部分使用了两个“_”表 示匿名的参数。如果在Lambda表达式中传递的参数没有使用,可以用“_”来替换。 turnTo()函数是自定义的函数,在这个函数中使用了泛型,即定义了类型变量T,将类 型变量传递给Class中,表示各种Class类型。在函数体中,通过创建显式Intent对 象,并启动startActivity()函数实现从MainActivity跳转到指定类的Activity中。如果调 用的是turnTo(FirstActivity::class.java)函数,则等价执行的代码如下: //创建从当前的MainActivity 跳转到FirstActivity 活动的意图对象 val intent =Intent(MainActivity@this, FirstActivity: : class.java) //在当前Activity 中启动Intent MainActivity@this.startActivity(intent) 其中,MainActivity@this表示MainActivity的当前对象。 (3)定义其他的Activity。移动模块中还定义了FirstActivity和SecondActivity,它们 的布局文件分别为activity_first.xml和activity_second.xml,均保存在res/layout目录下。 它们都定义了一个文本标签中显示字符串。布局文件类似activity_main.xml布局文件的 定义。这两个Activity的任务就是引用各自的布局文件显示界面内容。 ·49· //模块Ch03_02 第一个活动的定义文件FirstActivity.kt class FirstActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_first) } } //模块Ch03_02 第二个活动的定义文件SecondActivity.kt class SecondActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_second) } } (4)配置清单文件中注册Activity,代码如下: 在配置清单文件中,配置了FirstActivity、SecondActivity 和MainActivity。其中, MainActivity作为入口,在程序加载时首先启动和显示,如图3-2所示。 运行结果中菜单项对应的图标在下列菜单列表并没有显示。有一个编程技巧,可以将 所有的菜单项向下降一个等级。将res/menu目录下的menu.xml修改成如下形式: 这时,可以出现显示二级菜单,菜单的图标可以显示。 3.2.2 隐式Intent 隐式Intent没有明确地指定要启动哪个Activity,而是通过Android系统分析Intent, 并根据分析结果来启动对应的Activity。执行过程如图3-3所示,即某个ActivityA 通过 startActivity()函数操作某个隐式Intent调用另外的Activity。Android系统先分析应用配 置清单AndroidManifest.xml中声明的IntentFilter内容,再搜索应用中与Intent相匹配的 IntentFilter内容,若匹配成功后,则Android系统调用匹配的Activity,用ActivityB 的 onCreate()函数启动新的Activity,实现从ActivityA 跳转到ActivityB。 一般情况下,隐式Intent需要在配置清单文件AndroidManifest.xml中指定Intent Filter的action、category和data3个属性。Android系统会对IntentFilter的3个属性进 行分析和匹配,以搜索对应Activity或其他组件。 (1)action:表示该Intent所要完成的一个抽象的Activity。 (2)category:用于为action增加额外的附加类别信息。 (3)data:向action提供操作的数据。 ·51· 图3-3 通过隐式Intent通过Android系统启动活动 但是这3个属性并不是必须全部配置,可以根据具体的需要进行组合配置。例如: 例3-2 使用隐式Intent的应用实例。MainActivity可以分别调用自定义的Activity 和拨打电话。 在本应用定义两个活动: 一个是自定义的CustomedActivity;另一个表示 MainActivity,用于移动应用的入口。在MainActivity中提供了两个按钮,单击后都可以实 现调用CustomedActivity和拨打电话的功能。在系统的配置清单文件中,首先配置并注册 这些Activity。 (1)在配置清单AndroidManifest.xml中注册Activity,代码如下: ·52· (2)定义CustomedActivity,代码如下: 在上述的布局中定义了ImageView 组件,用于显示图片,通过app:srcCompat直接引 用图片res/mipmap目录下的ic_launcher图片资源。 //模块Ch03_03 定义CustomedActivity 的文件CustomedActivity.kt class CustomedActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_customed) } } (3)定义MainActivity,代码如下: