Android4.0-Fragment架構實現方式剖析

來源:互聯網
上載者:User

標籤:

經過反覆的學習對比,個人覺得帶著問題學習新知是最有效學習方式,因此文本就以提問的方式來講述Fragment架構實現方式。

1、什麼是Fragment?

Fragment包含在Activity中,Fragment只能存在於Activity的上下文(context)內,沒有Activity就無法使用Fragment,因此Fragment只能在Activity的上下文(context)建立。Fragment可以作為Activity的一部分,Fragment和Activity非常相似,Fragment擁有一個與她相關的視圖階層,擁有一個與活動非常相似的生命週期。

2、為什麼要使用Fragment?

Activity的使用局限:不能將多個Activity活動介面放在螢幕上一併顯示。因此建立了Fragment來彌補Activity的局限。Fragment可以像Activity一樣響應Back鍵等類似Activity的功能。

3、實現Fragment的時候,為什麼要有一個預設的建構函式?

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

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的目前狀態,而不是應該用於初始化她的值)

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

 在使用Fragment之前,一定要瞭解Fragment的生命週期。Fragment的生命週期相比Activity的生命週期要更為複雜,理解何時處理Fragment至關重要。由於Fragment是依賴於Activity的,接下來看一下兩者的生命週期有什麼異同,Fragment與Activity生命週期1-3、1-4所示:


通過對Fragment和Activity對比,將會發現許多不同之處,主要原因是因為Activity和Fragment之間需要互動。 相信大家對Activity的生命週期已經非常熟悉,在此就不做過多介紹,直接切入正題介紹Fragment的生命週期。在Fragment開始階段,Fragment會以對象的形式存在於記憶體中。建立Fragment執行個體有如下兩種情況:

  1. 建立Fragment執行個體;

  2. 系統從儲存狀態重新建立Fragment的情況下,將初始化參數添加到片段對象中。當系統從儲存的狀態還原Fragment時,會調用預設的建構函式,然後附件初始化參數包;

使用代碼建立Fragment的執行個體:

public static MyFragment newInstance(int index){    MyFragment mf = new MyFragment();    Bundle args = new Bundle();    args.putInt("index",index);    mf.setArguments(args);    return mf;}

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線程),在實際項目中觸發後台線程進行準備非常有用,阻塞調用應該位於後台線程中。)

範例程式碼:

/**     * During creation, if arguments have been supplied to the fragment     * then parse those out.     */    @Override public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Bundle args = getArguments();        if (args != null) {            mLabel = args.getCharSequence("label", mLabel);        }    }

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父元素中,該關聯會自動完成。如果在此回調中將片段的視圖階層附加到父元素,很可能會出現異常。執行個體代碼如下所示:

 /**     * Create the view for this fragment, using the arguments given to it.     */    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        View v = inflater.inflate(R.layout.hello_world, container, false);// 不能將Fragment的視圖附加到此回調的容器元素,因此attachToRoot參數必須為false         View tv = v.findViewById(R.id.text);        ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)");        tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));        return v;    }
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所示:

10.巧妙使用setRetainInstance()

為什麼會在這兒花一定的篇幅詳細說明setRetainInstance()方法呢?因為此方法可以有效地提高系統的運行效率,對流暢性要求較高的應用可以適當採用此方法進行設定。

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

5、怎樣管理Fragment? 

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

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


Android4.0-Fragment架構實現方式剖析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.