Fragment 用法總結(二)

Fragment 用法總結(二)

上一篇文章裏主要講到Fragment如何創建,如何與Activity交互,基本的API用法等。有興趣可以先了解下Fragment 用法總結(一)

Fragment的生命週期大家都熟悉,本篇文章主要從Activity管理Fragment的角度來了解Fragment的生命週期變化,並與Activity的生命週期協調一致。

Fragment的生命週期

lifecycle

通常,我們的Fragment至少應實現以下生命週期方法:

  • onCreate()
    系統會在創建Fragment時調用此方法。我們應該在這個方法裏初始化那些在Fragment暫停或停止後恢復時必需保留的Fragment組件。
  • onCreateView()
    系統會在Fragment首次繪製其用戶界面時調用此方法。 要想爲Fragment繪製 UI,從此方法中返回的 View 必須是Fragment佈局的根視圖。如果Fragment未提供 UI,可以返回 null。
  • onPause()
    系統將此方法作爲用戶離開Fragment的第一個信號(但並不總是意味着此Fragment會被銷燬)進行調用。 通常應該在此方法內保存一些將來需要恢復的數據(因爲用戶可能不會返回)。

其它生命週期方法的作用在下面會講到。

處理Fragment生命週期

activity_fragment_lifecycle

管理Fragment生命週期與管理 Activity 生命週期很相似。和 Activity 一樣,Fragment也以三種狀態存在:

  • 恢復
    Fragment在運行中的 Activity 中可見。

  • 暫停
    另一個 Activity 位於前臺並具有焦點,但此Fragment所在的 Activity 仍然可見(前臺 Activity 部分透明,或未覆蓋整個屏幕)。

  • 停止
    Fragment不可見。宿主 Activity 已停止,或Fragment已從 Activity 中刪除,但已添加到返回棧。 停止Fragment仍然處於活動狀態(系統會保留所有狀態和成員信息)。 不過,它對用戶不再可見,如果 Activity 被終止,它也會被終止。
    同樣與 Activity 一樣,假使 Activity 的進程被終止,而需要在重建 Activity 時恢復Fragment狀態,可以使用 Bundle 保留Fragment的狀態。可以在Fragment的 onSaveInstanceState() 回調期間保存狀態,並可在 onCreate()、onCreateView() 或 onActivityCreated() 期間恢復狀態。

Activity 生命週期與Fragment生命週期之間的最顯著差異在於它們在其各自返回棧中的存儲方式。 默認情況下,Activity 停止時會被放入由系統管理的 Activity 返回棧(以便用戶通過“返回” 按鈕回退到Activity)。不過,Fragment只有在Fragment的事務執行期間通過調用 addToBackStack() 顯式請求保存實例時,系統纔會將Fragment放入由宿主 Activity 管理的返回棧。

在其他方面,管理Fragment生命週期與管理 Activity 生命週期非常相似。 因此,管理 Activity 生命週期的做法同樣適用於Fragment。 但還需要了解 Activity 的生命週期對Fragment生命週期的影響。

注意:如需 Fragment 內的某個 Context 對象,可以調用 getActivity()。但要注意,請僅在Fragment附加到 Activity 時調用 getActivity()。如果Fragment尚未附加,或在其生命週期結束期間分離,則 getActivity() 將返回 null,所以getActivity()和getContext()(API level 23 新增方法)要在Fragment回調函數onAttach之後才能調用。

與 Activity 生命週期協調一致

Fragment所在的 Activity 的生命週期會影響Fragment的生命週期,其表現爲,Activity 的每次生命週期回調都會引發每個Fragment的類似回調。 例如,當 Activity 收到 onPause() 時,Activity 中的每個Fragment也會收到 onPause()。

不過,Fragment還有幾個額外的生命週期回調,用於處理與 Activity 的唯一交互,以執行構建和銷燬Fragment UI 等操作。這些額外的回調方法是:

  • onAttach()
    在Fragment已與 Activity 關聯時調用(Activity 傳遞到此方法內)。
  • onCreateView()
    調用它可創建與Fragment關聯的視圖層次結構。
  • onActivityCreated()
    在 Activity 的 onCreate() 方法已返回時調用。
  • onDestroyView()
    在刪除與Fragment關聯的視圖層次結構時調用。
  • onDetach()
    在取消Fragment與 Activity 的關聯時調用。

宿主 Activity 影響Fragment的生命週期流。Activity 的每個連續狀態如何決定Fragment可以收到的回調方法。 例如,當 Activity 收到其 onCreate() 回調時,Activity 中的Fragment只會收到 onActivityCreated() 回調。

一旦 Activity 達到恢復狀態,就可以意向 Activity 添加Fragment和刪除其中的Fragment。 因此,只有當 Activity 處於恢復狀態時,Fragment的生命週期才能獨立變化。

不過,當 Activity 離開恢復狀態時,Fragment會在 Activity 的推動下再次經歷其生命週期。

Fragment的生命週期如何變化

事物方法操作

我們結合Activity對fragment的操作方法,瞭解一下不同的事物操作方法對Fragment的生命週期方法的影響。
動圖演示
下面是動圖操作的步驟:
一、fragmentTransaction.add(R.id.content_main, fragmentA).commit();

FragmentA: onAttach;
FragmentA: onCreate;
FragmentA: onCreateView;
FragmentA: onActivityCreated;
FragmentA: onStart;
FragmentA: onResume;

爲Activity添加第一個Fragment的時候不要把事物添加到返回棧,如果添加了,則會在“返回”按鈕按下後回退到Activity的空白頁面,這應該是大家不想看到的。
二、fragmentTransaction.replace(R.id.content_main, fragmentB).addToBackStack(null).commit();

FragmentA: onPause;
FragmentA: onStop;
FragmentA: onDestroyView;
FragmentB: onAttach;
FragmentB: onCreate;
FragmentB: onCreateView;
FragmentB: onActivityCreated;
FragmentB: onStart;
FragmentB: onResume;

FragmentTransaction的replace方法相當於remove方法和add方法的結合,上面的程序輸出也證明了這一點
三、fragmentTransaction.hide(fragmentB).add(R.id.content_main, fragmentC).addToBackStack(null).commit();

FragmentC: onAttach;
FragmentC: onCreate;
FragmentC: onCreateView;
FragmentC: onActivityCreated;
FragmentC: onStart;
FragmentC: onResume;

FragmentTransaction的hide方法不觸發Fragment的生命週期方法,所以隱藏FragmentB沒有觸發接下來的生命週期,上面的輸出表明這個事物提交後只觸發了添加fragmentC的生命週期方法。
四、fragmentTransaction.detach(fragmentC).addToBackStack(null).commit();

FragmentC: onPause;
FragmentC: onStop;
FragmentC: onDestroyView;

將fragmentC detach
五、按下返回鍵 popBackStack

FragmentC: onCreateView;
FragmentC: onActivityCreated;
FragmentC: onStart;
FragmentC: onResume;

返回操作,恢復到detach FragmentC的狀態,FragmentC從detach狀態變爲attach狀態
六、按下返回鍵 popBackStack FragmentC被移除

FragmentC: onPause;
FragmentC: onStop;
FragmentC: onDestroyView;
FragmentC: onDestroy;
FragmentC: onDetach;

返回操作,恢復到加載FragmentC前的狀態,上一個提交的事物爲hide->FragmentB add ->FragmentC 則恢復操作相反 “remove->FragmentC” “show->FragmentB”,
上面講到hide方法不觸發Fragment生命週期,同理show方法也不觸發Fragment生命週期。
由於FragmentC已經退出”歷史舞臺”取消與Activity關聯,所以執行onDetach方法。
七、按下返回鍵 popBackStack FragmentB

FragmentB: onPause;
FragmentB: onStop;
FragmentB: onDestroyView;
FragmentB: onDestroy;
FragmentB: onDetach;
FragmentA: onCreateView;
FragmentA: onActivityCreated;
FragmentA: onStart;
FragmentA: onResume;

返回操作,恢復到加載FragmentB前的狀態,則replace的逆操作“remove FragmentB” “add FragmentA”
八、按下返回鍵 popBackStack FragmentA

FragmentA: onPause;
FragmentA: onStop;
FragmentA: onDestroyView;
FragmentA: onDestroy;
FragmentA: onDetach;

返回操作,恢復到加載FragmentA前的狀態,沒有加入回退棧,Activity直接退出。

眼尖的同學肯定發現了,上面動態圖片中FragmentA的EditView設置了新內容,但是在回退回來的時候,又恢復到原來的內容。這是因爲:如果不給一個View設置一個id,那麼在Activity或者Fragment調用onSaveInstanceState(Bundle outState)方法時,就沒辦法保存它的狀態,這會導致在恢復界面的時候丟失上次的狀態。

橫豎屏切換

從FragmentA跳轉到FragmentB之後進行橫豎屏切換,生命週期變化如下圖:
這裏寫圖片描述
圖片中方塊內的數字是對象的hashCode。
從圖片裏的FragmentA和FragmentB的生命週期變化中發現在FragmentB被銷燬前會先銷燬FragmentA,而且在創建新的FragmentB對象前將先創建FragmentA和宿主Activity關聯起來。
所以結論是,橫豎屏切換之後無論宿主Activity還是Fragment都會重新創建(創建前依次銷燬之前添加的Fragment),Fragment創建順序由之前的添加順序決定。注意,橫豎屏切換後新創建的宿主Activity不能很好的還原原來的Fragment返回棧中Fragment的狀態,比如之前的Fragment被hide和detach(如果在Fragment的構造函數或者onCreate()方法裏調用setRetainInstance(true)方法,則一切正常,setRetainInstance(true)的使用方法可查看下一篇Fragment 用法總結(三))。

替換Fragment的方法


  • hide(A).add(B).addToBackStack(null).commit();
    這種方法FragmentA在hide和恢復的時候不會觸發生命週期回調方法。注意此方法在橫豎屏切換時候被hide的FragmentA會重新顯示出來,引起界面重疊。
  • replace(id,B).addToBackStack(null).commit();
    這種方法FragmentA在replace和恢復的時候會走一遍生命週期函數。replace和remove + add效果一樣。
  • detach(A).add(B).addToBackStack(null).commit();
    這裏detach會銷燬FragmentA的視圖,添加到回退棧後“返回”可以恢復視圖,這裏也要觸發FragmentA生命週期回調方法。

注意:當你移除或替換 Fragment 並向返回堆棧添加事務時,已移除的 Fragment 會停止(而不是銷燬)。如果用戶向後導航,還原該 Fragment,它會重新啓動。如果你沒有向返回堆棧添加事務,那麼該 Fragment 在移除或替換時就會被銷燬。

Fragment的視圖狀態

  • Fragment返回時中保留View狀態
    首先要調用addToBackStack()把切換事物加到回退棧裏,保證Fragment能成功返回,其次不要每次都new一個新的Fragment對象,保存Fragment實例。這裏要注意屏幕橫豎切換的時候Fragment會重疊。
  • Fragment返回時中不保留View狀態
    切換Fragment的事物裏每次new一個新的Fragment或者不給視圖添加id就可以(好像沒什麼意義)。

如果是非應用行爲導致Fragment異常退出和恢復,在onSaveInstanceState()裏保存狀態。

上面動圖的示例代碼

本篇文章主要從Activity管理Fragment的角度來了解Fragment的生命週期變化,並與Activity的生命週期協調一致。下一篇Fragment 用法總結(三)主要介紹:沒有視圖的Fragment的用處、使用Fragment創建對話框、如何與ActionBar和MenuItem集成等高級用法。

發佈了42 篇原創文章 · 獲贊 216 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章