目录
5、使用 Intent 在 Activity 之间穿梭
Intent:Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同的组件传递数据。Intent一般用于启动Activity,启动Service以及发送广播等场景。Intent有多个构造函数的重载。Intent大致可以分为两种:显示Intent和隐式Intent。
(1)使用显式 Intent
① 右击com.example.activitytest包 -> New -> Activity -> Empty Activity -> 命名SecondActivity -> 勾选Generate Layout File -> 给布局文件命名,但不要勾选Launcher Activity选项
② 修改布局文件 activity_second.xml 中的代码,定义一个按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 2" />
</LinearLayout>
③ 任何一个 Activity 都是需要在 AndroidMainfest.xml 中注册的,打开 AndroidMainfest.xml ,Android Studio 已经帮我们自动完成了。因为 SecondActivity 不是主 Activity,因此不需要配置<intent-filter>标签里的内容。
<activity
android:name=".SecondActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
android:exported="true"
android:label="This is MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
SecondActivity自动添加代码,在Activity加载布局,SetContentView(R.layout.activity_second),AndroidMainfest.xml自动注册布局。
④ 修改 SecondActivity中按钮的点击事件,点击按钮就会跳转到 SecondActivity
button1.setOnClickListener {
Toast.makeText(this,"You clicked Button 1",Toast.LENGTH_SHORT).show()
// 显式Intent
//第一个参数:启动Activity的上下文
//第二个参数,指定想要启动的目标Activity
val intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
}
⑤ 运行,点击 Button 1,跳转到 SecondActivity,按一下 Back 键就可以销毁当前 Activity,从而回到上一个 Activity。
(2)使用隐式Intent
它并不明确指出想要启动哪一个Activity,而是指定了一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并且班我们找出合适的Activity(可以响应这个隐式Intent的Activity)去启动。
通过在<activity>标签下配置<intent-filter>内容,可能指定当前Activity能够响应的action和category,打开AndroidMainfest.xml,添加下列代码。只有<action>和<category>中的内容同时匹配Intent中指定的action和category时,这个Activity才能响应该Intent。
<activity
android:name=".SecondActivity"
android:exported="false" >
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
button1.setOnClickListener {
val intent = Intent("com.example.activitytest.ACTION_START")
startActivity(intent)
}
android.intent.category.DEFAULT 是一种默认的 category,在调用startActivity( )方法的时候就会自动将这个category 添加到 Intent 中。每个Intent 中只能指定一个action,但能指定多个category。
再增加一个 category:
button1.setOnClickListener {
val intent = Intent("com.example.activitytest.ACTION_START")
// 调用Intent中的 addCategory() 方法来添加一个自定义的 category
intent.addCategory("com.example.activitytest.MY_CATEGORY")
startActivity(intent)
}
修改AndroidMainfest.xml
<activity
android:name=".SecondActivity"
android:exported="false" >
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.MY_CATEGORY"/>
</intent-filter>
</activity>
重新运行程序,MainActivity 的界面点击一下按钮,就能成功启动 SecondActivity了。
(3)更多隐式 Intent 的用法
使用隐式 Intent ,不仅可以启动自己程序内的 Activity,还可以启动其他程序的 Activity,使多个应用程序之间的功能共享成为了可能。
比如,调用系统的浏览器打开需要的网页:
button1.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
}
指定 Intent 的 action 是 Intent.ACTION_VIEW,这是一个 Android 系统内置的动作,其常量值为 android.intent.action.VIEW。然后通过 Uri.parse( ) 方法将一个网址字符串解析成一个Uri 对象, 再调用 Intent 的 setData( ) 方法将这个 Uri 对象传递进去。
我们还可以在<intent-filter>标签中再配置一个<data>标签,用于更精确地指定当前 Activity 能够响应的数据。 <data>标签中主要可以配置以下内容。
- android:scheme。用于指定数据的协议部分,如上例中的 https 部分。
- android:host。用于指定数据的主机名部分,如上例中的 www.baidu.com
- android:port。用于指定数据的端口部分,一般紧随在主机名之后。
- android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容,
- android:mimeType。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
只有当<data>标签中指定的内容和 Intent 中携带的 Data 完全一致时,当前 Activity 才能够响应该 Intent。在<data>标签中一般不会指定过多的内容。
比如打开网页只需要指定 android:scheme 为 https,就可以响应所以 https 协议的 Intent。
① 创建 ThirdActivity,给布局文件命名为 third_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ThirdActivity">
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 3"/>
</LinearLayout>
② 在 AndroidManifest.xml 中修改 ThirdActivity 的注册信息
<activity
android:name=".ThirdActivity"
android:exported="false"
tools:ignore="AppLinkUrlError">
<intent-filter >
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
我们通过android:scheme 指定了数据的协议必须是 https 协议,这样 ThirdActivity 应该就和浏览器一样,能够响应一个打开网页的 Intent。
另外由于 Android Studio 认为所有能够响应 ACTION_VIEW 的 Activity 都应该加上 BROWSABLE 的 category ,否则就会给出一段警告提醒。加上 BROWSABLE 的 category 是为了实现 deep link 功能,和我们目前学习的东西无关,所以直接使用 tools:ignore 属性将警告忽略即可。
③ 运行程序,点击 MainActivity 上的按钮。
系统自动弹出了一个列表,显示目前能够响应这个 Intent 的所有程序。选择 Chrome 还会像之前一样打开浏览器,并显示百度主页,而如果选择了 ActivityTest,则会启动 ThirdActivity。虽然我们声明了 ThirdActivity 是可以响应打开网页的 Intent 的,但实际上这个 Activity 并没有加载并显示网页的功能。
除了 https 协议外,我们还可以指定很多其他协议,比如 geo 表示地理位置、tel 表示拨打电话。下面的代码展示了如何在程序中调用系统拨号界面。
button1.setOnClickListener {
// Intent.ACTION_DIAL Android 系统的内置动作
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}
(4)向下一个 Activity 传递数据
Intent 在启动 Activity 的时候还可以传递数据,Intent 中提供了一系列 putExtra( ) 方法的重载,可以把想要传递的数据暂存在 Intent 中,在启动另一个 Activity 后,只需要把这些数据从 Intent 中取出就可以了。
比如,MainActivity 中有一个字符串,把它传到 SecondActivity 中,
① 修改 MainActivity 中 button1的点击事件
button1.setOnClickListener {
val data = "Hello SecondActivity"
val intent = Intent(this,SecondActivity::class.java)
intent.putExtra("extra_data",data)
startActivity(intent)
}
putExtra( ) 方法接受两个参数,第一个参数是键,用于之后从 Intent 中取值,第二个参数才是真正要传递的数据。
② 在 SecondActivity 中将传递的数据取出来,并打印出来
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val extraData = intent.getStringExtra("extra_data")
Log.d("SecondActivity","extra data is $extraData")
}
}
上述代码中的 intent 实际上调用的是父类的 getIntent( )方法,获取用于启动 SecondActivity 的 Intent,然后调用 getStringExtra( ) 方法并传入相应的键值,就可以得到传递的数据。如果传递的是整型数据,则使用 getIntExtra( ) 方法;如果传递的是布尔型数据,则使用 getBooleanExtra( ) 方法,以此类推。
③ 重新运行程序,在 MaintActivity 的界面点击一下按钮会跳转到 SecondActivity ,查看 Logcat 打印信息。
(5)返回数据给上一个 Activity
Activity 类中还有一个用于启动 Activity 的 startActivityForResult( ) 方法,但它期望在 Activity 销毁的时候能够返回一个结果给上一个 Activity。
startActivityForResult( ) 方法接受两个参数:第一个参数是 Intent;第二个参数是请求码,用于在之后的回调中判断数据的来源。
① 修改 MainActivity 中按钮的点击事件
button1.setOnClickListener {
val intent = Intent(this,SecondActivity::class.java)
startActivityForResult(intent,1)
}
这里使用了 startActivityForResult( ) 方法,来启动 SecondActivity,请求码只要是唯一值就可以,这里传入了1。
② 在 SecondActivity 中给按钮注册点击事件,并在点击事件中添加返回数据的逻辑
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
button2.setOnClickListener {
val intent = Intent()
intent.putExtra("data_return","Hello MainActivity")
setResult(RESULT_OK,intent)
finish()
}
}
}
构建的Intent 仅仅用于传输数据,没有任何”意图“。把要传递的数据存在在 Intent 中,然后调用 setResult( ) 方法。这个方法专门用于向上一个 Activity 返回数据。setResult( ) 方法接受两个参数:第一个参数用于向上一个 Activity 返回处理结果,一般只使用 RESULT_OK 或 RESULT_CANCELED 这两个值;第二个参数则把带有数据的 Intent 传递回去。最后调用 finish( ) 方法来销毁当前 Activity 。
③ 重写onActivityResult( )方法来得到返回的数据
使用 startActivityForResult( ) 方法来启动 SecondActivity,在 SecondActivity 被销毁之后会回调上一个 Activity 的 onActivityResult( )方法,因此我们需要在 MainActivity 中重写这个方法来得到返回的数据。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
1 -> if(requestCode == RESULT_OK){
val resultData = data?.getStringExtra("data_return")
Log.d("FirstActivity","returned data is $resultData")
}
}
}
onActivityResult( ) 有三个参数:第一个参数 requestCode,即启动 Activity 时的请求码;第二个参数 resultCode,即我们在返回数据时传入的处理结果;第三个参数 data ,即携带着返回数据的 Intent。
由于在一个 Activity 中有可能调用 startActivityForResult( ) 方法去启动很多不同的 Activity,每一个 Activity 返回的数据都会回调到 onActivityResult( )这个方法中,因此首先要检查 requestCode 判断数据来源,再通过 resultCode 的值来判断处理结果是否成功。最后从 data 中取值并打印出来,这样就完成了向上一个 Activity 返回数据的工作。
④ 重新运行程序,点击 Button1
如果不是通过按钮返回数据,而是通过按下 Back 键回到 MainActivity,可以通过在 SecondActivity 中重写 onBackPressed( ) 方法。
override fun onBackPressed(){
val intent = Intent()
intent.putExtra("data_return","Hello MainActivity")
setResult(RESULT_OK,intent)
finish()
}