Intent
2 用Intent实现Activity之间的跳转
一个应用一般情况下不会就只有一个Activity,那么如何实现不同Activity之间的跳转呢
2.1 显示Intent
在当前包路径下New一个空Activity,勾选如图
点击Finish,将自动生成的second_layout中的代码替换掉
<?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/button_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button_2"/>
</LinearLayout>
准备工作做完了,我们现在来看看Intent
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据
Intent的作用一般有三种:
- 启动Activity
- 启动Service
- 发送广播
我们现在学启动Activity,Service和广播后面涉及到再学
Intent大致可以分为两种
- 显式Intent
- 隐式Intent
Intent有多个构造函数的重载,其中一个Intent(Context packageContext, Class<?> cls)
的两个参数:
- Context要求提供一个启动Activity的上下文
- Class用于指定想要启动的目标Activity
通过这个构造函数就可以构建出Intent的“意图”,此外Activity类中提供了一个startActivity()方法,用于启动Activity,并接收一个Intent参数,即上面构建的Intent可以传进来,这样,就可以启动目标Activity了
将FirstActivity中按钮点击事件代码进行修改
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
其中,this表示FirstActivity,SecondActivity::class.java 相当于Java中的SecondActivity.class写法
运行结果为点击button_1会跳转到SscondActivity对应的布局
这种方式之所以被称为显示Intent,是因为Intent的“意图”非常明显,val intent = Intent(this, SecondActivity::class.java)
,即在FirstActivity的基础上打开SecondActivity
2.2 隐式Intent
隐式Intent不会明确指出启动哪一个Activity,而是需要根据action和category等信息来匹配、分析,找出合适的Activity来启动。
简单来说,显示的Intent就是点名的时候报一个人的身份证号码;隐式的Intent就是说母亲姓李,父亲姓张,自己姓赵的三班的男生,这样的符合条件的男生虽然没有被明确点名,但是也知道叫到了自己。在隐式的Intent中,这样的锁定条件的角色就由action和category来担任了。
打开AndroidManifest.xml,修改为如下形式(即添加几行代码)
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
在隐式Intent中声明的时候,必须action和category都匹配上的Activity,才会被认为是目标Activity,因为android.intent.category.DEFAULT
是一种默认的category,在调用startActivity()的时候会自动将这个category添加到Intent中,所以只需要在Intent中添加action就好。
将点击事件中的Intent做以下修改:
val intent = Intent("com.example.activitytest.ACTION_START")
然后运行,发现也能实现跳转功能
接下来,试试在AndroidManifest.xml中再加一条category
<category android:name="com.example.activitytest.MY_CATEGORY"/>
在点击事件中,调用Intent的addCategory()方法
intent.addCategory("com.example.activitytest.MY_CATEGORY")
再运行,发现能够匹配到SecondActivity
2.3 更多隐式Intent的用法
使用隐式Intent不仅可以启动自己程序的Activity,还可以启动其他程序的Activity。比如在自己程序中需要打开一个网页,只要调用系统的浏览器来打开这个网页就行了。
点击事件中的代码修改:
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("http://www.baidu.com")
startActivity(intent)
指定Intent的action是Intent.ACTION_VIEW,这是一个Android系统内置的动作。Uri.parse()方法将网址字符串解析成一个Uri对象,通过setData()方法接收。直接.data而不是.setData()是Kotlin中的语法糖,前面说过。
运行,如图,点击button_1后调用系统浏览器
此外,我们可以在<intent-filter>
中加入<data>
标签,更精确地指定当前Activity能够响应的数据,属性如下:
android:scheme 协议部分
android:host 主机名部分
android:port 端口部分
android:path 主机名和端口后面部分,跟在域名后的内容
android:mimeType 可以处理的数据类型
New一个ThirdActivity,并将third_layout.xml内容替换,在AndroidManifest.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/button_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button_3"/>
</LinearLayout>
在AndroidManifest.xml中修改信息
<activity android:name=".ThirdActivity">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
由于AS认为所有能够响应ACTION_VIEW的Activity都应该加上BROWSABLE的category,否则就会出现警告,因此要加上Android_lint的tools:ignore。
但是我这里出现了飘红,找了很久想办法解决了飘红,解决方法在我的另一篇博客,但是功能还是不能实现,我这里先放一放了。
正常情况下运行结果是点击按钮后出现选择界面,让我们选择用浏览器还是ThirdActivity打开。
除了https协议外,我们还可以指定其他协议,比如geo表示显示地理位置、tel表示拨打电话。
将点击事件代码修改:
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
Intent.ACTION_DIAL又是一个Android系统的内置动作,tel指定协议,10086指定号码。
运行结果如图
2.4 Activity间的数据传递
2.4.1 向下传递
在向下一个Activity传递数据时,通过Intent.putExtra()方法保存数据,在下一个Activity中,通过getIntent()方法获取用于启动该Activity的Intent,调用Intent.getStringExtra()方法获取数据(不一定是String,可能是其他类型),另外,由于Kotlin提供的get/set的语法糖,在获取Intent时,也不用getIntent()方法,直接用intent就可以。
FirstActivity中的代码(在button_1的点击事件中)
val data = "Hello SecondActivity"
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("extra_data", data)
startActivity(intent)
SecondActivity中的代码
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.second_layout)
val data = intent.getStringExtra("extra_data")
Log.d("SecondActivity", "data is $data")
}
运行结果,点击button_1后,跳转到SecondActivity,并出现日志
2.4.2 向上返回
在向下传递中,我们用intent来传递数据,但是我们从下一级返回上一级Activity时,并没有一个用来传递数据的Intent。不过,在Activity类中,还有一个用于启动Activity的startActivityForResult()
方法,这个方法就需要在Activity销毁的时候能够返回一个结果给上一个Activity。
startActivityForResult()
方法接收两个参数,第一个参数还是Intent实例,第二个参数是请求码,用于在之后的回调中判断数据的来源。
FirstActivity:
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 1)
请求码只要是一个唯一值就可以了,接下来在SecondActivity中加入按钮的点击事件,在其中书写添加返回数据的逻辑
button_2.setOnClickListener {
val intent = Intent()
intent.putExtra("data_return", "Hello FirstActivity")
setResult(RESULT_OK, intent)
finish()
}
可以看到,我们构建了一个没有“意图”的intent,仅仅用来传递数据。
之后调用了setResult()
方法,这个方法专门用来向上返回数据,它接收两个参数
- 用于向上一个Activity返回处理结果,一般使用RESULT_OK或者RESULT_CANCELED两个值
- 把带有数据的intent传递回去
最后用finish()销毁当前Activity
此外,由于我们是使用startActivityForResult()
方法来启动SecondActivity的,在其销毁之后会回调上一个Activity的onActivityResult()
方法,因此需要子啊FirstActivity中重写这个方法来得到返回的数据。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
1 -> if (resultCode == RESULT_OK){
val returnData = data?.getStringExtra("data_return")
Log.d("FirstActivity", "returned data is $returnData")
}
}
}
onActivityResult()
接收三个参数
- 在启动Activity时传入的请求码,我们传入的1
- 返回数据时传入的处理结果
- 携带者数据的intent
运行结果,点击button_1跳转带SecondActivity,点击button_2返回到FirstActivity并打印出日志