09 使用Intent在活动之间穿梭
Intent
定义:
Intent是Android程序中各组件之间进行交互的一种重要方式
-
可以指明当前组件想要执行的动作
-
可以在不同组件之间传递数据
用途:
Intent 一般可被用于启动活动、启动服务以及发送广播等场景
分类:
- 显式Intent
- 隐式Intent
Intent 作为一个负责组件间传递消息的信息对象,最重要的就是其包含的信息。实际上无论是显式还是隐式,Intent 发出的时候,系统对应的行为正是由 Intent 所包含信息的组合决定。一个 Intent 所包含的信息如下图:
目标:
启动活动
一、使用显式Intent
新建活动emptyActivity->SecondActivity
-
修改
second_layout.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 2 --> <Button android:id="@+id/button_2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button 2"></Button> </LinearLayout>
-
SecondActivity.java
package com.example.myactivitytest; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second_layout); } }
-
在
AndroidManifest.xml
中注册<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myactivitytest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyActivityTest"> <!-- Android studio已经帮我们把SecondActivity注册了 SecondActivity不是主活动,所以不用配置 <intent-filter> --> <activity android:name=".SecondActivity" /> <activity android:name=".FirstActivity" android:label="This is FirstActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
-
修改
FirstActivity.java
中Button1的点击事件button1.setOnClickListener()
package com.example.myactivitytest; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast; public class FirstActivity extends AppCompatActivity { /* 项目中的任何活动都需要重写Activity的onCreate()方法 */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 调用setContentView()方法给当前活动加载布局 传入参数为布局文件的id 项目中添加的任何资源都会在R文件中生成一个相应的资源id 我们把创建的first_layout.xml布局的id已经添加到了R文件中 Resource:资源 所有的活动在AndroidManifest.xml中注册才能生效 Android studio可以帮我们自动在xml中注册 */ setContentView(R.layout.first_layout); /* 定义一个Toast的触发点 通过findViewById()方法获取到在布局文件中定义的元素 @return 返回一个View对象,我们需要向下转型,将它转成Button对象 传入R.id.button_1得到按钮的实例android:id="@+id/button_1" */ Button button1 = (Button) findViewById(R.id.button_1); /* 调用setOnClickListener()为按钮注册一个监听器 点击按钮就会执行监听器的onClick()方法 弹出Toast的功能需要写在onClick()方法中 */ button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* 在FirstActivity这个活动的基础上打开SecondActivity这个活动 然后通过startActivity()方法来执行这个Intent 1. 构建一个Intent 2. 传入FirstActivity.this作为上下文 3. 传入SecondActivity.class作为目标活动 */ Intent intent = new Intent( FirstActivity.this,SecondActivity.class); //startActivity传入一个Intent对象 startActivity(intent); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { /* 通过getMenuInflater()方法得到MenuInflater对象 再调用inflate()方法就可以给当前活动创建菜单 两个参数: 指定我们通过哪个资源文件来创建菜单:R.menu.main 指定我们菜单项将添加到哪一个menu对象中: menu,就是onCreateOptionsMenu方法的参数 @return boolean: true 表示允许创建的菜单显示出来 false 创建的菜单将无法显示 */ getMenuInflater().inflate(R.menu.main,menu); //return super.onCreateOptionsMenu(menu); return true; } /** * 通过调用item.getItemId()来判断我们点击的是哪一个菜单项 * @param item * @return */ @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()){ case R.id.add_item: Toast.makeText(this,"你点击了Add",Toast.LENGTH_SHORT).show(); break; case R.id.remove_item: Toast.makeText(this,"你点击了Remove",Toast.LENGTH_SHORT).show(); break; default: } return true; } }
运行程序
点击button1后的界面
二、使用隐式Intent
隐式Intent并不明确指出我们要启动哪一个活动,交给系统去帮我们找到合适的活动,然后再启动
-
配置
<intent-filter>
内容<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myactivitytest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyActivityTest"> <!-- Android studio已经帮我们把SecondActivity注册了 SecondActivity不是主活动,所以不用配置 <intent-filter> --> <activity android:name=".SecondActivity" > <!-- 在<action>标签中我们指明了当前活动 可以响应com.example.myactivitytest.ACTION_START这个action 而<category>标签则包含了一些附加信息,更精确地指明了当前的活动能 够响应的Intent中还可能带有的category。 只有<action>和<category>中的内容同时能够 匹配上Intent中指定的action和 category时, 这个活动才能响应该Intent。 --> <intent-filter> <action android:name="com.example.myactivitytest.ACTION_START" /> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".FirstActivity" android:label="This is FirstActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
-
修改
FirstActivity.java
中Button1的点击事件button1.setOnClickListener()
button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* 在FirstActivity这个活动的基础上打开SecondActivity这个活动 然后通过startActivity()方法来执行这个Intent 1. 构建一个Intent 2. 传入FirstActivity.this作为上下文 3. 传入SecondActivity.class作为目标活动 */ //Intent intent = new Intent( //FirstActivity.this,SecondActivity.class); //startActivity传入一个Intent对象 Intent intent = new Intent("com.example.myactivitytest.ACTION_START"); //android.intent.category.DEFAULT是一种默认的category, //在调用startActivity()方法的时候会自动将这个category添加到Intent中 startActivity(intent); } }); }
每个Intent中只能指定一个action,但却能指定多个category
category一定要在Java文件和中同时声明,缺一不可
三、其他隐式Intent
使用隐式Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享成为了可能。
在应用程序中展示一个网页的Intent
比如说你的应用程序中需要展示一个网页,这时你没有必要自己去实现一个浏览器(事实上也不太可能),而是只需要调用系统的浏览器来打开这个网页就行了。
-
修改
FirstActivity.java
中Button1的点击事件button1.setOnClickListener()
button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* 更多隐式Intent Intent.ACTION_VIEW 安卓系统内置动作 public static final String ACTION_VIEW = "android.intent.action.VIEW" */ Intent intent = new Intent(Intent.ACTION_VIEW); /* 通过Uri.parse()方法,将一个网址字符串解析成一个uri对象, 再调用Intent的 setData()方法将这个Uri对象传递进去。 */ intent.setData(Uri.parse("http://www.baidu.com")); startActivity(intent); } }); }
- 运行后点击button1结果:
可以在
<intent-filter>
标签中再配置一个<data>
标签,用于更精确地指定当前活动能够响应什么类型的数据。`标签中主要可以配置以下内容
- android:scheme。用于指定数据的协议部分,如上例中的 http部分。
- android:host。用于指定数据的主机名部分,如上例中的www.baidu.com部分。
- android:port。用于指定数据的端口部分,一般紧随在主机名之后。
- android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
- android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
建立一个活动,响应打开网页的Intent
-
新建
ThirdActivity.java
-
third_layout.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 3 --> <Button android:id="@+id/button_3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button 3"></Button> </LinearLayout>
-
修改
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.myactivitytest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyActivityTest"> <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="http"/> </intent-filter> </activity> <!-- Android studio已经帮我们把SecondActivity注册了 SecondActivity不是主活动,所以不用配置 <intent-filter> --> <activity android:name=".SecondActivity"> <!-- 在<action>标签中我们指明了当前活动 可以响应com.example.myactivitytest.ACTION_START这个action 而<category>标签则包含了一些附加信息,更精确地指明了当前的活动能 够响应的Intent中还可能带有的category。 只有<action>和<category>中的内容同时能够 匹配上Intent中指定的action和 category时, 这个活动才能响应该Intent。 --> <intent-filter> <action android:name="com.example.myactivitytest.ACTION_START" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name=".FirstActivity" android:label="This is FirstActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
选择chrome
选择MyActivityTest
另:
FirstActivity.java
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
/*
更多隐式Intent
Intent.ACTION_VIEW 安卓系统内置动作
public static final String ACTION_VIEW = "android.intent.action.VIEW"
*/
Intent intent = new Intent(Intent.ACTION_VIEW);
/*
通过Uri.parse()方法,将一个网址字符串解析成一个uri对象,
再调用Intent的 setData()方法将这个Uri对象传递进去。
*/
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
});
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.myactivitytest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyActivityTest">
<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="tel"/>
</intent-filter>
</activity>
<!--
Android studio已经帮我们把SecondActivity注册了
SecondActivity不是主活动,所以不用配置 <intent-filter>
-->
<activity android:name=".SecondActivity">
<!--
在<action>标签中我们指明了当前活动
可以响应com.example.myactivitytest.ACTION_START这个action
而<category>标签则包含了一些附加信息,更精确地指明了当前的活动能
够响应的Intent中还可能带有的category。
只有<action>和<category>中的内容同时能够
匹配上Intent中指定的action和 category时,
这个活动才能响应该Intent。
-->
<intent-filter>
<action android:name="com.example.myactivitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".FirstActivity"
android:label="This is FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
点击Button1
选择Phone
四、向下一个活动传递数据
可以在启动活动的时候传递数据
-
Intent 中提供了一系列 putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中,
-
启动了另一个活动后,只需要把这些数据再从Intent中取出就可以
eg:
-
把FirstActivity中的一个字符串传递到SecondActivity中
button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* 向下一个活动传递数据 */ String data = "毁灭吧,我累了!"; Intent intent = new Intent( FirstActivity.this,SecondActivity.class); /* putExtra()方法接收两个参数 第一个参数是键,用于后面从Intent中取值 第二个参数是value,是真正要传递的数据。 */ intent.putExtra("extra_data",data); startActivity(intent); } });
-
在 SecondActivity 中将传递的数据取出,并打印出来
package com.example.myactivitytest; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second_layout); //通过getIntent()方法获取到用于启动SecondActivity的Intent Intent intent = getIntent(); /* 调用getStringExtra()方法,传入相应的键值,就可以得到传递的数据 还有getIntExtra()、getBooleanExtra()等 */ String data = intent.getStringExtra("extra_data"); Log.d("SecondActivity",data); } }
运行程序
在logcat中查看debug日志
五、返回数据给上一个活动
Activity中还有一个startActivityForResult()方法也是用于启动活动的,但这个方法期望在活动销毁的时候能够返回一个结果给上一个活动。
startActivityForResult()方法接收两个参数
- 第一个参数是Intent
- 第二个参数是请求码,用于在之后的回调中判断数据的来源
-
修改FirstActivity中的按钮点击事件
button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* 返回数据给上一个活动 startActivityForResult()方法接收两个参数 - 第一个参数是Intent - 第二个参数是请求码,用于在之后的回调中判断数据的来源 */ Intent intent = new Intent( FirstActivity.this,SecondActivity.class); //使用startActivityForResult()方法来启动SecondActivity // 请求码只要是一个唯一值就可以了,这里传入了1 startActivityForResult(intent,1); } });
-
在SecondActivity中给按钮注册点击事件,并在点击事件中添加返回数据的逻辑
package com.example.myactivitytest; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second_layout); Button button2 = (Button) findViewById(R.id.button_2); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //构建一个Intent,用于传递数据 Intent intent = new Intent(); intent.putExtra("data_return", "跟得上我的脚步吗?"); /* setResult()专门用于向上一个活动返回数据 传入两个参数: 第一个参数用于向上一个活动返回处理结果 一般只使用RESULT_OK 或RESULT_CANCELED 第二个参数则把带有数据的Intent传递回去 */ setResult(RESULT_OK, intent); //调用了finish()方法来销毁当前活动 finish(); } }); } }
-
我们使用startActivityForResult()方法来启动SecondActivity,在SecondActivity被销毁之后会回调上一个活动的onActivityResult()方法,因此我们需要在FirstActivity中重写这个方法来得到返回的数据。
/** * onActivityResult()方法带有三个参数 * @param requestCode 第一个参数requestCode,我们在启动活动时传入的请求码(startActivityForResult(intent,1);) * @param resultCode 第二个参数resultCode,我们在返回数据时传入的处理结果 * @param data 第三个参数data,携带着返回数据的Intent */ @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case 1: if (resultCode == RESULT_OK) { String returnedData = data.getStringExtra("data_return"); Log.d("FirstActivity", returnedData); } break; default: } }
由于在一个活动中有可能调用startActivityForResult()方法去启动很多不同的活动,每一个活动返回的数据都会回调到onActivityResult()这个方法中,因此我们首先要做的就是通过检查requestCode 的值来判断数据来源。确定数据是从SecondActivity返回的之后,我们再通过resultCode的值来判断处理结果是否成功。最后从data中取值并打印出来,这样就完成了向上一个活动返回数据的工作。
运行程序,在FirstActivity的界面点击按钮会打开SecondActivity ,然后在SecondActivity界面点击Button 2按钮会回到FirstActivity,这时查看logcat 的打印信息