Fragment

認識:首先咱們知道Fragment是咱們在單個Activity上要切換多個UI界面,顯示不一樣內容。模塊化這些UI面板以便提供給其餘Acitivity使用便利。同時咱們顯示的Fragment也會受到當前的這個Acitivity生命週期影響。(而日常的Fragment有其本身的生命週期)html

 

1、使用方法:java

一、建立一個或者多個你須要的Fragment類,其實就是相似activity同樣,也有OnCreate()等回調函數。android

二、若是要讓當前的程序也就是Activity使用Fragment的話,須要把Acitivity中相關的回調函數內容搬過去。you might simply move code from your activity's callback methods into the respective callback methods of your fragmentapp

 

通常地,咱們須要如下回調函數:ide

onCreate()
模塊化

onCreateView()
函數

onPause()佈局

除了以上3個咱們基本上都要用到的回調函數方法,那麼咱們寫得Fragment中還有其餘: Handling the Fragment Lifecycleui

 

2、關於繼承Fragment類this

除了基本的Fragment類,咱們還能夠繼承的有:

DialogFragment

ListFragment

PreferenceFragment

 

3、添加用戶界面

咱們再 onCreateView() 裏面處理咱們的Fragment要顯示的界面,返回的是一個View,這個View其實就是咱們定義這個Fragment的Layout的root項(最外的、最大的那個哦!)。

從XML佈局中生成View咱們用LayoutInflater這個助手類提供的方法。

 

一、什麼是Fragment?

Fragment包含在Activity中,Fragment只能存在於Activity的上下文(context)內,沒有Activity就沒法使用Fragment,所以Fragment只能在Activity的上下文(context)建立。Fragment能夠做爲Activity的一部分,Fragment和Activity很是類似,Fragment擁有一個與她相關的視圖層次結構,擁有一個與活動很是類似的生命週期。

二、爲何要使用Fragment?

Activity的使用侷限:不能將多個Activity活動界面放在屏幕上一併顯示。所以建立了Fragment來彌補Activity的侷限。Fragment能夠像Activity同樣響應Back鍵等相似Activity的功能。

三、實現Fragment的時候,爲何要有一個默認的構造函數?

談到這兒,就不得不說一下Fragment的結構。Fragment的結構包括:視圖層次結構、初始化參數的包

首先來看一下Fragment和Activity的繼承關係,如圖1-1,1-2所示:

 
          

                          圖1-1 Fragment類繼承關係                                                                                                             圖1-2 Activity類繼承關係                         

從圖中很容易看出Fragment是直接從Object繼承的,而Activity是Context的子類。所以咱們能夠得出結論:Fragment不是Activity的擴展。可是與Activity同樣,在咱們使用Fragment的時候咱們總會擴展Fragment(或者是她的子類),並能夠經過子類更改她的行爲。

 Fragment能夠擁有一個與用戶交互的視圖層次結構,該視圖層次結構和Activity的視圖層次結構同樣,也能夠經過XML佈局規範建立(擴充)或者代碼建立。(備註:若是視圖層次結構須要向用戶顯示,則必須將Fragment的視圖層次結構附加到Activity的試圖層次結構中)

前面介紹了那麼多,只是爲了拋磚引玉,接下來進入正題,爲何Fragment必須包含一個默認的構造函數(在Java類中,若是沒有構造函數,在編譯的時候會自動建立一個不帶參數的默認構造函數。所以若是Java類中沒有其餘的構造函數,能夠將默認函數省略,編譯器會自動建立;若是Java類中有其餘構造函數時,在編譯時系統不會建立默認的構造函數,所以在有非默認構造函數時,又須要在編譯時出現默認構造函數,就必須在Java類中顯示的寫出默認構造函數)初始化參數的包——相似於活動,碎片可由系統自動保存並在之後還原。當系統還原Fragment時,她調用默認的構造函數,而後將參數包還原到新建的Fragment。該Fragment執行的後續回調可以訪問這些參數,能夠將碎片還原到上一個狀態。所以在使用Fragment時,必定要確保如下兩點:

  1. 確保Fragment類存在默認的構造函數;
  2. 在Fragment建立後當即添加一個參數包(Bundle),使Fragment重建時能夠正確設置Fragment,也使Android系統能夠在必要時正確還原Fragment。

小結:Fragment的子類必須具備默認的構造函數和一個參數包,由於Fragment在從新建立的時候會調用默認的構造函數,並且會在從新建立時將狀態保存到一個包(Bundle)對象(備註:注意區分對象包和前面所說的參數包)中,這個包(Bundle)對象會被回送到該Fragment的onCreate()回調。這個保存的包(Bundle)也會傳遞到onInflate()\onCreateView()\onActivityCreated()。(備註:這不是做爲初始化參數而附加的包。可能在這個包中存儲Fragment的當前狀態,而不是應該用於初始化她的值)

四、Fragment的生命週期是怎樣與Activity的生命週期整合的?

 在使用Fragment以前,必定要了解Fragment的生命週期。Fragment的生命週期相比Activity的生命週期要更爲複雜,理解什麼時候處理Fragment相當重要。因爲Fragment是依賴於Activity的,接下來看一下二者的生命週期有什麼異同,Fragment與Activity生命週期如圖1-三、1-4所示:

                        

                    圖1-3 Activity運行時Fragment生命週期                                                                                   圖1-4 Activity生命週期

經過對Fragment和Activity對比,將會發現許多不一樣之處,主要緣由是由於Activity和Fragment之間須要交互。 相信你們對Activity的生命週期已經很是熟悉,在此就不作過多介紹,直接切入正題介紹Fragment的生命週期。在Fragment開始階段,Fragment會以對象的形式存在於內存中。建立Fragment實例有以下兩種狀況:

  1. 建立Fragment實例;
  2. 系統從保存狀態從新建立Fragment的狀況下,將初始化參數添加到碎片對象中。當系統從保存的狀態還原Fragment時,會調用默認的構造函數,而後附件初始化參數包;

使用代碼建立Fragment的實例:

[java]  view plain copy
 
  1. public static MyFragment newInstance(int index){  
  2.     MyFragment mf = new MyFragment();  
  3.     Bundle args = new Bundle();  
  4.     args.putInt("index",index);  
  5.     mf.setArguments(args);  
  6.     return mf;  
  7. }  
1.onInflate()回調

API文檔:onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState)--Called when a fragment is being created as part of a view layout inflation, typically from setting the content view of an activity.

Called when a fragment is being created as part of a view layout inflation, typically from setting the content view of an activity. This may be called immediately after the fragment is created from a tag in a layout file. Note this is before the fragment's onAttach(Activity) has been called; all you should do here is parse the attributes and save them away.

若是Fragment是由<fragment>標記定義的(一般是在活動調用setContentView()來設置本身的主要佈局),Fragment將調用本身的onInflate()回調。這一過程當中傳入一個AttributeSet(包含來自<fragment>標記的特性)和一個保存的包。若是從新建立碎片,而且以前在onSaveInstanceState()中保存了某種狀態,此包(Bundle)將包含保存的狀態值。onInflate()預計開發人員會讀取特性值並保存她們供之後使用。在Fragment的onInflate()這一輩子命階段,對用戶界面執行任何操做都尚早,由於Fragment甚至尚未與其Activity關聯。

(備註:onInflate()文檔與實際使用不符,文檔代表onInflate()始終在onAttach()以前調用。實際上,在Activity從新啓動後,onInflate()可能在onCreateView()以後調用。這對於將值設置到包(Bundle)中並調用setArguments()而言太遲了,Android官方文檔中Fragment生命週期的圖示中也沒有將onInflate()包含在生命週期,看來Android的大牛們也很難預測onInflate()會在什麼時候回調)

2.onAttach()回調

好了,討論了那麼糾結的問題,相比你們都被我繞暈了吧,可是追求技術的道路中是不能有半點怠慢的,透徹分析問題纔是咱們追求技術的使命。若是你們都頭有點暈的話,建議你們先看部喜劇片吧,清醒一下頭腦!若是還能堅持的,就跟隨個人思路繼續探尋。onAttach()回調,回頭一看,MyGod,該方法終於出如今Fragment生命週期的圖解中了,總算是引領你們步入正軌了。

API文檔:public void onAttach (Activity activity) --Called when a fragment is first attached to its activity. onCreate(Bundle)will be called after this.

onAttach()回調將在Fragment與其Activity關聯以後調用。須要使用Activity的引用或者使用Activity做爲其餘操做的上下文,將在此回調方法中實現。

注意:Fragment類有一個getActivity()方法,返回與Fragment關聯的Activity。在Fragment的整個生命週期中,初始化參數包(Bundle)能夠從碎片的getArguments()方法得到。

切忌:將Fragment附加到Activity以後,就沒法再次調用setArguments()——除了在最開始,沒法向初始化參數添加內容。

3.onCreate()回調

接下來回調的方法就是onCreate(),你們能夠不要與Activity的onCreate()回調方法混淆了。此回調獲取傳入的參數包(備註:若是在建立時設置了參數包(Bundle)的話就能夠得到),不該該將須要依賴於Activity視圖層次結構的存在性的代碼放在此回調方法中,儘管Fragment如今可能已經與其Activity關聯,可是咱們尚未得到Activity的onCreate()已完成的通知,因此不能將依賴於Activity視圖層次結構存在性的代碼放入此回調方法中。

(備註:在onCreate()回調方法中,咱們應該儘可能避免耗時操做(避免阻塞UI線程),在實際項目中觸發後臺線程進行準備很是有用,阻塞調用應該位於後臺線程中。)

示例代碼:

[java]  view plain copy
 
  1. /** 
  2.      * During creation, if arguments have been supplied to the fragment 
  3.      * then parse those out. 
  4.      */  
  5.     @Override public void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.   
  8.         Bundle args = getArguments();  
  9.         if (args != null) {  
  10.             mLabel = args.getCharSequence("label", mLabel);  
  11.         }  
  12.     }  
4.onCreateView()回調

接下來的回調方法是onCreateView(),在該回調方法中應該返回該Fragment的一個視圖層次結構。

API文檔:public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)--Called to have the fragment instantiate its user interface view. This is optional, and non-graphical fragments can return null (which is the default implementation). This will be called betweenonCreate(Bundle)andonActivityCreated(Bundle).

 其中的Bundle爲狀態包(備註:必須和前面所說的參數包(Bundle)區分開來)注意:不要將視圖層次結構附加到傳入的ViewGroup父元素中,該關聯會自動完成。若是在此回調中將碎片的視圖層次結構附加到父元素,極可能會出現異常。實例代碼以下所示:

 

[java]  view plain copy
 
  1. /** 
  2.     * Create the view for this fragment, using the arguments given to it. 
  3.     */  
  4.    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  5.            Bundle savedInstanceState) {  
  6.        View v = inflater.inflate(R.layout.hello_world, container, false);// 不能將Fragment的視圖附加到此回調的容器元素,所以attachToRoot參數必須爲false   
  7.        View tv = v.findViewById(R.id.text);  
  8.        ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)");  
  9.        tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));  
  10.        return v;  
  11.    }  
5.onActivityCreated()回調

終於到了與用戶交互的時刻了,onActivityCreated()回調會在Activity完成其onCreate()回調以後調用。在調用onActivityCreated()以前,Activity的視圖層次結構已經準備好了,這是在用戶看到用戶界面以前你可對用戶界面執行的最後調整的地方。(備註:若是Activity和她的Fragment是從保存的狀態從新建立的,此回調尤爲重要,也能夠在這裏確保此Activity的其餘全部Fragment已經附加到該活動中了)

6. Fragment與Activity相同生命週期調用

接下來的onStart()\onResume()\onPause()\onStop()回調方法將和Activity的回調方法進行綁定,也就是說與Activity中對應的生命週期相同,所以不作過多介紹。

7.onDestroyView()回調

該回調方法在視圖層次結構與Fragment分離以後調用。

8.onDestroy()回調
再也不使用Fragment時調用。(備註:Fragment仍然附加到Activity並任然能夠找到,可是不能執行其餘操做)
9.onDetach()回調
Fragme生命週期最後回調函數,調用後,Fragment再也不與Activity綁定,釋放資源  
經過以上說明,縱觀Fragment生命週期和Activity生命週期整合後如圖1-5所示:
                          
                 圖1-5 Activity和Fragment生命週期整合                                                                                        圖1-6 Fragment生命週期
10.巧妙使用setRetainInstance()

爲何會在這兒花必定的篇幅詳細說明setRetainInstance()方法呢?由於此方法能夠有效地提升系統的運行效率,對流暢性要求較高的應用能夠適當採用此方法進行設置。

 Fragment有一個很是強大的功能——就是能夠在Activity從新建立時能夠不徹底銷燬Fragment,以便Fragment能夠恢復。在onCreate()方法中調用setRetainInstance(true/false)方法是最佳位置。當Fragment恢復時的生命週期如圖1-6所示,注意圖中的紅色箭頭。當在onCreate()方法中調用了setRetainInstance(true)後,Fragment恢復時會跳過onCreate()和onDestroy()方法,所以不能在onCreate()中放置一些初始化邏輯,切忌!

五、怎樣管理Fragment? 

既然Fragment必須存在Activity的上下文(context)內,那麼怎樣管理Fragment是咱們接下來須要關心的話題。FragmentManager組件負責管理屬於一個活動的碎片(包括後退棧上的碎片和空閒的碎片)。能夠在Activity或附加的Fragment上使用getFragmentManager()方法來獲取碎片管理器。代碼以下所示:

[java]  view plain copy
 
  1. FragmentManager fragmentManager = getFragmentManager()  
  2. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();  
相關文章
相關標籤/搜索