Fragment應用之簡述

Fragment的應用真的是越來越廣泛了,之前Android在3.0版本加入Fragment的時候,主要是為了解決Android Pad屏幕比較大,空間不能充分利用的問題。但隨著界面布局的復雜化,處理起來也更加的復雜,引入Fragment可以把activity拆分成各個部分。每個Fragment都有它自己的布局和生命周期。

一、Fragment的生命周期

Fragment生命周期.png
  1. onAttach()
    作用:fragment已經關聯到activity。
@Override
  public void onAttach(Activity activity) {
      super.onAttach(activity);
      Log.i("onAttach_Fragment");
  }

該方法有一個Activity類型的參數,代表綁定的Activity,獲得activity的傳遞的值 就可以進行 與activity的通信里, 當然也可以使用getActivity(),前提是這個fragment已經和宿主的activity關聯,并且沒有脫離。

  1. **onCreate() **
    作用:初始化Fragment,系統創建fragment的時候回調該方法,在該方法里面實例化一些變量,參數是:Bundle savedInstance, 用于保存 Fragment 參數, Fragement 也可以重寫 onSaveInstanceState(BundleoutState) 方法, 保存Fragement狀態。
  2. onCreateView()
    作用:初始化Fragment的布局。加載布局和findViewById的操作通常在此函數內完成,當系統用到fragment的時候 fragment就要返回它的view,越快越好 ,所以盡量在這里不要做耗時操作,比如從數據庫加載大量數據,可進行各種判斷省得每次都要加載,減少資源消耗,實例如下:
if(text==null){
      Bundle args=getArguments();
      text=args.getString("text");
    }
    if (view == null) {
      view = inflater.inflate(R.layout.hello, null);
    }
  1. onActivityCreated()
    作用:初始化那些你需要你的父Activity或者Fragment的UI已經被完整初始化才能初始化的元素。
    執行該方法時,與Fragment綁定的Activity的onCreate方法已經執行完成并返回,在該方法內可以進行與Activity交互的UI操作,當執行onActivityCreated()的時候 activity的onCreate才剛完成。所以在onActivityCreated()調用之前 activity的onCreate可能還沒有完成,所以不能再onCreateView()中進行 與activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面進行。
  2. onStart()
    和activity一致,啟動Fragement 啟動時回調,,此時Fragement由不可見變為可見狀態。
  3. onResume()
    執行該方法時,Fragment處于活動狀態,用戶可與之交互。激活Fragement 進入前臺, 可獲取焦點時激活。
  4. onPause()
    和activity一致 其他的activity獲得焦點,這個Fragment仍然可見,但是用戶不能與之交互。第一次調用的時候,指的是 用戶 離開這個Fragment(并不是被銷毀)。
  5. onStop()
    和activity一致, fragment不可見的, 可能情況:activity被stopped了或者 fragment被移除但被加入到回退棧中,一個stopped的fragment仍然是活著的如果長時間不用也會被移除。
  6. onDestroyView()
    Fragment中的布局被移除時調用。
    表示Fragment銷毀相關聯的UI布局, 清除所有跟視圖相關的資源,但未與Activity解除綁定,依然可以通過onCreateView方法重新創建視圖。
  7. onDestroy()
    銷毀Fragment。通常按Back鍵退出或者Fragment被回收時調用此方法。
  8. onDetach()
    Fragment解除與Activity的綁定。在onDestroy方法之后調用。

下面給出activity和fragment同時運行時候的生命周期:

  • 開始啟動:
03-10 16:55:57.722 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onCreate() 方法執行!
03-10 16:55:57.728 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onCreate() 方法執行!
03-10 16:55:57.728 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onCreateView() 方法執行!
03-10 16:55:57.728 1700-1700/com.liujc.fragmentlife D/TestFragment: 沒有保存的數據!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onActivityCreated() 方法執行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onStart() 方法執行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onStart() 方法執行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onResume() 方法執行!
03-10 16:55:57.730 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onResume() 方法執行!

細心的你可能會發現為什么Fragment沒走onAttach()方法呢?難道生命周期還有問題不成。其實Fragment的onAttach()方法有2個重載onAttach(Context context)和onAttach(Activity activity),我的測試機用的android 5.0系統,而在API低于 23 的版本中不會去調用onAttach(Context context),只會去調用onAttach(Activity)。然后把兩個方法都加上運行一下結果如下:

03-10 17:19:08.539 19010-19010/com.liujc.fragmentlife D/MainActivity: Activity onCreate() 方法執行!
03-10 17:19:08.546 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onAttach(Activity activity) 方法執行!
03-10 17:19:08.546 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onCreate() 方法執行!
03-10 17:19:08.547 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onCreateView() 方法執行!
03-10 17:19:08.547 19010-19010/com.liujc.fragmentlife D/TestFragment: 沒有保存的數據!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onActivityCreated() 方法執行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/MainActivity: Activity onStart() 方法執行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onStart() 方法執行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/MainActivity: Activity onResume() 方法執行!
03-10 17:19:08.548 19010-19010/com.liujc.fragmentlife D/TestFragment: Fragment onResume() 方法執行!
  • 按下home按鍵
03-10 17:00:08.455 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onPause() 方法執行!
03-10 17:00:08.456 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onPause() 方法執行!
03-10 17:00:09.048 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onSaveInstanceState() 方法執行!
03-10 17:00:09.052 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onStop() 方法執行!
03-10 17:00:09.054 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onStop() 方法執行!
  • 再回到界面
03-10 17:01:20.870 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onRestart() 方法執行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onStart() 方法執行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onStart() 方法執行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/MainActivity: Activity onResume() 方法執行!
03-10 17:01:20.873 1700-1700/com.liujc.fragmentlife D/TestFragment: Fragment onResume() 方法執行!
  • 銷毀activity
03-10 17:05:53.900 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onPause() 方法執行!
03-10 17:05:53.901 22559-22559/com.liujc.fragmentlife D/MainActivity: Activity onPause() 方法執行!
03-10 17:05:54.435 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onStop() 方法執行!
03-10 17:05:54.435 22559-22559/com.liujc.fragmentlife D/MainActivity: Activity onStop() 方法執行!
03-10 17:05:54.437 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onDestroyView() 方法執行!
03-10 17:05:54.441 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onDestroy() 方法執行!
03-10 17:05:54.441 22559-22559/com.liujc.fragmentlife D/TestFragment: Fragment onDetach() 方法執行!
03-10 17:05:54.441 22559-22559/com.liujc.fragmentlife D/MainActivity: Activity onDestroy() 方法執行!

可以看出 當現實fragment的時候都先執行activity方法,當銷毀的時候都是現執行 fragment的方法,這樣更好理解fragment是嵌套在activity中。

二、將Fragment添加到Activity之中

可以通過在Activity布局文件中聲明Fragment,用Fragment標簽把Fragment插入到Activity的布局中,或者是用應用程序源碼將它添加到一個存在的ViewGroup中。但Fragment并不是一個定要作為Activity布局的一部分,Fragment也可以為Activity隱身工作。

  1. 在activity的布局文件里聲明fragment。
    可以像為view一樣為fragment指定布局屬性。例如:
<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"> 
        
        <fragment android:name="com.liujc.test.FragmentOne"
            android:id="@+id/fragment_one"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

fragment標簽中的android:name 屬性指定了布局中實例化的Fragment類。
當系統創建activity布局時,它實例化了布局文件中指定的每一個fragment,并為它們調用onCreateView()函數,以獲取每一個fragment的布局。系統直接在元素的位置插入fragment返回的View。
  注意:每個fragment都需要一個唯一的標識,如果重啟activity,系統可用來恢復fragment(并且可用來捕捉fragment的事務處理,例如移除)。
為fragment提供ID有三種方法:

  • 用android:id屬性提供一個唯一的標識。
  • 用android:tag屬性提供一個唯一的字符串。
  • 如果上述兩個屬性都沒有,系統會使用其容器視圖(view)的ID。
  1. 通過編碼將fragment添加到已存在的ViewGroup中。
    在activity運行的任何時候,你都可以將fragment添加到activity布局中。要管理activity中的fragment,可以使用FragmentManager。可以通過在activity中調用getFragmentManager()獲得。使用FragmentManager 可以做如下事情,包括:
  • 使用findFragmentById()(用于在activity布局中提供有界面的fragment)或者findFragmentByTag()獲取activity中存在的fragment(用于有界面或者沒有界面的fragment)。
  • 使用popBackStack()(模仿用戶的BACK命令)從后臺棧彈出fragment。
  • 使用addOnBackStackChangedListener()注冊一個監聽后臺棧變化的監聽器。

在Android中,對Fragment的事務操作都是通過FragmentTransaction來執行。
FragmentTransaction transaction = fm.benginTransatcion();//開啟一個事務
操作大致可以分為兩類:

  • 顯示:add()replace()show()attach()
    **transaction.add() **
    往Activity中添加一個Fragment。
    **transaction.replace() **
    使用另一個Fragment替換當前的,實際上就是remove()然后add()的合體。
    **transaction.show() **
    顯示之前隱藏的Fragment。
    **attach() **
    重建view視圖,附加到UI上并顯示。
  • 隱藏:remove()hide()detach()
    ** transaction.remove() **
    從Activity中移除一個Fragment,如果被移除的Fragment沒有添加到回退棧(回退棧后面會詳細說),這個Fragment實例將會被銷毀。
    transaction.hide()
    隱藏當前的Fragment,僅僅是設為不可見,并不會銷毀。
    detach()
    會將view從UI中移除,和remove()不同,此時fragment的狀態依然由FragmentManager維護。

注意:

  • 調用show() & hide()方法時,Fragment的生命周期方法并不會被執行,僅僅是Fragment的View被顯示或者?隱藏。
  • 執行replace()時(至少兩個Fragment),會執行第二個Fragment的onAttach()方法、執行第一個Fragment的onPause()-onDetach()方法,同時containerView會detach第一個Fragment的View。
  • 執行add()方法執行onAttach()-onResume()的生命周期,相對的remove()就是執行完成剩下的onPause()-onDetach()周期。

add方式實現fragment的效果就是:切換fragment時不會重新創建,是什么樣子切換回來還是什么樣子;
用replace的效果就是:切換fragment時每次都會重新創建初始化。
從Activity中取得FragmentTransaction的實例:

FragmentManager fragmentManager = getFragmentManager() 
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

用add()函數添加fragment,并指定要添加的fragment以及要將其插入到哪個視圖(view)之中(注意commit事務):

ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
  1. 添加沒有界面的fragment。
    也可以使用fragment為activity提供后臺動作,卻不呈現多余的用戶界面。
      想要添加沒有界面的fragment ,可以使用add(Fragment, String)(為fragment提供一個唯一的字符串“tag”,而不是視圖(view)ID)。這樣添加了fragment,但是,因為還沒有關聯到activity布局中的視圖(view) ,收不到onCreateView()的調用。所以不需要實現這個方法。對于無界面fragment,字符串標簽是唯一識別它的方法。如果之后想從activity中取到fragment,需要使用findFragmentByTag()。

三、Fragment與Activity交互

  • Activity中已經有了該Fragment的引用,直接通過該引用進行交互。
    如果沒引用可以通過調用fragment的函數findFragmentById()或者findFragmentByTag(),從FragmentManager中獲取Fragment的索引,例如:
    ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
  • 在Fragment中可以通過getActivity得到當前綁定的Activity的實例。
  • 創建activity事件回調函數,在fragment內部定義一個回調接口,宿主activity來實現它。

四、Fragment事務后臺棧

在調用commit()之前,可以將事務添加到fragment事務后臺棧中(通過調用addToBackStatck())。這個后臺棧由activity管理,并且允許用戶通過按BACK鍵回退到前一個fragment狀態。
下面的代碼中一個fragment代替另一個fragment,并且將之前的fragment狀態保留在后臺棧中:

Fragment newFragment = new ExampleFragment();
 FragmentTransaction transaction = getFragmentManager().beginTransaction();
 
 transaction.replace(R.id.fragment_container, newFragment);
 transaction.addToBackStack(null);

 transaction.commit();

注意:

  • 如果添加多個變更事務(例如另一個add()或者remove())并調用addToBackStack(),那么在調用commit()之前的所有應用的變更被作為一個單獨的事務添加到后臺棧中,并且BACK鍵可以將它們一起回退。
  • 當移除一個fragment時,如果調用了addToBackStack(),那么之后fragment會被停止,如果用戶回退,它將被恢復過來。
  • 調用commit()并不立刻執行事務,相反,而是采取預約方式,一旦activity的界面線程(主線程)準備好便可運行起來。然而,如果有必要的話,你可以從界面線程調用executePendingTransations()立即執行由commit()提交的事務。
  • 只能在activity保存狀態(當用戶離開activity時)之前用commit()提交事務。如果你嘗試在那時之后提交,會拋出一個異常。這是因為如果activity需要被恢復,提交后的狀態會被丟失。對于這類丟失提交的情況,可使用commitAllowingStateLoss()。

五、Fragment的setUserVisibleHint()

Android應用開發過程中,ViewPager同時加載多個fragment,以實現多tab頁面快速切換, 但是fragment初始化時若加載的內容較多,就可能導致整個應用啟動速度緩慢,影響用戶體驗。 為了提高用戶體驗,我們會使用一些懶加載方案,實現加載延遲。這時我們會用到getUserVisibleHint()與setUserVisibleHint()這兩個方法。

/**
*
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
*                        false if it is not.
*/
public void setUserVisibleHint(boolean isVisibleToUser) {
   if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
       mFragmentManager.performPendingDeferredStart(this);
   }
   mUserVisibleHint = isVisibleToUser;
   mDeferStart = !isVisibleToUser;
}

/**
 * @return The current value of the user-visible hint on this fragment.
 * @see #setUserVisibleHint(boolean)
 */
public boolean getUserVisibleHint() {
    return mUserVisibleHint;
}

從上述源碼注釋我們可以看出,當fragment被用戶可見時,setUserVisibleHint()會調用且傳入true值,當fragment不被用戶可見時,setUserVisibleHint()則得到false值。而在傳統的fragment生命周期里也看不到這個函數。可以看出其實這個setUserVisibleHint()方法算是手動調用的,并不是在Fragment的生命周期中自動調用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,588評論 25 707
  • Fragment 描述: ??翻譯可以譯為:碎片、片段,Android 3.0開始引入fragments 的概念;...
    Lost_Robot閱讀 1,715評論 0 11
  • Fragment要點 1、Fragment作為Activity界面的一部分組成出現 2、可以在一個Activity...
    玉圣閱讀 1,240評論 0 16
  • 生活中的小幸運,又發生了! 今天本來心情難過的很,下班的時候拿起手機要拍照,忽然發現,手機相機根本用不了,用不了,...
    夏天說早安閱讀 169評論 0 0
  • 1.必須確認面試者能否勝任未來他面對的任務,比如給一個具體會面對的問題讓他回答,看他如何解決,從而來判斷
    Mark86閱讀 163評論 0 0