在上一次讲述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跳转到DemoActivity
ComponentName 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:打开项目后第一个启动的Activity
ACTION_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在http://android.net包下,URI在http://java.net包下)。在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对象,可以存入多个键值对形式的数据。

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

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

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

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

//MainActivity
Intent intent = new Intent(MainActivity.this,DemoActivity.class);
intent.putExtra("value","123456789");
startActivity(intent);
​
//DemoActivity
Intent 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方法*/
@Override
protected 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之间数据传递的使用,这个也是用的特别多的。

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

  1. android studio 跳转后保留原页面数据_这些技巧和习惯,让你的原生 Android 手机更好用(上篇)...

    从此前的 EMUI 9 到尚未正式推出的 ColorOS 6,硬件上难以突破技术限制的国产 Android 手机厂商,近来都开始在软件体验,即定制安卓 ROM 上大做文章.国内长久以来糟糕的安卓生态体 ...

  2. vue跳转页面之后返回_vue页面跳转后返回原页面初始位置方法

    vue页面跳转到新页面之后,再由新页面返回到原页面时候若想返回调出原页面的初始位置,怎么来解决这个问题呢?首先我们应该在跳出页面时候记录下跳出的scrollY,返回原页面的时候在设置返回位置为记录下的 ...

  3. Android Studio CPU profiler性能分析工具介绍和使用详解

    Android Studio CPU profiler性能分析工具介绍和使用详解 CPU profiler介绍 Android Studio CPU 性能剖析器可实时检查应用的 CPU 使用率和线程活 ...

  4. Android studio 多渠道(多环境)打包grade配置详解

    Android studio 多渠道(多环境)打包grade配置详解 场景:开发app,我们需要两套环境或者两套环境以上的apk,每套环境的apk分两个版本debug版和release版. 公司有套平 ...

  5. Android Studio打不开虚拟机,两种情况详解

    Android Studio打不开虚拟机,两种情况详解 文章目录 Android Studio打不开虚拟机,两种情况详解 1.VT-x is disabled in BIOS 2.Emulator文件 ...

  6. HTML页面跳转后返回原页面,H5跳转链接之后,返回原界面

    H5的发布链接本身是一个网页,当用户从H5的第3页跳转到另一个链接,相当于从一个网页跳到另一个网页,这时如果用户点返回,浏览器会直接让用户访问H5的第一页,而不是访问之前的第3页. 如果要让用户返回时 ...

  7. vue页面跳转后返回原页面初始位置

    vue页面跳转到新页面之后,再由新页面返回到原页面时候若想返回调出原页面的初始位置,怎么来解决这个问题呢?首先我们应该在跳出页面时候记录下跳出的scrollY,返回原页面的时候在设置返回位置为记录下的 ...

  8. Android Studio 中的 gradle 介绍及build.gradle配置文件详解

    不同于 Eclispse,Android Studio 是采用 Gradle 来构建项目的,Gradle 是一个非常先进强大的项目构建工具,它使用了一种基于 Groovy 领域的特定语言(DSL)来声 ...

  9. Android Studio 安装AVD (处理器为AMD)图文详解

    最近帮一个朋友配置AS,起初一切很正常,从sdk的安装,到grade自动构建工具的引入,但到虚拟机启动时,给我干懵了,不管怎么样尺寸的虚拟机,就是启动不了,显示程序终止. 查了很多资料,基本都是说电脑 ...

最新文章

  1. MindMotion MM32 单片机上的MicroPython移植-PWM
  2. img下面的png图片 vs 读不出来_VUX中XImg组件加载图片不正确,BusPlugin不好使,求解...
  3. OpenKruise v0.8.0 版本发布:K8s 社区首个规模化镜像预热能力
  4. Case Study. Technical and Commercial understating. Internal use only.
  5. 《深入浅出数据分析》第十三章
  6. 【opencv有趣应用】二维码和条形码的检测
  7. Unity Behaviors for Interception
  8. 释放空间后将指针置空
  9. 机器学习实战 利用sklearn库预测科比生涯数据
  10. 全国各地区域码 --- (当地身份证号前六位)
  11. 早停法的应用(keras)
  12. ntp服务restrict_ntp服务器配置问题
  13. PROE_CONFIG
  14. 融资融券的交易成本有哪些?
  15. 使用ftp访问资源管理器托拽文件下载出现“当前的安全设置不允许从该位置下载文件”提示
  16. 本地计算机无法启动dhcp服务,提示Windows无法启动DHCP Client服务如何解决
  17. MySQL5.7修改root账户密码
  18. linux su 资源不可用,CentOS 6/Linux su: 无法设置用户ID: 资源暂时不可用
  19. 教你小小JAVA爬虫爬到HDU首页(只为学习)
  20. 如何让电脑在指定时间内自动播放和关闭音乐

热门文章

  1. 转载:JQuery制作的选项卡改进版
  2. 如何在Kubernetes里创建一个Nginx service
  3. Elasticsearch - 索引管理
  4. 简单看看PageHelper的配置
  5. 十大响应式Web设计框架
  6. 【原创】TimeSten安装与配置
  7. 用计算机写作ppt文库,[百度文库.ppt
  8. android fersco 框架,Android Fresco框架的简单使用
  9. python os 获取当前路径的绝对路径的上层目录_os 模块常用函数
  10. 双目标帕累托优化_结构力学中的优化分析(3) —— 结构优化分析