700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > android studio 跳转后保留原页面数据_Intent详解以及Activity的跳转与数据传递

android studio 跳转后保留原页面数据_Intent详解以及Activity的跳转与数据传递

时间:2024-06-19 10:11:56

相关推荐

android studio 跳转后保留原页面数据_Intent详解以及Activity的跳转与数据传递

在上一次讲述Activity的时候,还有一个非常重要且常用的知识点没有讲,就是不同Activity之间的跳转和数据传递。我们在平常在使用app应用的时候,Activity的跳转和数据传递是经常会接触到。基本上每一款app都会有,除非这个应用只有一个界面。例如,我现在要做一款通讯app,当我在通讯录界面(假设为ContactActivity)点击其中的一个联系人,此时就会跳转到该联系人的详情界面(假设为DetailActivity);这其实就是Activity之间的跳转。那么有时候我们在跳转的过程可能会携带一些数据传递给要跳转的界面,这就是Activity之间的数据传递。但要实现这两个功能单靠Activity是不够的,我们还需要使用Intent来帮忙。

1.Intent概述

Intent的中文意思是 意图、目的、意向;

百度百科:

Intent(意图)主要是解决Android应用的各项组件之间的通讯。

Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给被调用的组件,并完成组件的调用。

简单来讲,Intent就是就是用于组件的通讯,当我们想在当前Activity去启动另外一个Activity的时候,程序并不会直接去通知或告诉系统要启动哪一个Activity,而是通过Intent进行封装,来表明意图。除了Activity的通讯,Service、BroadcastReciver的通讯我们都会用到Intent。当然这里我们主要还是讲Activity。

那么Activity之间的跳转是怎么通过Intent来实现的呢?

我们先来看一段常用的跳转代码

Intent intent = new Intent(MainActivity.this,DemoActivity.class);startActivity(intent);//startActivity(new Intent(MainActivity.this,DemoActivity.class));

上面的代码就实现了MainActivity和DemoActivity之间的跳转。(上面两行和下面注释的一行实现效果是一样的)Activity提供了两种方法来启动启动Activity,其中一个就是startActivity(Intent intent);另外一个在下面会讲到,使用方式就是传入一个Intent对象。也就是说通过Intent封装好意图后,此时我们还需要通过startActivity才能启动对应的Activity。我们可以通过一张图来更深刻的理解上边的代码。

根据上图其实就可以很好理解上面的跳转代码。这其实是显式跳转的其中一种方式。其实页面跳转是可以通过多种方式来实现的,而这么多方式又分为两大类,第一类是显式跳转,第二类是隐式跳转;下面会讲到,但在讲解这两种跳转方式之前,我们还需要先了解下Intent的各种属性。因为Android应用根据Intent来启动哪个组件或者以哪种方式来启动组件其实就是取决于Intent的各种属性的。

2.Intent的属性

Component

翻译为组件。在Android中,对于Component这个属性我们主要要了解的是ComponentName对象,通过官方解释可以知道ComponentName是特定应用组件的标识符,通常我们通过封装两个属性来标识一个组件:包名以及该组件所的类的完整路径名

//包名:我们在AndroidMainifest中可以看到package="com.example.demo"//com.example.demo就是包名,现在假设要找的组件类名就是DemoActivity,所以我们可以知道该组件所在的类的完整路径名就是com.example.demo.DemoActivity。将这两个属性封装成一个ComponentName对象,我们就可以确定一个组件。

确定好组建后,我们就可以通过Intent中的方法给其对象设置好属性:

//还是实现从MainActivity跳转到DemoActivityComponentName componentName = new ComponentName("com.example.demo","com.example.demo.DemoActivity");Intent intent = new Intent();intent.setComponent(componentName);startActivity(intent);

通过上面代码我们可以看到,和之前讲的一样,先将两个属性封装起来,然后就可以找到特定的组件了。其实ComponentName对象中包含了多个构造方法,下面我们再看一种:

//改变一下ComponentName对象构造方法中的参数,其余不变ComponentName componentName = new ComponentName(MainActivity.this,DemoActivity.class);

然后我们在DemoActivity中获取一下intent中封装的Component属性。

ComponentName componentName = getIntent().getComponent();Log.d(TAG, "componentName----->"+componentName);Log.d(TAG, "pkgName----->"+componentName.getPackageName());Log.d(TAG, "className----->"+componentName.getClassName());

跳转页面后,可以看到如下结果:

日志

还是包名+类的完整路径名,所以组件名称可以表示为包名/类的完整路径名,其实如果类的完整路径名的前面和包名是一样的话,是可以省略的----com.example.demo/.DemoActivity。通过ComponentName我们不仅可以在一个应用内部进行跳转,我们还可以跳转到别的应用上去。当然,一般来说我们不会通过这种方式跳转到第三方应用,毕竟包名有可能是变化的。

除此之外,intent中还包含了几种方法:

setClass(Context packageContext, Class<?> cls)setClassName(Context packageContext,String className)setClassName(String packageName, String className)//其实和上面是差不多的,举一个简单的例子,效果是一样的intent.setClassName("com.example.demo","com.example.demo.DemoActivity");startActivity(intent);

Action和Category

Action译为动作;在Android中,Action是一个抽象的动作,也就是说我们设置Action只是为它设定了一个动作,却并不知道这个动作具体是由哪个组件来完成。常用Action常量:

ACTION_MAIN:打开项目后第一个启动的ActivityACTION_VIEW:用来显示指定数据给用户的ACTION_CALL:指定给某人打电话的ACTION_SENDTO:指定给某人发短信的

Category译为种类,类别;它主要是用于对执行动作(Action)的类别进行描述(以哪种方式去执行intent请求的action)。常用的Category常量:

CATEGORY_DEFAULT: 用来把指定的Activity作为执行数据的默认动作,默认的启动方式CATEGORY_LAUNCHER: 把指定的Activity作为应用程序的入口,也就是启动程序后第一个所看到的界面,通常配合Action中的ACTION_MAIN使用

通常Action和Category会结合使用(android提供了大量的Action和Category常量,上面只是几个例子,刚兴趣的可以自行去搜索哈),不同于上面所讲的基本都是显示启动,这两个属性通常用于隐式启动。所以想要使用上面两个属性,我们还需要了解intent-filter过滤器。

intent-filter

我们在上面所写的代码例子都是通过显式意图来启动相关组件。显示意图一般都会给定指定的组件进行跳转,就是我们肉眼所能看见的,通过代码我们就知道该intent封装的是哪一个组件的属性。反之,隐式意图就是我们单单通过代码是看不出来要启动哪个组件的,那要怎么实现隐式意图呢?我们就需要intent-filter过滤器的帮忙。

intent-filter是一种根据某一个intent当中action、data以及category等属性,对适合接收这个intent组件进行匹配和筛选的机制。也就是说,当我们没有给Intent指定Component属性的时候,此时我们就只能通过action、category、data属性经过intent-filter进行过滤筛选出对应的组件。<intent-filter>....</intent-filter>是AndroidMainifest中<activity>...</activity>的一个子元素,其实我们在之前配置Activity时就有接触到intent-filter了。

可以看到,通过设置action为action.Main以及category为CATEGORY_LAUNCHER,此时MainActivity就作为该应用第一个启动的界面。下面我们来实现一个简单的隐式跳转:

<!--先在清单文件中进行配置--><activity android:name=".DemoActivity"android:label="DemoActivity" ><intent-filter><!--这个Action的name是我自己给的--><action android:name="com.example.DEMO_ACTIVITY"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></activity>

MainActivity里点击button触发

intent.setAction("com.example.DEMO_ACTIVITY");//intent.addCategory(Intent.CATEGORY_DEFAULT);startActivity(intent);

通过上面我们就可以实现隐式意图,来跳转到DemoActivity中,和上面的显示意图效果是一样的。代码中我将intent.addCategory(Intent.CATEGORY_DEFAULT);给注释掉了,如果没有给Intent对象添加Category时,会默认启动Category属性为Intent.CATEGORY_DEFAULT的组件,上面在清单文件中为DemoActivity配置了<category android:name="android.intent.category.DEFAULT"/>。还有上面清单文件中的action的name是我自己设定的,你给个hello也是ok的,只要在setAction的括号中写对应的就好。

需要注意的是,我们为Intent对象指定action的时候只能指定一个,所以是setAction;而category可以添加多个,所以是addCategory。其实在AndroidMainifest文件中我们可以在<intent-filter.../>中配置多个action和category,但Intent对象只能指定一个action,只要这个action符合了<intent-filter.../>配置的action的其中一个,并且category也符合就可以启动该组件。

可能看到这里,有些同学会发现这个隐式意图貌似没啥特点,反而好像比显式意图还麻烦了许多。那么我们现在就来看看隐式意图的特点:

//此时我给intent的action设置Intent.ACTION_DIAL常量,这个是Android内部提供的常量,//通过这个常量就可以跳转到系统的拨号界面intent.setAction(Intent.ACTION_DIAL);intent.addCategory(Intent.CATEGORY_DEFAULT);startActivity(intent);

跳转到拨号界面

上面点击Demo按钮后此时就不会再跳转到DemoActivity了,因为此时我设置给Intent对象的action、category属性所对应的组件已经不再是DemoActivity,而是系统的拨号界面。其实Android内部提供了大量标准的action、category常量,用来启动一些系统自带的程序组件例如电话,短信等,在这些系统应用的清单文件中是已经配置了对应的属性的。

再看一个例子:

//此时我为Action和Category所设置的两种属性对应的就是系统桌面(系统桌面其实也是一个应用)intent.setAction(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);startActivity(intent);

跳转到系统桌面

这样我们就不难看出,显式启动的话多用来实现一个应用内部的跳转,而隐式启动的话多用来实现应用与应用之间的跳转。

Data

译为数据;Data属性通常用来为Action提供操作的数据,也就是说Action负责跳转到指定的组件,Data就可以提供数据给跳转到的组件。上面我们实现了从自己的应用跳转到系统打电话应用的拨号界面,现在我们希望在跳转过去的同时,可以直接输入好电话号码到拨号界面上。这其实是很多app非常常见的一个功能,点击一个电话号码然后跳转到拨号界面并将号码输入到拨号界面上。

intent.setAction(Intent.ACTION_DIAL);Uri uri = Uri.parse("tel:123456789");intent.setData(uri);startActivity(intent);

跳转到拨号界面并自动输入好号码

我们可以看到,我们为Data属性设置了一个Uri对象。那么为什么通过parse(解析)"tel:123456789",就可以实现接下来的操作呢?下面来解释一下这个Uri对象。

其实这个Uri和我们所知道URI是差不多的,只是它们所在的包不同(Uri在包下,URI在包下)。在Android中Uri其实是扩展了JAVA中URI的一些功能来适用于Android开发。

Uri字符串的格式:scheme://host:port/path//举个例子,下面这一段表示手机通讯录中的第一个记录content://com.android.contacts/contacts/1​//所以通过这个解析这个uri字符串,再配合action中的ACTION_VIEW常量//我们就是可以显式手机通讯录中的第一个记录intent.setAction(Intent.ACTION_VIEW);intent.setData(Uri.parse("content://com.android.contacts/contacts/1"));startActivity(intent);

显式手机通讯录中的第一个记录

通过代码可以知道,content其实就对应着schemecom.android.contacts对应着host部分;port部分省略;/contacts/1对应path部分。而tel:123456789的tel对应的就是scheme部分。我们发现,这些uri字符串并不是完全按照格式一个一个对应的,有些可以不写也能执行相应的操作。

在解释这个之前,我们先要知道,我们也是可以在AndroidMainifest中在Activity的过滤器(<intent-filter.../>)配置到Data属性。并且这个<data.../>也包含着多个属性,其中就有对应着Uri格式中的那四个部分(scheme、host、port、path)。其实,我们在为Intent指定Data属性时,有着scheme、host、port、path这四个部分,此时并不需要被启动组件的<data.../>的四个部分全都要设置。

给Intent对象设置好action和data后,就会开始进行匹配。会先检查对应组件的<intent-filter.../>中的<action.../>子元素;然后就会开始检查<intent-filter.../>中的<data.../>子元素:

如果<data.../>子元素只指定了android:scheme属性,那么只要为Intent对象设置好的data属性的scheme部分是相同的,就可以启动该组件。

如果<data.../>子元素只指定了android:scheme和android:host属性,那么和上面一样,intent的data属性的scheme部分和host部分对应即可启动。

如果<data.../>子元素只指定了android:scheme、android:host属性和android:port属性,同上;或者指定了android:scheme、android:host属性和android:path属性,也是同上。也就是port和path有一个就可以,但是有android:port或者android:path时,前面一定要有指定android:host,否则就是无效。

如果四个属性全在<action.../>指定了,那么Intent对象设置好的data属性也要全部指定。

这样就可以解释为什么上面两个例子并不需要解析完整的uri字符串。因为在对应组件中的<intent-filter.../>中的<data.../>子元素并没设置全部的四个部分(scheme、host、port、path)。就像上面tel:123456789,其实在系统内部的电话应用中的AndroidMainifest的拨号的Activity下的<intent-filter.../>中的<data.../>子元素只设置了一个android:scheme=“tel”

Extra

Intent的Extra属性是用来在不同的Activity之间进行数据交换的,通过key-value(键值对)的形式。我们首先要知道Intent的这两个方法:

putExtras(): 用来把Bundle对象作为附加信息进行添加getExtras(): 获取bundle中保存的信息

Bundle对象其实就如同一个map对象,可以存入多个键值对形式的数据。

下面来实现一个简单的数据传递:

//MainActivityIntent intent = new Intent(MainActivity.this,DemoActivity.class);Bundle bundle = new Bundle();bundle.putString("value","123456789");intent.putExtras(bundle);startActivity(intent);​//DemoActivityIntent intent = getIntent();Bundle bundle = intent.getExtras();Log.d(TAG, "value---->"+ bundle.get("value"));

当我们点击按钮进行跳转后,可以发现数据确实传递了过去

上面因为只传递了一个数据,所以可以直接这样写:

//MainActivityIntent intent = new Intent(MainActivity.this,DemoActivity.class);intent.putExtra("value","123456789");startActivity(intent);​//DemoActivityIntent intent = getIntent();Log.d(TAG, "value---->"+ intent.getStringExtra("value"));

我们再来讲下数据回传。我们有时候跳转到一个Activity后,希望在返回时能得到这个Activity的返回的一些数据。就比如你先充值游戏币,你本来是在游戏界面的,然后你点击充值进入充值界面,输入金额点击确认充值又会回到游戏界面并且你的游戏界面上已经有了你充值的游戏币信息了。

最开始说了,Activity提供了两种方法来启动其它Activity,一个是startActivity(Intent intent),另外一个就是startActivityForResult(Intent intent, int requestCode)不同于startActivity,startActivityForResult多了一个requestCode的参数,翻译过来就是请求码。这个请求码是自己设定的。

如果想要实现数据回传,我们还需要先在当前的Activity重写onActivityResult(int requestCode, int resultCode, Intent intent) 这个回调方法。当被启动Activity返回结果时,这个方法就会被回调。我们可以看到该方法中有三个参数,第一个就是请求码,第二个是结果码。结果码就是你将要启动的Activity结束后所返回的结果码。

Actvity中还提供了一个setResult(int resultCode, Intent data)方法用来设置结果码。被启动的Activity需要调用这个方法来设置处理结果。那么为什么要设置请求码和结果码呢?

请求码 和 结果码 其实是方便执行不同的请求,因为在一个项目中,有可能你在当前Activity会对多个不同Activity发送请求,并且被请求的Activity可能在关闭后会返回不同的结果。当被请求的Activity被关闭时就会调用请求界面的onActivityResult方法,那么通过请求码我们可以知道onActivityResult方法是被哪个被请求的界面关闭所触发的;通过结果码我们可以知道被请求的Activity返回的哪个结果。

那么关闭Activity我们通常使用finish()就可以结束当前的Activity。

下面来看一个数据回传例子,功能主要是MainActivity有两个按钮,一个按钮是去到充值界面充值,充值界面也有两个按钮,一个是确认充值,点击后,将充值金额返回到MainActivity中显式;另一个是取消充值,点击后返回到MainActivity并显式充值失败。MainActivity中的第二个按钮是去到活动界面,去活动界面点击领取奖励按钮返回到MainActivity界面显式领取成功,这里不给出界面布局的代码,只给出部分逻辑处理的代码:

首先在MainActivity中

public void toDemo(View view) {//点击按钮去到充值界面Intent intent = new Intent(MainActivity.this, DemoActivity.class);//请求码这里我给了0startActivityForResult(intent,0);}​public void toActivity(View view){//点击按钮去到查看活动界面Intent intent = new Intent(MainActivity.this, Demo2Activity.class);//请求码这里我给了1startActivityForResult(intent,1);}/*** 重写onActivityResult方法*/@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {//处理充值界面返回结果if (requestCode == 0 ) {if(resultCode == 0){//当结果码是0时,就说明是充值成功的了。这个结果码时我在DemoActivity中自己给的if (intent != null) {String gameCoin = intent.getStringExtra("coin");mGameCoin.setText(gameCoin);}}else{// 当结果码不为0时,说明充值失败Toast.makeText(MainActivity.this,intent.getStringExtra("cancel"),Toast.LENGTH_LONG).show();}}//处理查看活动界面返回结果if (requestCode == 1){if (resultCode == 0) {Toast.makeText(MainActivity.this,intent.getStringExtra("get"),Toast.LENGTH_LONG).show();}}

在DemoActivity的onCreate中

mEdMoney = findViewById(R.id.ed_money);Button recharge = findViewById(R.id.btn_recharge);Button cancel = findViewById(R.id.btn_cancel);//确认充值按钮recharge.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent();//获取edittext里的数据intent.putExtra("coin", mEdMoney.getText().toString().trim());//调用setResult方法设置结果码,这里我给了0DemoActivity.this.setResult(0, intent);//结束当前ActivityDemoActivity.this.finish();}});//取消充值按钮cancel.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent();intent.putExtra("cancel","充值失败");//调用setResult方法设置结果码,这里我给了1,此时不为0,对应的就是充值失败DemoActivity.this.setResult(1, intent);DemoActivity.this.finish();}

在Demo2Activity的中

//点击领取奖励按钮事件Intent intent = new Intent();intent.putExtra("get","领取成功");setResult(0, intent);finish();

上面就是数据回传了,通过requestCode来判断是从哪个Activity回来的,通过resultCode来判断被请求的Activity返回的是哪个结果。

Flag

Flag属性用于为Intent添加一些额外的控制旗帜,主要是用来指示安卓程序应该如何去启动另一个Activity,类似之前所讲的Activity启动模式。可以调用addFlags()来添加控制旗帜。

常用的Flag旗帜:

FLAG_ACTIVITY_NEW_TASK:默认启动旗帜,设置这个旗帜后,每次跳转Activity都会重新创建一个新的Activity放到任务栈的栈顶。就像启动模式中standard标准模式。

FLAG_ACTIVITY_CLEAR_TOP:类似于启动模式中的singleTask,就是栈中不会有重复的Activity。

FLAG_ACTIVITY_BROUGHT_TO_FRONT:就是将要启动的Activity带到栈顶,然后原本在栈顶的Activiy往后退。

那么除了以上的旗帜,还有很多别的旗帜,这里就不细讲了。

3.总结

上面主要讲解了Intent的各种属性,并且也讲解Activity的跳转与数据传递。内容并不少,但其实并不需要去全部记住。主要是要分清楚显示意图和隐式意图,并且知道什么时候使用哪个意图更加合适。以及熟悉Activity之间数据传递的使用,这个也是用的特别多的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。