本篇我们增加一个页面用来显示试题的正确答案,通过这个例子来简单了解下如何启动一个Activity以及Intent如何进行传参。
目前我们的程序中只有用户的答案,启动一个Activity来显示一个ABCD似乎太过单调了,所以我们为每道试题增加一个解析内容。
strings.xml
<string name="Q1_D">垃圾回收是指:虚拟机会监测应用程序的对象创建和使用,并在一些特定的时候销毁无用的对象以回收内存。垃圾回收的基本想法是要找出虚拟机中哪些对象已经不会再被使用然后将其释放。</string>
<string name="Q2_D">运行时异常是在Java虚拟机的正常运行期间可以抛出的那些异常</string>
<string name="Q3_D">round 表示"四舍五入",算法为Math.floor(x+0.5) ,即将原来的数字加上 0.5 后再向下取整,所以 Math.round(11.5) 的结果为 12,Math.round(-11.5) 的结果为 -11。</string>
<string name="Q4_D">onstart()在活动变得对用户可见时调用</string>
同理我们修改Question类,增加一个字段来表示试题解析内容的字符串资源的id,这里只列出增加的内容
private int mAnswerDescription;
public int getmAnswerDescription() {
return mAnswerDescription;
}
public void setmAnswerDescription(int mAnswerDescription) {
this.mAnswerDescription = mAnswerDescription;
}
/**
* @param context 上下文菜单
* 初始话,根据题目资源id,查找对应选项以及正确答案的资源id
*/
private void initQuestion(Context context) {
// 根据id获取名称
String mQuestionDescriptionName = getQuestionEntryName(context, mQuestionDescription);
....
String mAnswerDescriptionName = mQuestionDescriptionName.concat("_D");
//根据名称获取对应的资源id
......
mAnswerDescription = getResourceId(context, mAnswerDescriptionName);
}
ok,准备工作完成,接下来我们需要在Android Studio中新建一个Activity。
输入Activity名称并勾选创建布局文件选项,这样的话Android Studio就会自动为我们生成AnswerActivity.java 和activity_answer.xml文件。
要想使用我们新创建的AnswerActivity,那么必须在AndroidManifest.xml中进行注册,这一步Android Studio也帮我们完成了,只要记得有这么一步骤就好了。
在activity_main.xml中增加按钮,当点击该按钮的时候启动AnswerActivity显示。
<Button
android:id="@+id/bt_answer_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="@string/question_answer_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bt_previous" />
当然了,别忘记在strings.xml中添加question_answer_description资源:
<string name="question_answer_description">查看答案</string>
MainActivity.java中为查看答案按钮增加点击事件:
private Button bt_answer_description;
....
private void findView() {
......
bt_answer_description = findViewById(R.id.bt_answer_description);
}
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
.......
//查看答案监听器
bt_answer_description.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showAnswer();
}
});
}
/**
* 显示AnswerActivity
*/
private void showAnswer() {
}
启动Activity可以通过下面两个方法来实现:
void startActivity (Intent intent)
public void startActivityForResult(android.content.Intent intent,
int requestCode)
intent:将要启动的intent
requestCode:请求码,如果该值大于0,那么在被调用的Acitivity销毁时会将该值返回到onActivityResult中。requestCode主要用来区分是哪一个Activity返回。
注意:requestCode必须是一个唯一值,并且requestCode <= 0xffff,一般是65535.
这里又有一个Intent的概念,我们这里简单介绍下。
Intent 是 Android 一个常用的用于组件间互相通信的信息对象,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间进行数据传递。
Intent 又可以分为下面两种:
显式Intent
:通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理 Intent 的应用。通常,我们会在自己的应用中使用显式 Intent 来启动组件,这是因为我们知道要启动的 Activity 或服务的类名。
隐式 Intent
:不指定特定的组件,但必须声明要执行的常规操作,从而允许其他应用中的组件来处理。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。
隐式 Intent 指定能够在可以执行相应操作的设备上调用任何应用的操作。如果您的应用无法执行该操作而其他应用可以,且您希望用户选取要使用的应用,则使用隐式 Intent 非常有用。
在我们使用Intent的过程中,推荐使用resolveActivity来验证 Activity 是否会接收 Intent。如果结果为非空,则至少有一个应用能够处理该 Intent,并且可以安全调用startActivity()。如果结果为空,不要使用该 Intent。
常见的Intent构造函数:
Intent(Context, Class)
Context:应用和组件提供 Context
Class:Class 对象
接下来我们通过具体代码来看下怎么启动我们的AnswerActivity。
/**
* 显示AnswerActivity
*/
private void showAnswer() {
Intent intent = new Intent(MainActivity.this, AnswerActivity.class);
startActivity(intent);
}
代码很简单,构造一个Intent用来指定启动的Activity,然后调用startActivity方法。
此时我们运行程序:
点击查看答案按钮,那么屏幕会显示一个空白的页面,当然了这是没有问题的。我们还没有给我们的AnswerActivity布局添加组件呢。
AnswerActivity布局文件activity_answer:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AnswerActivity">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:text="正确答案:"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
app:layout_constraintStart_toEndOf="@id/tv1"
app:layout_constraintTop_toTopOf="@id/tv1" />
<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:text="答案解析:"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/tv4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv1" />
<TextView
android:id="@+id/tv4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv3"
app:layout_constraintTop_toTopOf="@id/tv3" />
<Button
android:id="@+id/bt_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="返回"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv4" />
</androidx.constraintlayout.widget.ConstraintLayout>
文件很简单,显示正确答案和解析,另外还有一个返回按钮,点击后回到答题页面。
同MainActivity我们在AnswerActivity中给对应的组件生成变量和相应的事件监听:
package com.qiushangge.androidstudy;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class AnswerActivity extends AppCompatActivity {
private TextView tv2;
private TextView tv4;
private Button bt_back;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_answer);
// 获取组件
findView();
//初始化习题列表
displayAnswer();
// 返回MainActivity监听器
bt_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
backToMain();
}
});
}
/**
* 接收数据并显示
*/
private void displayAnswer() {
tv2.setText("");
tv4.setText("");
}
/**
* 获取组件
*/
private void findView() {
tv2 = findViewById(R.id.tv2);
tv4 = findViewById(R.id.tv4);
bt_back = findViewById(R.id.bt_back);
}
/**
* 返回答题页面
*/
private void backToMain() {
}
}
先不看布局文件,那么MainActivity如何向AnswerActivity传递数据呢?
Intent传递数据可以使用下面的方式:
Intent.putExtra(键名,键值)
在MainActivity中,我们希望能够将正确答案和解析的id传递给AnswerActivity,从而让AnswerActivity能够正确的显示试题的答案和解析内容。
首先我们定义两个常量:
private static final String QUESTION_ANSWER = "com.qiushangge.androidstudy.question_answer";
private static final String QUESTION_ANSWER_DESCRIPTION = "com.qiushangge.androidstudy.question_answer_description";
这两个常量是用作putExtra方法中的键名,为了避免我们的这个名字重复,Android官方推荐使用包名+名称的形式,形式如上。
接着在启动AnswerActivity前使用putExtra方法添加需要传递的数据:
private void showAnswer() {
Intent intent = new Intent(MainActivity.this, AnswerActivity.class);
intent.putExtra(QUESTION_ANSWER, mQuestionList.get(mCurrentQuestion).getmAnswer());
intent.putExtra(QUESTION_ANSWER_DESCRIPTION, mQuestionList.get(mCurrentQuestion).getmAnswerDescription());
startActivity(intent);
}
数据已经传出去了,此时需要在AnswerActivity中获取Intent传递过来的数据:
/**
* 接收数据并显示
*/
private void displayAnswer() {
Intent intent = getIntent();
int answer = intent.getIntExtra(QUESTION_ANSWER,-1);
int description = intent.getIntExtra(QUESTION_ANSWER_DESCRIPTION,-1);
tv2.setText(answer);
tv4.setText(description);
}
当然了,别忘了把MainActivity中的常量定义拿过来:
private static final String QUESTION_ANSWER = "com.qiushangge.androidstudy.question_answer";
private static final String QUESTION_ANSWER_DESCRIPTION = "com.qiushangge.androidstudy.question_answer_description";
注意:Intent 为我们提供了一系列的get方法,使用时根据数据类型选择对应的方法。
另外我们还可以通过Bundle对象传递,就是简单的键值对,这里不再细说。
Bundle bundle = new Bundle();
bundle.putString("Name", name);
bundle.putString("Age", age);
intent.putExtras(bundle);
通过Parcelable传递类对象
Parcelable是Android为我们提供的序列化的接口。
序列化:将对象转换为可以传输的二进制流(二进制序列)的过程,这样我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 。
反序列化:从二进制流(序列)转化为对象的过程。
如果要使用这种方式,首先我们需要将Question类实现Parcelable 接口。
public class Question implements Parcelable
修改后的Question增加了如下内容:
protected Question(Parcel in) {
mQuestionDescription = in.readInt();
mAnswer = in.readInt();
mAnswerA = in.readInt();
mAnswerB = in.readInt();
mAnswerC = in.readInt();
mAnswerD = in.readInt();
mAnswerDescription = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mQuestionDescription);
dest.writeInt(mAnswer);
dest.writeInt(mAnswerA);
dest.writeInt(mAnswerB);
dest.writeInt(mAnswerC);
dest.writeInt(mAnswerD);
dest.writeInt(mAnswerDescription);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Question> CREATOR = new Creator<Question>() {
@Override
public Question createFromParcel(Parcel in) {
return new Question(in);
}
@Override
public Question[] newArray(int size) {
return new Question[size];
}
};
这里推荐大家使用Android Parcelable code generator插件,它能够快速为我们实现Parcelable接口,就像上面的代码。
修改MainActivity中的showAnswer方法,传递Question类:
private void showAnswer() {
Intent intent = new Intent(MainActivity.this, AnswerActivity.class);
// intent.putExtra(QUESTION_ANSWER, mQuestionList.get(mCurrentQuestion).getmAnswer());
// intent.putExtra(QUESTION_ANSWER_DESCRIPTION, mQuestionList.get(mCurrentQuestion).getmAnswerDescription());
intent.putExtra(QUESTION_ANSWER,mQuestionList.get(mCurrentQuestion));
startActivity(intent);
}
AnswerActivity中改变接收姿势:
private void displayAnswer() {
Intent intent = getIntent();
Question question = intent.getParcelableExtra(QUESTION_ANSWER);
// int answer = intent.getIntExtra(QUESTION_ANSWER,-1);
// int description = intent.getIntExtra(QUESTION_ANSWER_DESCRIPTION,-1);
// tv2.setText(answer);
// tv4.setText(description);
tv2.setText(question.getmAnswer());
tv4.setText(question.getmAnswerDescription());
}
关于Parcelable我们这里不再讨论,只要知道我们传递类对象的时候需要实现这个接口即可。如果不会的话,那么就找个插件吧,可以节省很多时间的。。
接下来就是本篇的最后一个知识点了,使用startActivityForResult
启动Activity并接收返回值。
public void startActivityForResult(android.content.Intent intent,
int requestCode)
intent:将要启动的intent
requestCode:请求码,如果该值大于0,那么在被调用的Acitivity销毁时会将该值返回到onActivityResult中。requestCode主要用来区分是哪一个Activity返回。
注意:requestCode必须是一个唯一值,并且requestCode <= 0xffff,一般是65535.
此时在MainActivity中我们可以这样使用:
/**
* 显示AnswerActivity
*/
private void showAnswer() {
Intent intent = new Intent(MainActivity.this, AnswerActivity.class);
// intent.putExtra(QUESTION_ANSWER, mQuestionList.get(mCurrentQuestion).getmAnswer());
// intent.putExtra(QUESTION_ANSWER_DESCRIPTION, mQuestionList.get(mCurrentQuestion).getmAnswerDescription());
intent.putExtra(QUESTION_ANSWER, mQuestionList.get(mCurrentQuestion));
if (intent.resolveActivity(getPackageManager()) != null) {
// startActivity(intent);
startActivityForResult(intent,1);
}
}
注意:推荐使用resolveActivity来验证 Activity 是否会接收 Intent。如果结果为非空,则至少有一个应用能够处理该 Intent,并且可以安全调用startActivity()。如果结果为空,不要使用该 Intent。
同时我们还需要重写方法onActivityResult。
函数定义:
protected void onActivityResult(int requestCode,
int resultCode,
@Nullable Intent data)
参数说明:
requestCode: 启动Activity时传入的请求码
resultCode:返回数据时传入的处理结果
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 message = data.getStringExtra(BACK_MESSAGE);
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
这里从AnswerActivity返回一个字符串数据,使用Toast显示。
实现AnswerActivity的返回方法backToMain:
/**
* 返回答题页面
*/
private void backToMain() {
Intent intent = new Intent();
intent.putExtra(BACK_MESSAGE, "我已经看过答案了,看来我要答对了。");
setResult(RESULT_OK, intent);
finish();
}
BACK_MESSAGE定义:
private static final String BACK_MESSAGE = "com.qiushangge.androidstudy.back_message";
setResult方法:
void setResult (int resultCode, Intent data)
int resultCode 结果返回码,通常为RESULT_CANCELED 和 RESULT_OK,可以自定义
Intent data 携带返回数据的Intent
运行程序:
好了,本篇就如何启动Activity和使用Intent传递数据的使用姿势做了简单的介绍,如果大家想继续深造的话,就得自己在多找找大神们的作品了。。