第 5 章 第二个 activity
請參考教材,全面理解和完成本章節內容... ...
復制第四章的工程ch4,將工程目錄改名為ch5。
之前的GeoQuiz應用只有一個activity,它負責管理著主界面。本章,將為應用增加名為CheatActivity的第二個activity,它管理第二個用戶界面,可利用此界面查看當前問題的答案,如圖5-1所示。
圖5-1 CheatActivity提供了偷看答案的機會
如果用戶選擇先查看答案,然后再返回QuizActivity回答問題,則會收到一條新的信息,如圖5-2所示。
圖5-2 有沒有偷看答案,別想瞞過QuizActivity
5.1? 創建第二個 activity
要創建新的activity,接下來要做的事不少。首先創建CheatActivity所需的布局文件,然后創建CheatActivity類本身。
不過,現在我們還是先打開strings.xml文件,添加本章需要的所有字符串資源,如代碼清單5-1所示。
代碼清單5-1 添加字符串資源(strings.xml)
5.1.1 創建新布局
本章開頭的屏幕截圖(圖5-1)展示了CheatActivity視圖的大致樣貌。圖5-3描述了它的組件定義。
圖5-3 CheatActivity的布局圖示
為創建布局文件, 在項目導航視圖中打開app\res\layout目錄, 右擊layout目錄, 選擇New->Layout Resource file。(New->XML也行)
?
圖5-4 創建新的布局文件
在接下來彈出的對話框中,輸入布局文件名activity_cheat.xml并選擇LinerLayout作為根元素(Root Tag),最后單擊Finish按鈕完成,如圖5-5所示。
圖5-5 命名新布局文件
布局向導已經添加了LinerLayout根元素。接下來只需添加一個android:gravity屬性和其他三個子元素即可。
以后,我們將不再列示大段的XML代碼,而僅以圖5-3的方式給出布局組件圖示。最好現在開始習慣參照圖5-3創建布局XML文件。完成創建activity_cheat.xml布局文件后,記得對照代碼清單5-2進行檢查核對。
代碼清單5-2 第二個activity的布局組件定義(activity_cheat.xml)
保存布局文件,切換到圖形工具模式預覽新建布局。雖然沒有創建供設備橫屏使用的布局文件,不過,借助開發工具,我們可以預覽默認布局橫屏時的顯示效果。
在圖形布局工具中,找到預覽界面上方工具欄里的一個設備(紅框)模樣的按鈕。單擊該按鈕切換布局預覽方位,如圖5-6所示。
圖5-6 水平方位預覽布局(activity_cheat.xml)
可以看到,默認布局在豎直與水平方位下效果都不錯。布局搞定了,接下來我們來創建新的activity子類CheatActivity類。
5.1.2 創建新的activity子類
在項目導航視圖中,展開app\java目錄,右鍵單擊com.xq.geoquiz包,選擇New → Class菜單項。在隨后彈出的對話框中,將類命名為 CheatActivity, 點擊OK按鈕完成創建,如圖5-7所示。
圖5-7 創建CheatActivity類
讓CheatActivity 繼承AppCompatActivity, 覆蓋onCreate()方法,將定義在activity_cheat.xml文件中的布局資源ID傳入setContentView()方法,如代碼清單5-3所示。
代碼清單5-3 覆蓋onCreate()方法(CheatActivity.java)
CheatActivity還有更多任務需要在onCreate()方法中完成。不過現在我們先進入下一環節,即在應用的manifest配置文件中聲明CheatActivity。
5.1.3 在manifest配置文件中聲明activity
manifest配置文件是一個包含元數據的XML文件,用來向Android操作系統描述應用。該文件總是以AndroidManifest.xml命名,可在項目的根目錄找到它。通過項目導航視圖,在項目的根目錄中找到并打開它。
應用的所有activity都必須在manifest配置文件中聲明,這樣操作系統才能夠使用它們。
創建QuizActivity時,因使用了新建應用向導,向導會自動完成聲明工作。而CheatActivity則需手工完成聲明工作。(也可使用向導新建CheatActivity, 同時自動完成聲明工作)
在AndroidManifest.xml配置文件中,完成CheatActivity的聲明,如代碼清單5-4所示。
代碼清單5-4 在manifest配置文件中聲明CheatActivity(AndroidManifest.xml)
這里的android:name屬性是必需的。屬性值前面的“.”可告知OS:在manifest配置文件頭部包屬性值指定的包路徑下,可以找到CheatActivity的類文件。
manifest配置文件里還有很多有趣的東西。不過,我們現在還是先集中精力把CheatActivity配置并運行起來吧。在后續章節中,我們還將學習到更多有關manifest配置文件的知識。
5.1.4 為QuizActivity添加cheat按鈕
按照開發設想,用戶在QuizActivity用戶界面上點擊某個按鈕,應用立即產生CheatActivity實例,并顯示其用戶界面。因此,我們需要在layout/activity_quiz.xml以及layout-land/activity_quiz.xml布局文件中定義需要的按鈕。
在默認的垂直布局中,添加新按鈕定義并設置其為根LinearLayout的直接子類。新按鈕應該定義在Next按鈕之前,按鈕添加方法如代碼清單5-5所示。
代碼清單5-5 默認布局中添加cheat按鈕(layout/activity_quiz.xml)
同樣,修改水平布局文件,將新按鈕定義在根FrameLayout的底部居中位置,如代碼清單5-6所示。
代碼清單5-6 水平布局中添加cheat按鈕(layout-land/activity_quiz.xml)
保存修改后的布局文件。然后重新打開QuizActivity.java文件,添加新按鈕變量以及資源引用代碼。最后再添加View.onClickListener監聽器代碼存根。啟用新按鈕的做法如代碼清單5-7所示。
代碼清單5-7 啟用Cheat按鈕(QuizActivity.java)
準備工作完成了,下面我們來學習如何啟動CheatActivity。
5.2? 啟動 activity
一個activity啟動另一個activity最簡單的方式是使用以下Activity方法:
public void startActivity(Intent intent)我們可能“會以為”startActivity()方法是一個類方法,啟動activity就是針對Activity子類調用該方法。實際并非如此, activity調用startActivity()方法時,調用請求實際發給了操作系統。
準確地說,該方法調用請求是發送給操作系統的ActivityManager。ActivityManager負責創建Activity實例并調用其onCreate()方法。activity的啟動示意圖如圖5-8所示。
圖5-8 啟動activity
ActivityManager如何知道該啟動哪一個Activity呢? 答案就在于傳入startActivity()方法的Intent參數。
基于intent的通信
提示:
Intent代表一個應用"想去做什么事",你可以用它做各種各樣的任務,不過大部分的時候他們被用來啟動另一個Activity。
intent對象是組件用來與操作系統通信的一種媒介工具。目前為止,我們唯一見過的組件(component)就是activity。實際上還有其他一些component,如service、broadcast receiver以及content provider。
Intent是一種多功能通信工具。Intent類提供了多個構造方法,以滿足不同的使用需求。
在GeoQuiz應用中,我們使用intent告知ActivityManager該啟動哪一個activity,因此可使用以下構造方法(構造一個Intent對象):
public Intent(Context packageContext, Class<?> cls)傳入該方法的Class對象指定 ActivityManager應該啟動的activity;Context對象告知ActivityManager在哪一個包里可以找到Class對象,關系圖如圖5-9所示。
圖5-9 intent:ActivityManager的信使
在mCheatButton的監聽器代碼中,創建包含CheatActivity類的Intent實例,然后將其傳入startActivity(Intent)方法。代碼清單5-8中,QuizActivity.this 代表啟動方,之所以用this是因為當前Activity是Context的子類。
代碼清單5-8 啟動CheatActivity(QuizActivity.java)
在啟動activity前,ActivityManager會檢查確認指定的Class是否已在配置文件中聲明。如已完成聲明,則啟動activity,應用正常運行。反之,則拋出ActivityNotFoundException異常。這就是我們必須在manifest配置文件中聲明應用全部activity的原因所在。
顯式與隱式intent
如果通過指定Context與Class對象,然后調用intent的構造方法來創建Intent,則創建的是顯式intent。通常,在同一個應用中,我們使用顯式intent來啟動activity。
同一個應用里的兩個activity間,通信卻要借助于應用外部的ActivityManager,這可能看起來有點啰嗦。不過,這種模式會使不同應用間的activity交互變得容易很多。
一個應用的activity如需啟動另一個應用的activity,可通過創建隱式intent來處理。我們會在第21章學習到隱式intent的使用。
運行GeoQuiz應用。單擊Cheat按鈕,新activity實例的用戶界面將顯示在屏幕上。單擊后退按鈕,CheatActivity實例會被銷毀,繼而返回到QuizActivity實例的用戶界面中。
5.3? activity 間的數據傳遞
既然CheatActivity與QuizActivity都已經就緒,接下來就可以考慮它們之間的數據傳遞了。圖5-10展示了兩個activity間傳遞的數據信息。
圖5-10 QuizActivity與CheatActivity間的對話
CheatActivity啟動后,QuizActivity會將當前問題的答案通知給它。
用戶知道答案后,單擊后退鍵回到QuizActivity,CheatActivity隨即會被銷毀。在被銷毀前的瞬間,它會將用戶是否作弊的數據傳遞給QuizActivity。
接下來,我們首先要學習如何將數據從QuizActivity傳遞到CheatActivity。
5.3.1 使用intent extra
為將當前問題答案通知給CheatActivity,需將以下語句的返回值傳遞給它:
mQuestionBank[mCurrentIndex].isTrueQuestion();該值將作為extra信息,附加在傳入startActivity(Intent)方法的Intent上發送出去。
extra信息可以是任意數據,它包含在Intent中,由啟動方activity發送出去。接受方activity接收到操作系統轉發的intent后,訪問并獲取包含在其中的extra數據信息, 其關系如圖5-11所示。
圖5-11 Intent extra:activity間的通信與數據傳遞
如同 QuizActivity.onSaveInstanceState(Bundle) 方法中用 來保存 mCurrentIndex 值的key-value結構,extra也同樣是一種key-value結構。
將extra數據信息添加給intent,我們需要調用Intent.putExtra()方法, 方法原型如下:
public Intent putExtra(String name, boolean value)Intent.putExtra()方法有多種形式。不變的是,它總是有兩個參數。一個參數是固定為String類型的key,另一個參數值可以是多種數據類型。
在CheatActivity.java中,為extra數據信息新增key-value對中的key,如代碼清單5-9所示。
代碼清單5-9 添加extra常量(CheatActivity.java)
activity可能啟動自不同的地方,我們應該為activity獲取和使用的extra定義一個key。如代碼清單5-9所示,使用包名來修飾extra數據信息,這樣可以避免來自不同應用的extra間發生命名沖突。
接下來,再回到QuizActivity,將extra附加到intent上,如代碼清單5-10所示。
代碼清單5-10 將extra附加到intent上(QuizActivity.java)
要從extra獲取數據,會用到如下方法:
public boolean getBooleanExtra(String name, boolean defaultValue)第一個參數是extra的名字。getBooleanExtra()方法的第二個參數是指定默認值(默認答案),它在無法獲得有效key值時使用。
在CheatActivity代碼中,編寫代碼實現從extra中獲取信息,然后將信息存入成員變量中,如代碼清單5-11所示。
代碼清單5-11 獲取extra信息(CheatActivity.java)
請注意,Activity.getIntent()方法返回了由startActivity(Intent) 方法轉發的Intent對象。
最后,在CheatActivity代碼中,編碼實現單擊“顯示答案”按鈕后可獲取答案并將其顯示在TextView上,如代碼清單5-12所示。
代碼清單5-12 啟用作弊模式(CheatActivity.java)
TextView 相關的代碼還是很直觀的??赏ㄟ^使用 TextView.setText(int) 方法來設置TextView要顯示的文字。TextView.setText(int)方法有多種變體。這里,我們通過傳入資源ID來調用該方法。
運行GeoQuiz應用。單擊Cheat按鈕彈出CheatActivity的用戶界面。然后單擊“顯示答案”按鈕查看當前問題的答案。
5.3.2 從子activity獲取返回結果
現在用戶可以毫無顧忌地偷看答案了。如果CheatActivity可以把用戶是否偷看過答案的情況通知給QuizAcitivity就更好了。下面我們來修正這個問題。
若需要從子activity獲取返回信息時,可調用以下Activity方法:
public void startActivityForResult(Intent intent, int requestCode)該方法的第一個參數同前述的intent。第二個參數是請求代碼(是一個用戶定義的整數)是先發送給子activity,然后再返回給父activity。當一個activity啟動多個不同類型的子activity,且需要判斷區分消息回饋方時,我們通常會用到該請求代碼。
在QuizActivity中,修改mCheatButton的監聽器,調用startActivityForResult(Intent, int)方法,如代碼清單5-13所示。
代碼清單5-13 調用startActivityForResult()方法(QuizActivity.java)
QuizActivity只會啟動一個類型的子activity。具體發送信息是什么都無所謂,因此對于需要的請求代碼參數,傳入0即可。
1. 設置返回結果
實現子activity發送返回信息給父activity,有以下兩種方法可供調用:
public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)
通常來說,參數result code可以是以下兩個預定義常量中的任何一個:
- Activity.RESULT_OK
- Activity.RESULT_CANCELED
(如需自己定義結果代碼,還可使用另一個常量:RESULT_FIRST_USER)
在父activity需要依據子activity的完成結果采取不同操作時,設置結果代碼resultCode很有幫助。
例如,假設子activity有一個OK按鈕及一個Cancel按鈕,并且為每個按鈕的單擊動作分別設置了不同的結果代碼。根據不同的結果代碼,父activity會采取不同的操作。
子activity可以不調用setResult()方法。如不需要區分附加在intent上的結果或其他信息,可讓操作系統發送默認的結果代碼。
如果子activity是以調用startActivityForResult()方法啟動的,結果代碼則總是會返回給父activity。在沒有調用setResult()方法的情況下,如果用戶單擊了后退按鈕,父activity則會收到Activity.RESULT_CANCELED的結果代碼。
2. 返還intent
GeoQuiz應用中,數據信息需要回傳給QuizActivity。因此,我們需要創建一個Intent,附加上extra信息后,調用Activity.setResult(int, Intent)方法將信息回傳給QuizActivity。
前面,我們已經為CheatActivity接收的extra定義了常量。CheatActivity要回傳信息給QuizActivity,我們同樣需要為回傳的extra做類似的定義。
為什么不在接收信息的父activity中定義extra常量呢?這是因為,傳入及傳出extra針對CheatActivity定義了統一的接口。這樣,如果在應用的其他地方使用CheatActivity,我們只需要關注和使用定義在CheatActivity中的那些常量。
在CheatActivity代碼中,為extra增加常量key,再創建一個私有方法,用來創建intent,附加extra并設置結果值。然后在Show Answer按鈕的監聽器代碼中調用該方法。設置結果值的方法如代碼清單5-14所示。
代碼清單5-14 設置結果值(CheatActivity.java)
用戶單擊“顯示答案”按鈕時,CheatActivity調用setResult(int, Intent)方法將結果代碼以及intent打包。
然后,在用戶單擊后退鍵回到QuizActivity時,ActivityManager調用父activity的以下方法:
protected void onActivityResult(int requestCode, int resultCode, Intent data)該方法的參數來自于QuizActivity的原始請求代碼以及傳入SetResult()方法的結果代碼和intent。圖5-12展示了應用內部的交互時序。
圖5-12 GeoQuiz應用內部的交互時序圖
最后覆蓋QuizActivity的onActivityResult(int, int, Intent)方法來處理返回結果。
3. 處理返回結果
在 QuizActivity.java 中,新增一個成員變量保存 CheatActivity 回傳的值。然后覆蓋onActivityResult()方法獲取它。onActivityResult()方法的實現如代碼清單5-15所示。
代碼清單5-15 onActivityResult()方法的實現(QuizActivity.java)
觀察onActivityResult()方法的實現代碼,我們發現,QuizActivity并不關心請求代碼或結果代碼是什么。不過,在其他情況下,某些條件判斷編碼會使用到這些代碼值。
最后,修改QuizActivity中的checkAnswer(boolean)方法,確認用戶是否偷看答案并給出相應的反應。基于mIsCheater變量值改變toast消息的做法如代碼清單5-16所示。
代碼清單5-16 基于mIsCheater變量值改變toast消息(QuizActivity.java)
運行GeoQuiz應用。偷看下答案,看看會發生什么。
5.4? activity 的使用與管理
來看看當我們在各activity間往返的時候,操作系統層面到底發生了什么。首先,在桌面啟動器中點擊GeoQuiz應用時,操作系統并沒有啟動應用,而只是啟動了應用中的一個activity。確切地說,它啟動了應用的launcher activity。在GeoQuiz應用中,QuizActivity就是它的launcher activity。
使用應用向導創建GeoQuiz應用以及QuizActivity時,QuizActivity默認被設置為launcher activity。
在配置文件中,QuizActivity聲明的intent-filter元素節點下,可看到QuizActivity被指定為launcher activity,如代碼清單5-17所示。
代碼清單5-17 QuizActivity被指定為launcher activity(AndroidManifest.xml)
QuizActivity 實例出現在屏幕上后,用戶可單擊 Cheat!按鈕。CheatActivity 實例在QuizActivity實例上被啟動。此時,它們都處于activity棧中,如圖5-13所示。
圖5-13 GeoQuiz的回退棧
單擊后退鍵,CheatActivity實例被彈出棧外,QuizActivity重新回到棧頂部,如圖5-13所示。
轉載于:https://www.cnblogs.com/jlxuqiang/p/4750034.html
總結
以上是生活随笔為你收集整理的第 5 章 第二个 activity的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 代码注释技术
- 下一篇: Groovy开发语言