android学习笔记----隐式意图和显式意图
目录
使用意图在第二个activity回传数据给第一个activity
扩展阅读(国外网站)Android意图教程:http://www.vogella.com/tutorials/AndroidIntent/article.html#usingintents_call
隐式意图和显式意图:
显式意图:显式意图明确指明了启动活动的上下文和想要启动的目标活动,显式意图明确指定了Intent应该传递给哪个组件。
隐式意图:没有明确指定组件名的Intent为隐式意图。 Android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URI和数据类型)找到最合适的组件来处理这个意图。
开启自己应用的界面用显式意图,开启其他应用(一般指系统应用)的时候用隐式意图(比如拨打电话)。
显式意图安全一些,隐式意图可以通过匹配intent-filter里面的标签对应来跳转到相应的页面 。
关于更多intent参见官方文档:https://developer.android.google.cn/guide/components/intents-common
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testactivity">
<uses-permission android:name="android.permission.CALL_PHONE" />
<application
android:allowBackup="true"
android:icon="@drawable/kakaxi"
android:label="@string/app_name"
android:roundIcon="@drawable/mingren"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="第一个activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".TestActivity"
android:label="第二个activity">
<intent-filter>
<action android:name="mytestAction" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="aa/bb"
android:scheme="mytestscheme" />
</intent-filter>
</activity>
<activity android:name=".Test3" />
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/id_btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onclick"
android:text="拨打电话" />
<Button
android:id="@+id/id_btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onclick"
android:text="隐式意图跳转到TestActivity" />
<Button
android:id="@+id/id_btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onclick"
android:text="显式意图跳转到TestActivity" />
</LinearLayout>
MainActivity.java
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onclick(View view) {
switch (view.getId()) {
case R.id.id_btn1:
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);
} else {
call();
}
break;
case R.id.id_btn2:
// 创建意图对象
Intent intent = new Intent();
// 以下隐式意图都可以自定义
// 设置拨打的动作
intent.setAction("mytestAction");
// 设置category
intent.addCategory("android.intent.category.DEFAULT");
// 设置数据和基本类型
intent.setDataAndType(Uri.parse("mytestscheme:"), "aa/bb");// 一定要有冒号:可以后面随便加个数,和tel:类似
// 主要是为了匹配清单文件的约束
// 开启Activity,记得加上权限
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
break;
case R.id.id_btn3:
Intent intent1 = new Intent(MainActivity.this, Test3.class);
// 如果是构造的空参intent,则设置包名和类名,或者构造2个参数intent
//intent1.setClassName("com.example.testactivity", "com.example.testactivity.Test3");
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent1);
}
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
call();
} else {
Toast.makeText(this, "you denied the permision", Toast.LENGTH_SHORT).show();
}
break;
}
}
private void call() {
// 创建意图对象
Intent intent = new Intent();
// 设置拨打的动作
intent.setAction(Intent.ACTION_CALL);
// 设置拨打的数据
intent.setData(Uri.parse("tel:" + 119));
// 开启Activity,记得加上权限
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
}
批注:
按钮1,2为了演示隐式意图,按钮3演示显式意图
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
我们要检查一下设备上是否存在至少一个组件可以处理这个intent,才会去执行startActivity(intent);否则不执行,避免崩溃。
关于隐式意图:
在case R.id.id_btn2:中
intent.setData(data)和intent.setType(type)注意这两个方法会互相清除,意思就是:如果先设置setData(data)后设置setType(type),那么后设置的setType(type)会把前面setData(data)设置的内容清除掉,而且会报错,反之一样,所以如果既要设置类型与数据,那么使用public Intent setDataAndType(Uri data, String type) 这个方法。看源码很好理解
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
setData会将mimetype置为null, setType会将URI置为null。
可以配置多个意图过滤器,只要能够完整的匹配任何一个意图过滤器intent-filter,就可以跳转到那个activity
如果intent-filter里面只有<action>和<category>标签,那么只有<action>和<category>中的内容同时能够匹配上Intent中指定的action和category时,这个活动才能响应这个Intent。如果还有data标签,也要一一对应才行。
因为使用隐式意图的Intent中会添加默认的Category,所以隐式意图必须有
<category android:name="android.intent.category.DEFAULT" />这个标签,那么没有手动addCategory,也会自动添加该category,所以必须写。
每个Intent对象中只能指定一个action(setAction),却能指定多个category(addCategory)。
关于data标签:
为什么设置数据和基本类型时intent.setDataAndType(Uri.parse("mytestscheme:"), "aa/bb");
这里一定要有冒号:后面可以随便加个数,或者不加,和tel:类似,这里不是拨打电话,所以可以不加,主要为了符合约束条件
关于显式意图:
在case R.id.id_btn3:中
Intent intent1 = new Intent(MainActivity.this, Test3.class);
startActivity(intent1);
等价于<===========>
Intent intent1 = new Intent();
intent1.setClassName("com.example.testactivity", "com.example.testactivity.Test3");
startActivity(intent1);
一般写上面那一种就可以了。
TestActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout2);
}
}
Test3.java
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
public class Test3 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout3);
}
}
layout2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第二个activity"/>
</LinearLayout>
layout3.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第三个activity"/>
</LinearLayout>
使用意图在第二个activity回传数据给第一个activity
实验要求:编程实现具有“登录”按钮的主界面,点击“登录”按钮后打开另一个新的Activity,在新打开的Activity中输入的用户名、密码等个人信息,在用户关闭这个Activity后,将已输入的用户名、密码等个人信息 传回主界面Activity中并进行显示。
运行效果图:
MainActivity.java
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private static final int REQUESTCODE = 1;
private TextView tv_name;
private TextView tv_password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_name = (TextView) findViewById(R.id.id_name);
tv_password = (TextView) findViewById(R.id.id_password);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUESTCODE && resultCode == RESULT_OK) {
tv_name.setText("name: " + data.getStringExtra("name"));
tv_password.setText("password: " + data.getStringExtra("password"));
}
}
public void click(View view) {
Intent intent = new Intent(this, SecondActivity.class);
startActivityForResult(intent, REQUESTCODE);
}
}
SecondActivity.java
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;
public class SecondActivity extends AppCompatActivity {
private EditText et_name;
private EditText et_password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
et_name = (EditText) findViewById(R.id.id_name);
et_password = (EditText) findViewById(R.id.id_password);
}
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("name", et_name.getText().toString());
intent.putExtra("password", et_password.getText().toString());
setResult(RESULT_OK, intent);
finish();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="click"
android:text="登录" />
<TextView
android:id="@+id/id_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp" />
<TextView
android:id="@+id/id_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp" />
</LinearLayout>
activity_second.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="name: "
android:textSize="24sp" />
<EditText
android:id="@+id/id_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:ems="50"
android:maxLength="10"
android:maxLines="1"
android:hint="请输入姓名"
android:inputType="textPersonName"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="password: "
android:textSize="24sp" />
<EditText
android:id="@+id/id_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:hint="请输入密码(不超过16位)"
android:ems="50"
android:maxLength="16"
android:maxLines="1"
android:textSize="24sp"
android:inputType="textPassword"/>
</LinearLayout>
</LinearLayout>
使用意图在activity之间传递数据(人品计算器):
MainActivity.java
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private EditText et_name;
private RadioGroup rg_group;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = (EditText) findViewById(R.id.et_name);
rg_group = (RadioGroup) findViewById(R.id.radioGroup);
}
public void click(View view) {
// 获取用户名
String name = et_name.getText().toString().trim();
if (TextUtils.isEmpty(name)) {
Toast.makeText(this, "亲,请输入姓名", Toast.LENGTH_SHORT).show();
return;
}
// 判断用户性别
int id = rg_group.getCheckedRadioButtonId();
int sex = 0;
switch (id) {
case R.id.rb_male:
sex = 1;
break;
case R.id.rb_female:
sex = 2;
break;
case R.id.rb_other:
sex = 3;
break;
}
if (sex == 0) {
Toast.makeText(this, "请选择性别", Toast.LENGTH_SHORT).show();
return;
}
// 跳转到ResultActivity页面,用显式意图跳转
Intent intent = new Intent(this, ResultActivity.class);
// 传递姓名
intent.putExtra("name", name);
// 传递性别
intent.putExtra("sex", sex);
// 如果希望在活动销毁的时候能够返回一个结果给上一个活动,就用startActivityForResult
startActivityForResult(intent, 1); // 请求码为1
}
// 在活动销毁后返回上一个活动的onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK){
int returnedData = data.getIntExtra("score", 0); // 默认为0
Toast.makeText(this, "您的测试分数为:" + returnedData, Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
批注:
public void startActivityForResult (Intent intent, int requestCode)
和打电话一样startActivityForResult(Intent, int, Bundle)
没有选择。
参数 | |
---|---|
intent |
Intent: 开始的意图。
|
requestCode |
int :如果>=0,则当活动退出时,此代码将在onActivityResult()中返回。 |
抛出 | |
---|---|
android.content.ActivityNotFoundException |
protected void onActivityResult (int requestCode, int resultCode, Intent data)
当您启动的活动退出时调用,为您提供requestCode,启动它,返回resultCode,以及来自它的任何其他数据。如果活动显式返回,则不会返回任何结果,或者在操作期间崩溃,resultCode将为RESULT_CANCELED。
当您的活动重新启动时,您将在onResume()之前立即收到此调用。
如果您的活动将noHistory设置为true,则永远不会调用此方法。
参数 | |
---|---|
requestCode |
int :最初提供给startActivityForResult()的整数请求代码,允许您识别此结果的来源。
|
resultCode |
int: 子活动通过其setResult()返回的整数结果代码。 |
data |
Intent :一个Intent,它可以将结果数据返回给调用者(各种数据可以附加到Intent“extras”)。 |
由于在一个活动中有可能调用startActivityForResult()方法去启动很多不同的活动,每一个活动返回的数据都会回调到onActivityResult()这个方法中,因此我们首先要做的就是通过resultCode的值来判断数据来源,确定这个是由哪一个活动返回的数据。
ResultActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
public class ResultActivity extends AppCompatActivity {
private TextView tv_sex;
private TextView tv_name;
private TextView tv_result;
private int score;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_result);
tv_name = (TextView) findViewById(R.id.tv_name);
tv_sex = (TextView) findViewById(R.id.tv_sex);
tv_result = (TextView) findViewById(R.id.tv_result);
// 获取MainActivity窜地过来的数据
Intent intent = getIntent();// 获取开启此activity的意图对象
// 获取name和sex的值
// 小技巧:传递的是什么数据类型,这边就按照传递的数据类型取
String name = intent.getStringExtra("name");
int sex = intent.getIntExtra("sex", 0);
// 根据name和sex显示数据
tv_name.setText(name);
byte[] bytes = null;
try {
// 显示性别
switch (sex) {
case 1:
tv_sex.setText("男");
bytes = name.getBytes("gbk");
break;
case 2:
tv_sex.setText("女");
bytes = name.getBytes("utf-8");
break;
case 3:
tv_sex.setText("人妖");
bytes = name.getBytes("iso8859-1");
break;
}
} catch (Exception e) {
e.printStackTrace();
}
int total = 0;
for (byte b : bytes) {
int number = b & 0xff;
total += number;
}
// 获取得分
score = Math.abs(total) % 100;
if (score > 90) {
tv_result.setText("你是世人的榜样!");
} else if (score > 80) {
tv_result.setText("你的人品不错..应该一表人才吧?");
} else if (score > 60) {
tv_result.setText("你有较好的人品..继续保持..");
} else if (score > 50) {
tv_result.setText("老实交待..那些论坛上面经常出现的偷拍照是不是你的杰作?");
} else if (score > 30) {
tv_result.setText("你的人品太差了。你应该有干坏事的嗜好吧?");
} else if (score > 20) {
tv_result.setText("杀过人没有?放过火没有?你应该无恶不做吧?");
} else if (score > 10) {
tv_result.setText("是我不好...不应该跟你谈人品问题的...");
} else {
tv_result.setText("不好意思,无法识别人品二字");
}
}
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("score", score);
setResult(RESULT_OK, intent);
finish();
}
}
批注:
当用户按下Back后退键,就回去执行onBackPressed()方法中的代码,我们重写这个方法再添加逻辑就行了。
这个activity接着在Manifest中添加<activity android:name=".ResultActivity" />就行了
activity_result.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="张三"/>
<TextView
android:id="@+id/tv_sex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="男"/>
<TextView
android:id="@+id/tv_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="人品好"/>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:hint="请输入姓名"/>
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男" />
<RadioButton
android:id="@+id/rb_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女" />
<RadioButton
android:id="@+id/rb_other"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="人妖" />
</RadioGroup>
<Button
android:onClick="click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="计算"/>
</LinearLayout>
扩展阅读(官方文档):
URI是统一资源标识符,而URL是统一资源定位符。因此,抽象地说,每个URL都是一个URI,但不是每个URI都是一个URL。这是因为URI的另一个子类别,统一资源名称(URN),它们命名资源但不指定如何定位它们。
URI和URL之间的概念区别反映在此类和URL类之间的差异中。
此类的实例表示RFC 2396定义的语法意义上的URI引用.URI可以是绝对的或相对的。根据通用语法解析URI字符串,而不考虑它指定的方案(如果有的话)。不执行主机查找(如果有),并且不构造依赖于方案的流处理程序。严格按照实例的字符内容定义等同,散列和比较。换句话说,URI实例只不过是一个结构化字符串,它支持比较,规范化,解析和相对化的语法,与方案无关的操作。
相反,URL类的实例表示URL的语法组件以及访问其描述的资源所需的一些信息。 URL必须是绝对的,也就是说,它必须始终指定方案。根据其方案解析URL字符串。始终为URL建立流处理程序,实际上,如果没有可用的处理程序的方案,无法为其创建URL实例。平等和散列取决于主机的方案和互联网地址,如果有的话;比较没有定义。换句话说,URL是一种结构化字符串,它支持解析的语法操作以及查找主机和打开与指定资源的连接的网络I / O操作。
关于意图的小Demo
Demo地址:https://github.com/liuchenyang0515/shiyan4_1
实验要求:
根据要求的界面,实现以下功能:
(a) 当点击“添加数据”按钮时,用户当前输入的“姓名、年龄和身高”这三条数据请保存到ArrayList集合中,并使用Toast控件显示“数据已保存!”的消息提示框,然后清空界面上已输入的数据,并准备接受下一次的输入;
(b) 当点击“全部显示”按钮时,界面跳转到下一个界面(第二个Activity),这该界面中显示前面已多次输入的“姓名、年龄和身高”的数据(多条记录,这些数据存放在上一步的集合中);提示:可以使用ListView分多行显示数据,每行显示一条记录;
(c) 当点击“清除显示”按钮时,清除当前正在输入的数据,即清空“姓名、年龄和身高”这三个控件中的输入,等待重新输入;
(d) 当点击“全部删除”按钮时,清空集合中的数据,并使用Toast控件显示“全部数据已经删除!”的消息提示框。
运行图如下:
=========================Talk is cheap, show me the code=========================