使用Intent的目的:启动器点击图标只会进入程序的主Activity,如何进入其他的Activity,实现不同Activity之间的交互。使用intent来实现。
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前空前要执行的动作,还可以在不同组件之间传递数据。
Intent一般可用于启动Activity、启动Service、发送广播等场景。
Intent大致分为两种:显式Intent、隐式Intent
1、使用显式Intent
第一步肯定要创建新的一个Activity,并给新的Activity一个新的layout布局。
Intent有多个构造函数的重载,其中一个是
Intent(Context packageContext, Class<?> cls)
第一个参数Context:要求提供一个启动Activity的上下文。
第二个参数Class:指定我们想要启动的目标Activity。
使用Intent:Activity类提供了startActivity()方法,专门用于启动Intent,它接收一个intent参数。
修改FirstActivity中的点击按钮事件,代码如下:
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
button1.setOnClickListener {
Toast.makeText(this,"You clicked Button1",Toast.LENGTH_SHORT).show()
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
}
}
第一个参数传入了this,即FirstActivity作为上下文。第二个参数传入SecondActivity::class.java,即打开SecondActivity。使用这种方式,意图很明显,称为显式Intent。
2、使用隐式Intent
隐式Intent不明确指明要启动哪一个Activity,二十指定一系列的 action 和 category等信息,由系统自动去分析这个Activity,并帮助我们找到合适的Activity去启动。
在AndroidManifest.xml中的SecondActivity中添加如下代码:
<activity
android:name=".SecondActivity"
android:exported="false"
android:label="This is the second Activity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
在<action>标签中我们指定了当前Activity可以相应com.example.activitytest.ACTION_START这个action。
在<category>中指明了当前Activity能够相应的Intent中可能带有的category。
只有<action>和<category>同时匹配Intent中的action和category时,这个Activity才能响应这个Intent。
在按钮点击事件中添加代码:
button1.setOnClickListener {
Toast.makeText(this,"You clicked Button1",Toast.LENGTH_SHORT).show()
val intent = Intent("com.example.activitytest.ACTION_START")
startActivity(intent)
}
这是Intent的另一个构造函数,直接输入了action的字符串,就可以响应SecondActivity。
能启动SecondActivity的原因,是因为SecondActivity的category标签使用的是默认值DEFAULT。在调用 startActivity()时,会自动传入 默认的category。
每一个Intent只能传入一个 action ,但是可以指定多个 category 。当我们在点击事件中的Intent中再添加一个category,程序会崩溃,找不到这个Activity。
button1.setOnClickListener {
Toast.makeText(this,"You clicked Button1",Toast.LENGTH_SHORT).show()
val intent = Intent("com.example.activitytest.ACTION_START")
intent.addCategory("com.example.activitytest.MY_CATEGORY")
startActivity(intent)
}
程序会崩溃,查看错误日志(logcat中的error日志),可发现由于系统无法找到对应的Intent!!!
解决办法:
在SecondActivity的<category>添加上缺少的category即可。
<activity
android:name=".SecondActivity"
android:exported="false"
android:label="This is the second Activity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.example.activitytest.MY_CATEGORY"/>
</intent-filter>
</activity>
3、更多隐式Intent用法
隐式Intent不仅能够打开自己的Activity,还可以启动其他程序的Activity,从而实现不同应用程序之间的交互。
可以通过按钮点击事件,使用隐式intent打开一个网址,具体代码如下:
button1.setOnClickListener {
Toast.makeText(this,"You clicked Button1",Toast.LENGTH_SHORT).show()
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.csdn.net/")
startActivity(intent)
}
首先、通过指定Intent的action是Intent.ACTION_VIEW,这是Android内置的一个动作,其常量值为android.intent.action.VIEW。
然后、通过Uri.parse(),将一个网址字符串解析成一个Uri对象。再通过Intent的setData() 方法,将uri对象传递进入Intent对象。这里的 intent.data 是语法糖。系统内部自动调用setData方法。
setData()方法:这个方法其实并不复杂,它接收一个Uri对象,主要用于指定当前Intent正在操作的数据,而这些数据通常是以字符串形式传入Uri.parse()方法中解析产生的。
可以在<intent-filter>中添加<data>标签,用于当前Activity能更精确的响应对应的数据。<data>标签包含以下配置:
android:scheme。用于指定数据的协议部分,如上例中的https部分。
android:host。用于指定数据的主机名部分,如上例中的www.baidu.com部分。
android:port。用于指定数据的端口部分,一般紧随在主机名之后。
android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内
容。
android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
只有当<data>标签中指定的内容和Intent中的内容完全一致时,此Activity才能响应该Intent。
例子:
创建一个新的ThirdActivity,并创建一个新的third_layout,代码如下:
<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"
android:orientation="vertical"
tools:context=".ThirdActivity">
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button 3"/>
</LinearLayout>
在AndroidManiTest.xml中进行注册:
<activity
android:name=".ThirdActivity"
android:exported="true" >
<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>
补充:这里的android:exported 此元素是设置当前Activity是否可以由其他应用程序启动:
设置为true:则任何应用都可以访问该Activity,并且可以通过其确切的类名启动。即允许不同进程间进行通信。
设置为false:则Activity只能由相同应用程序的组件、具有相同用户 ID 的应用程序或特权系统组件启动。这是没有意图过滤器时的默认值。
当mainActivity中有<intent-filter>标签时,应该设置android:exported为true,否则会报错。
因为我们对ThirdActivity也赋予了相应的<intent-filter>并且它的action也为android.intent.action.VIEW,响应的data也为https,因此当点击按钮时,会默认弹出一个列表,可供选择,进入网页还是进入新的ThirdActivity。
除了,调用https协议之外,还可以调用很多其他协议,例如 geo 表示地理位置,tel 表示拨打电话
通过按钮进行电话拨打的代码如下:
button1.setOnClickListener {
Toast.makeText(this,"You clicked Button1",Toast.LENGTH_SHORT).show()
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}
设置Intent的action为Intent.ACTION_DIAL,然后再data部分调用Uri.parse(),协议为tel.
其它的类似。
4、向下一个Activity传递数据
Intent不仅可以用来打开其它的Activity,还可以用来进行数据传输。
传输方法很简单,Intent提供了一系列的 putExtra() 方法重载,可以把想要传输的数据暂时封存到Intent中,启动新的Activity后,再从Intent中取出。
打开Activity 时,先将strData存放在了intent中,代码如下:
button1.setOnClickListener {
val strData = "Message"
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("extra_name", strData)
startActivity(intent)
}
取出代码如下,需要在SecondActivity 的 onCreate() 函数中自行取出,代码如下:
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.second_layout)
val extraData = intent.getStringExtra("extra_data")
Log.d("SecondActivity","extra data is $extraData")
}
}
代码中的intent实际上是调用父类的getIntent()方法,从而来获得启动SecondActivity 的intent。然后调用getStringExtra()方法并传入相应的键值,就可以得到传递的数据了。这里由于我们传递的是字符串,所以使用getStringExtra()方法来获取传递的数据。如果传递的是整型数据,则使用getIntExtra()方法;如果传递的是布尔型数据,则使用getBooleanExtra()方法,以此类推。
5、向上一个Activity传递数据
由于返回上一个Activity,仅需back即可。没有专门的Intent,并不能像向下传递消息一样。
Activity类提供了一个启动Activity的 startActivityForResult() 方法。
button1.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 1)
}
接下来在SecondActivity中添加点击事件:
button2.setOnClickListener {
val intent = Intent()
intent.putExtra("data_return", "Hello FirstActivity")
setResult(RESULT_OK, intent)
finish()
}
着同样创建了一个intent,并没有意图,仅用来进行数据传输。
紧接着把要传递的数据存放在Intent中,然后调用了setResult()方法。这个方法非常重要,专门用于向上一个Activity返回数据。
setResult()方法接收两个参数:
第一个参数用于向上一个Activity返回处理结果,一般只使用RESULT_OK或RESULT_CANCELED这两个值;
第二个参数则把带有数据的Intent传递回去。最后调用了finish()方法来销毁当前Activity。
Tips:当前返回数据是通过SecondActivity中的按钮来返回,但是一般返回上一Activity直接使用back,因此需要另一种写法:
由于我们是使用startActivityForResult()方法来启动SecondActivity的,在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 returnedData = data?.getStringExtra("data_return")
Log.d("FirstActivity", "returned data is $returnedData")
}
}
}
onActivityResult()方法带有3个参数:第一个参数requestCode,即我们在启动Activity时传入的请求码;第二个参数resultCode,即我们在返回数据时传入的处理结果;第三个参数data,即携带着返回数据的Intent。由于在一个Activity中有可能调用startActivityForResult()方法去启动很多不同的Activity,每一个Activity返回的数据都会回调到onActivityResult()这个方法中,因此我们首先要做的就是通过检查requestCode的值来判断数据来源。确定数据是从SecondActivity返回的之后,我们再通过resultCode的值来判断处理结果是否成功。最后从data中取值并打印出来,这样就完成了向
上一个Activity返回数据的工作。
但是目前版本已经被Google淘汰,需要使用更新的 Activity Result API,用法如下:
private val requestDataLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
if (it.resultCode == RESULT_OK) {
val returnData = it.data?.getStringExtra("data_return")
Log.d("FirstActivity","data return is $returnData")
}
}
通过 registerForActivityResult() 来注册对Activity的结果的监听。
registerForActivityResult()方法接收两个参数:
第一个参数是一种Contract类型,由于我们是希望从另外一个Activity中请求数据,因此这里使用了StartActivityForResult。
第二个参数是一个Lambda表达式,当有结果返回时则会回调到这里,然后我们在这里获取并处理数据即可。