第 10 章 使用 fragment argument

請參考教材,全面理解和完成本章節內容... ...java

複製工程ch9,將工程目錄更名爲ch10。安全

本章,咱們將實現CriminalIntent「陋習手記」應用的列表與明細部分的關聯。用戶點擊某個「陋習」crime列表項時,會生成一個負責託管CrimeFragment的CrimeActivity,並顯示出某特定Crime實例的明細信息。如圖10-1所示。編碼

image

圖10-1 從CrimeListActivity中啓動CrimeActivityspa

10.1 從 fragment 中啓動 activity

從fragment中啓動activity的實現方式,基本等同於從activity中啓動另外一activity的實現方式。咱們調用Fragment.startActivity(Intent)方法,該方法在後臺會調用對應的Activity方法。操作系統

CrimeListFragmentonListItemClick(...)實現方法裏,用啓動CrimeActivity實例的代碼,替換日誌記錄crime標題的代碼,如代碼清單10-1所示。(暫時忽略Crime變量未使用的提示信息,下一節會使用它)3d

代碼清單10-1 啓動CrimeActivity(CrimeListFragment.java)日誌

image

以上代碼中,指定要啓動的activity爲CrimeActivityCrimeListFragment建立了一個顯式intent。在CrimeListFragment中使用getActivity()方法傳入它的託管activity,此activityIntent構造方法須要的Context對象。code

運行CriminalIntent應用。點擊任意列表項,屏幕上會出現一個託管CrimeFragmentCrimeActivity,如圖10-2所示。對象

image

圖10-2 空白的CrimeFragmentblog

因爲不知道該顯示哪一個Crime對象的信息,CrimeFragment也就沒有顯示出特定Crime對象的數據信息。

10.1.1 附加extra信息

經過將mCrimeId值附加到Intent的extra上,咱們能夠告知CrimeFragment應顯示的Crime。在onListItemClick(...)方法中,將用戶所選CrimemCrimeId值附加到用來啓動CrimeActivity的intent上。輸入代碼清單10-2所示代碼,IDE會報告一個錯誤信息,這是由於CrimeFragment.EXTRA_CRIME_ID的key值尚未建立。暫時忽略該條錯誤信息,稍後咱們會建立它。

代碼清單10-2 啓動附加extra的CrimeActivity(CrimeListFragment.java)

image

建立了顯式intent後,調用putExtra( )方法,傳入匹配mCrimeId的字符串key與key值,完成extra信息的準備。這裏,因爲UUIDSerializable對象,咱們調用了可接受Serializable對象的putExtra( )方法,即putExtra(String, Serializable)方法。

10.1.2 獲取extra信息

簡單獲取extra的方法是,返回至CrimeFragment類,爲extra添加key。而後,在onCreate(Bundle)方法中,獲得CrimeActivity的intent內的extra信息後,再使用它獲取Crime對象,如代碼清單10-3所示。

代碼清單10-3 獲取extra信息並取得Crime對象(CrimeFragment.java)

image

在代碼清單10-3中,除了getActivity()方法的調用,獲取extra數據的實現代碼與activity裏獲取extra數據的代碼同樣。getIntent()方法返回用來啓動CrimeActivityIntent。而後調用IntentgetSerializableExtra(String)方法獲取UUID並存入變量中。

取得Crime的ID後,利用該ID從CrimeLab單例中調取Crime對象。使用CrimeLab.get(...)方法須要Context對象,所以CrimeFragment傳入了CrimeActivity

10.1.3 使用Crime數據更新CrimeFragment視圖

既然CrimeFragment獲取了Crime對象,它的視圖即可顯示該Crime對象的數據。參照代碼清單10-4,更新onCreateView(...)方法,顯示Crime對象的標題及解決狀態。(顯示日期的代碼早已就緒)

代碼清單10-4 更新視圖對象(CrimeFragment.java)

image
運行應用。選中Crime #4,查看顯示了正確crime數據信息的CrimeFragment實例,如圖10-3所示。CriminalIntent
image

圖10-3 Crime #4列表項的明細內容

10.1.4 直接獲取extra信息方式的缺點

只需幾行簡單的代碼,就可實現讓fragment直接獲取託管activity的intent。然而,這種方式是以犧牲fragment的封裝性爲代價的。CrimeFragment再也不是可複用的構建單元,由於它老是須要由某個具體activity託管着,該activity的Intent又定義了名爲EXTRA_CRIME_ID的extra。

CrimeFragment類來講,這看起來合情合理。但這也意味着,按照當前的編碼實現,CrimeFragment便再也沒法用於任何其餘的activity了。

一個比較好的作法是,將mCrimeId存儲(Stash)在CrimeFragment的某個地方,而不是將它保存在CrimeActivity的私有空間裏。這樣,無需依賴於CrimeActivity的intent內指定extra的存在,CrimeFragment就能(本身)獲取本身所需的extra數據信息。fragment的「某個地方」實際就是它的arguments bundle

10.2  fragment argument

每一個fragment實例均可附帶一個Bundle對象。該bundle可含有多個key-value對,咱們能夠如同附加extra到Activity的intent中那樣使用它們。一個key-value對即一個argument。

要建立fragment argument,首先需建立Bundle對象。而後,使用Bundle限定類型的「put」方法(相似於Intent的方法),將argument添加到bundle中(如如下代碼所示)。

Bundle args = new Bundle();

args.putSerializable(EXTRA_MY_OBJECT, myObject);

args.putInt(EXTRA_MY_INT, myInt);

args.putCharSequence(EXTRA_MY_STRING, myString);

10.2.1 附加argumentfragment

附加argument bundle給fragment,需調用Fragment.setArguments(Bundle)方法。注意,該任務必須在fragment建立後、添加給activity前完成。

爲知足以上苛刻的要求,Android開發者遵循的習慣作法是:添加名爲newInstance()的靜態方法給Fragment類。使用該方法,完成fragment實例及bundle對象的建立,而後將argument放入bundle中,最後再附加給fragment。

託管activity須要fragment實例時,需調用newInstance()方法,而非直接調用其構造方法。並且,爲知足fragment建立argument的要求,activity可傳入任何須要的參數給newInstance()方法。

如代碼清單10-5所示,在CrimeFragment類中,編寫能夠接受UUID參數的newInstance(UUID)方法,經過該方法,完成arguments bundle以及fragment實例的建立,最後附加argument給fragment。

代碼清單10-5 編寫newInstance(UUID)方法(CrimeFragment.java)

image

如今,當CrimeActivity建立CrimeFragment時,應調用CrimeFragment.newInstance(UUID)方法,並傳入從它的extra中獲取的UUID參數值。回到CrimeActivity類中,在createFragment()方法裏,從CrimeActivity的intent中獲取extra數據信息,並將之傳入CrimeFragment.newInstance(UUID)方法,如代碼清單10-6所示。

代碼清單10-6 使用newInstance(UUID)方法(CrimeActivity.java)

image

注意,交互的activity和fragment不須要也沒法同時保持通用獨立性。CrimeActivity必須瞭解CrimeFragment的內部細節,好比知曉它內部有一個newInstance(UUID)方法。這很正常。託管activity就應該知道有關託管fragment方法的細節,但fragment則沒必要知道其託管activity的細節問題。至少在須要保持fragment通用獨立性的時候是如此。

10.2.2 獲取argument

fragment在須要獲取它的argument時,會先調用Fragment類的getArguments()方法,接着再調用Bundle的限定類型的「get」方法,如getSerializable(...)方法。

如今回到CrimeFragment.onCreate(...)方法中,調整代碼,改成從fragment的argument中獲取UUID,如代碼清單10-7所示。

代碼清單10-7 從argument中獲取crime ID(CrimeFragment.java)

image

運行CriminalIntent應用。雖然運行結果仍與以前一致,但咱們應該感到由衷地高興。由於咱們不只保持了CrimeFragment類的獨立性,又爲下一章實現CriminalIntent應用更爲複雜的列表項導航打下了良好基礎。

10.3 從新加載顯示列表項

運行CriminalIntent應用,點擊某個列表項,而後修改對應的Crime明細信息。這些修改的數據被保存至模型層,但返回列表後,列表視圖並無發生改變。下面咱們來處理這個問題。

如模型層保存的數據發生改變(或可能發生改變),應通知列表視圖的adapter,以便其及時獲取最新數據並從新加載顯示列表項。在適當的時點,與系統的ActivityManager回退棧協同運做,能夠完成列表項的刷新。

CrimeListFragment啓動CrimeActivity實例後,CrimeActivity被置於回退棧頂。這致使原先處於棧頂的CrimeListActivity實例被暫停並中止。

用戶點擊後退鍵返回到列表項界面,CrimeActivity隨即被彈出棧外並被銷燬。CrimeListActivity繼而被從新啓動並恢復運行。應用的回退棧如圖10-4所示。

image

圖10-4 CriminalIntent應用的回退棧

CrimeListActivity恢復運行狀態後,操做系統會向它發出調用onResume()生命週期方法的指令。CrimeListActivity接到指令後,它的FragmentManager會調用當前被activity託管的fragment的onResume()方法。這裏,CrimeListFragment即惟一的目標fragment。

在CrimeListFragment中,覆蓋onResume()方法刷新顯示列表項,如代碼清單10-8所示。

代碼清單10-8 在onResume()方法中刷新列表項(CrimeListFragment.java)

image

爲何選擇覆蓋onResume()方法來刷新列表項顯示,而非onStart()方法呢?當一個activity位於咱們的activity以前時,咱們沒法保證本身的activity是否會被中止。如前面的activity是透明的,則咱們的activity可能只會被暫停。對於此場景下暫停的activity,onStart()方法中的更新代碼是不會起做用的。通常來講,要保證fragment視圖獲得刷新,在onResume()方法內更新代碼是最安全的選擇。

運行CriminalIntent應用。選擇某個crime項並修改其明細內容。而後返回到列表項界面,如預期那樣,列表項當即刷新反映了更改的內容。

通過前兩章的開發,CriminalIntent應用已得到大幅更新。如今,咱們來看看更新後的應用對象圖解,如圖10-5所示。

image

圖10-5 應用對象圖解更新版

相關文章
相關標籤/搜索