Fragment應(yīng)用之簡(jiǎn)述

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

一、Fragment的生命周期

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

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

  1. **onCreate() **
    作用:初始化Fragment,系統(tǒng)創(chuàng)建fragment的時(shí)候回調(diào)該方法,在該方法里面實(shí)例化一些變量,參數(shù)是:Bundle savedInstance, 用于保存 Fragment 參數(shù), Fragement 也可以重寫 onSaveInstanceState(BundleoutState) 方法, 保存Fragement狀態(tài)。
  2. onCreateView()
    作用:初始化Fragment的布局。加載布局和findViewById的操作通常在此函數(shù)內(nèi)完成,當(dāng)系統(tǒng)用到fragment的時(shí)候 fragment就要返回它的view,越快越好 ,所以盡量在這里不要做耗時(shí)操作,比如從數(shù)據(jù)庫加載大量數(shù)據(jù),可進(jìn)行各種判斷省得每次都要加載,減少資源消耗,實(shí)例如下:
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已經(jīng)被完整初始化才能初始化的元素。
    執(zhí)行該方法時(shí),與Fragment綁定的Activity的onCreate方法已經(jīng)執(zhí)行完成并返回,在該方法內(nèi)可以進(jìn)行與Activity交互的UI操作,當(dāng)執(zhí)行onActivityCreated()的時(shí)候 activity的onCreate才剛完成。所以在onActivityCreated()調(diào)用之前 activity的onCreate可能還沒有完成,所以不能再onCreateView()中進(jìn)行 與activity有交互的UI操作,UI交互操作可以在onActivityCreated()里面進(jìn)行。
  2. onStart()
    和activity一致,啟動(dòng)Fragement 啟動(dòng)時(shí)回調(diào),,此時(shí)Fragement由不可見變?yōu)榭梢姞顟B(tài)。
  3. onResume()
    執(zhí)行該方法時(shí),F(xiàn)ragment處于活動(dòng)狀態(tài),用戶可與之交互。激活Fragement 進(jìn)入前臺(tái), 可獲取焦點(diǎn)時(shí)激活。
  4. onPause()
    和activity一致 其他的activity獲得焦點(diǎn),這個(gè)Fragment仍然可見,但是用戶不能與之交互。第一次調(diào)用的時(shí)候,指的是 用戶 離開這個(gè)Fragment(并不是被銷毀)。
  5. onStop()
    和activity一致, fragment不可見的, 可能情況:activity被stopped了或者 fragment被移除但被加入到回退棧中,一個(gè)stopped的fragment仍然是活著的如果長(zhǎng)時(shí)間不用也會(huì)被移除。
  6. onDestroyView()
    Fragment中的布局被移除時(shí)調(diào)用。
    表示Fragment銷毀相關(guān)聯(lián)的UI布局, 清除所有跟視圖相關(guān)的資源,但未與Activity解除綁定,依然可以通過onCreateView方法重新創(chuàng)建視圖。
  7. onDestroy()
    銷毀Fragment。通常按Back鍵退出或者Fragment被回收時(shí)調(diào)用此方法。
  8. onDetach()
    Fragment解除與Activity的綁定。在onDestroy方法之后調(diào)用。

下面給出activity和fragment同時(shí)運(yùn)行時(shí)候的生命周期:

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

細(xì)心的你可能會(huì)發(fā)現(xiàn)為什么Fragment沒走onAttach()方法呢?難道生命周期還有問題不成。其實(shí)Fragment的onAttach()方法有2個(gè)重載onAttach(Context context)和onAttach(Activity activity),我的測(cè)試機(jī)用的android 5.0系統(tǒng),而在API低于 23 的版本中不會(huì)去調(diào)用onAttach(Context context),只會(huì)去調(diào)用onAttach(Activity)。然后把兩個(gè)方法都加上運(yùn)行一下結(jié)果如下:

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

可以看出 當(dāng)現(xiàn)實(shí)fragment的時(shí)候都先執(zhí)行activity方法,當(dāng)銷毀的時(shí)候都是現(xiàn)執(zhí)行 fragment的方法,這樣更好理解fragment是嵌套在activity中。

二、將Fragment添加到Activity之中

可以通過在Activity布局文件中聲明Fragment,用Fragment標(biāo)簽把Fragment插入到Activity的布局中,或者是用應(yīng)用程序源碼將它添加到一個(gè)存在的ViewGroup中。但Fragment并不是一個(gè)定要作為Activity布局的一部分,F(xiàn)ragment也可以為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標(biāo)簽中的android:name 屬性指定了布局中實(shí)例化的Fragment類。
當(dāng)系統(tǒng)創(chuàng)建activity布局時(shí),它實(shí)例化了布局文件中指定的每一個(gè)fragment,并為它們調(diào)用onCreateView()函數(shù),以獲取每一個(gè)fragment的布局。系統(tǒng)直接在元素的位置插入fragment返回的View。
  注意:每個(gè)fragment都需要一個(gè)唯一的標(biāo)識(shí),如果重啟activity,系統(tǒng)可用來恢復(fù)fragment(并且可用來捕捉fragment的事務(wù)處理,例如移除)。
為fragment提供ID有三種方法:

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

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

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

注意:

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

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

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

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

ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
  1. 添加沒有界面的fragment。
    也可以使用fragment為activity提供后臺(tái)動(dòng)作,卻不呈現(xiàn)多余的用戶界面。
      想要添加沒有界面的fragment ,可以使用add(Fragment, String)(為fragment提供一個(gè)唯一的字符串“tag”,而不是視圖(view)ID)。這樣添加了fragment,但是,因?yàn)檫€沒有關(guān)聯(lián)到activity布局中的視圖(view) ,收不到onCreateView()的調(diào)用。所以不需要實(shí)現(xiàn)這個(gè)方法。對(duì)于無界面fragment,字符串標(biāo)簽是唯一識(shí)別它的方法。如果之后想從activity中取到fragment,需要使用findFragmentByTag()。

三、Fragment與Activity交互

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

四、Fragment事務(wù)后臺(tái)棧

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

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

 transaction.commit();

注意:

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

五、Fragment的setUserVisibleHint()

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

/**
*
* @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;
}

從上述源碼注釋我們可以看出,當(dāng)fragment被用戶可見時(shí),setUserVisibleHint()會(huì)調(diào)用且傳入true值,當(dāng)fragment不被用戶可見時(shí),setUserVisibleHint()則得到false值。而在傳統(tǒng)的fragment生命周期里也看不到這個(gè)函數(shù)。可以看出其實(shí)這個(gè)setUserVisibleHint()方法算是手動(dòng)調(diào)用的,并不是在Fragment的生命周期中自動(dòng)調(diào)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

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