本篇我們增加一個頁面用來顯示試題的正確答案,通過這個例子來簡單瞭解下如何啓動一個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傳遞數據的使用姿勢做了簡單的介紹,如果大家想繼續深造的話,就得自己在多找找大神們的作品了。。