【Android】活动之间的穿梭

引入

在活动的初学建立了一个简单的活动,但只有一个活动不是过于简单,在你使用手机的时候按下一个按钮可能会跳转到下一个界面,此时就是活动之间的穿梭:使用Intent在活动之间穿梭

Intent:是android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。大致可分为显式Intent隐式Intent

使用显式Intent

Intent有多个构造函数的重载,其中一个为Intent(Context packageContext, Class<?> cls):第一个参数Context要求提供一个启动活动的上下文,第二个参数Class即为指定想要启动的目标活动,通过这个构造就可以直接告诉Intent的意图

在使用之前我们先创建了两个活动,第一个主活动有一个按钮Button1,第二个活动有一个按钮Button2,我们需要按下第一个按钮使其跳转到第二个活动

activity_main.xml:

<?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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/Button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button1"/>

</LinearLayout>

activity_second.xml:

<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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".SecondActivity">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/Button_2"
        android:text="Button 2"/>

</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">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ActivityTest"
        tools:targetApi="31">
        <activity
            android:name=".SecondActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

在主活动下写下按下Button1按钮带来的事件,代码如下:

Button button1 = (Button) findViewById(R.id.Button_1);
//按下Button1按钮使其跳转到第二个活动界面
button1.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
          Intent intent = new Intent(MainActivity.this, SecondActivity.class);
          startActivity(intent);
      }
});

即可完成上述要求,在第一个活动页面按下Button1则会跳转到第二个活动界面,此时按下back按钮就可以销毁第二个活动,返回到主活动

使用隐式Intent

隐式Intent:并不明确指出我们想要重启哪一个活动,而是指出一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的活动(就是可以响应我们这个隐式Intent的活动)去启动

  1. 此时我们需要修改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">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ActivityTest"
        tools:targetApi="31">
        <!--是否能被其他应用访问:android:exported="false"-->
        <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>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<action android:name="com.example.activitytest.ACTION_START"/>:这个标签声明了一个特定的动作,即ACTION_START。这个动作是自定义的,由com.example.activitytest这个命名空间限定。这意味着只有当Intent包含这个特定的动作时,配置了这个<intent-filter>的组件才会被触发,就会自动触发这个活动
<category android:name="android.intent.category.DEFAULT"/>:这个标签声明了Intent所属的类别。这里的是一种默认的category,在调用 startActivity()方法的时候会自动将其添加到Intent中。
  1. 修改主活动下按钮的点击事件:
Button button1 = (Button) findViewById(R.id.Button_1);
//按下Button1按钮使其跳转到第二个活动界面:隐式Intent,即根据action与category的同时匹配确定所要进入的活动
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent("com.example.activitytest.ACTION_START");
        startActivity(intent);
    }
});

我们使用的是另外一个Intent的构造函数,将action字符串直接传了进去,表明我们所要响应的活动。注意一个Intent只能指定一个action,但是可以指定多个category

指定多个category(以加入了android.intent.category.MY_DEFAULT为例):

主活动代码修改:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent("com.example.activitytest.ACTION_START");
        intent.addCategory("android.intent.category.MY_DEFAULT");
        startActivity(intent);
});

修改AndroidManifest.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_DEFAULT"/>
    </intent-filter>
</activity>

带来的效果仍为按下第一个按钮会跳转到第二个活动,但是与上一个不同在于,上一个是在按钮中直接声明要跳转到第二个活动,而这个是根据AndroidManifest.xml活动中的标签声明来确定到底是哪个活动,必需要将所有的标签声明匹配完毕,才可跳转到对应界面

更多隐式Intent的用法

使用隐式Intent不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这就为Android多个应用程序之间的功能共享成为了可能

  1. 修改主活动的代码,使其跳转到指定的页面(百度):
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse("http://baidu.com"));
        startActivity(intent);
    }
});
Intent intent = new Intent(Intent.ACTION_VIEW):使用Intent.ACTION_VIEW作为动作。这个动作是Android系统中预定义的,用于表示查看数据的操作。在这种情况下,它告诉Android系统我们想要查看网页。
intent.setData(Uri.parse("http://baidu.com")):这里使用Uri.parse()方法将字符串"http://baidu.com"转换成`Uri`对象。这个`Uri`对象代表了要查看的数据的地址,即百度的主页。

注意:我们可以在标签中再配置一个标签,用于更精确地指定当前活动能够响应什么类型的数据,主要配置:

  1. android:scheme:用于指定数据的协议部分,如上例中的http部分
  2. android:host:用于指定数据的主机名部分,如上例中的www.baidu.com部分
  3. android:port:用于指定数据的端口部分,一般紧随在主机名之后
  4. android:path:用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容
  5. android:mimeType:用于指定可以处理的数据类型,允许使用通配符的方式进行指定

运行之后:
按下Button1按钮就会跳转到百度页面:
在这里插入图片描述

  1. 创建一个活动使其打开网页:

创建一个活动为ThirdActivity:

修改AndroidManifest.xml中的代码:

<activity
      android:name=".ThirdActivity"
      tools:ignore="AppLinkUrlError"
      android:exported="true" >
      <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="http"/>
      </intent-filter>
</activity>

注:具体来说,AppLinkUrlError是一个错误检查,它可能会在XML布局文件中检测到一个android:autoLink属性时被触发。android:autoLink属性用于指定文本中的URL、地址、电话号码等应该自动转换为可点击的链接。使用tools:ignore="AppLinkUrlError"属性可以告诉Android Studio忽略这个错误。

主活动中代码修改:

button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            startActivity(intent);
        }
});

按下按钮的结果:
在这里插入图片描述

  1. 其他协议:指定其他的协议(tel:表示拨打电话;geo:表示地理位置等)
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(Intent.ACTION_DIAL);
        intent.setData(Uri.parse("tel:10086"));
        startActivity(intent);
    }
});

按下按钮的结果:
在这里插入图片描述

向下一个活动传递数据

主活动的代码:

Button button1 = (Button) findViewById(R.id.Button_1);
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String data = "Hello SecondActivity";
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        intent.putExtra("extra_data",data);
        startActivity(intent);
    }
});
  1. Intent intent = new Intent(MainActivity.this,SecondActivity.class):这行代码创建了一个Intent对象,用于启动SecondActivityMainActivity.this是当前上下文(这里是MainActivity),SecondActivity.class是要启动的Activity的类。
  2. intent.putExtra(“extra_data”,data):这行代码使用putExtra方法将字符串data作为额外的数据添加到Intent中。"extra_data"是一个键,用于在目标Activity中检索这个数据。
  3. startActivity(intent):这行代码调用startActivity方法,并传入之前创建的Intent对象。这将启动SecondActivity,并且携带了之前添加的额外数据。

第二个活动的代码:

Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);

第二行代码:这行代码使用getStringExtra方法从Intent中提取一个名为"extra_data"的额外数据项。getStringExtra是一个便捷方法,专门用于检索字符串类型的额外数据。如果Intent中没有这个键的数据,或者数据不是字符串类型,将返回null

运行日志打印:
在这里插入图片描述

返回数据给上一个活动

从AndroidX库的Activity Result API开始,你可以使用ActivityResultContracts,它提供了一种更现代、类型安全的方式来启动Activity并接收结果。

例如,使用ActivityResult API:

主活动代码:

public class MainActivity extends BaseActivity {
    private ActivityResultLauncher<Intent> activityResultLauncher;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        Log.d("MainActivity", "task id is " + getTaskId());
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        //由第二个活动返回数据给主活动
        activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == RESULT_OK) {
                Intent data = result.getData();
                if (data != null) {
                    String resulyData = data.getStringExtra("data_return");
                    Log.d("MainActivity", resulyData);
                }
            }
        });
    }
}
  1. ActivityResultLauncher 类: 这个类提供了注册回调的方法,例如 registerForActivityResult,它允许你注册一个 ActivityResultCallback 来接收启动的 Activity 的结果。
  2. 代码使用registerForActivityResult方法注册了一个ActivityResultLauncher。这个方法接受两个参数:一个ActivityResultContract的实例,在这个例子中是ActivityResultContracts.StartActivityForResult,它表示启动一个Activity并接收结果的标准契约;第二个参数是一个lambda表达式,用于定义当Activity的结果返回时如何处理。
  3. lambda表达式的主体部分,用于处理从SecondActivity返回的结果。首先检查结果码是否为RESULT_OK,这表示SecondActivity成功完成了操作。然后,尝试从返回的Intent中获取数据。如果数据不为空,从中提取字符串数据"data_return",并使用Log.d输出到日志。
  4. Button1按钮的点击事件,调用activityResultLauncher.launch(intent)启动SecondActivity。使用launch方法而不是startActivityForResult,因为我们已经通过ActivityResultLauncher处理了结果。

第二个活动中的代码:

Button button2 = (Button) findViewById(R.id.Button_2);
button2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.putExtra("data_return", "Hello MainActivity");
        setResult(RESULT_OK,intent);
        finish();
    }
});

setResult方法用于设置SecondActivity的结果码和返回数据。第一个参数RESULT_OK表示操作成功完成,第二个参数是包含返回数据的Intent对象。finish方法用于关闭当前的SecondActivity,将其从用户界面中移除。

注意:

注意:当我们在第二个活动页面按下按钮返回到第一个活动界面会给第一个活动返回数据,但是当我们直接按下back按钮,则没有了返回数据,通过以下方法来解决,在SecondActivty中:

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        // 这里实现Back键的自定义逻辑
        Intent intent = new Intent();
        intent.putExtra("data_return", "Hello MainActivity");
        setresult(RESULT_OK, intent);
        finish();
        return true; // 返回true表示事件已处理
    }
    return super.onKeyDown(keyCode, event);
}
  1. onKeyDownActivity类的一个方法,用于监听按键事件。当用户按下任何键时,这个方法会被调用。参数keyCode表示被按下的键的代码,event是对应的KeyEvent对象。
  2. KeyEvent.KEYCODE_BACK是一个常量,表示Back键的键码。这个条件判断用于检查是否按下了Back键。

运行日志打印:
在这里插入图片描述
到这里就结束了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值