天天看點

Fragment學習(二): 管理Fragment和Fragment通訊

一、 管理fragment

首先,如果你想在android3.0及以下版本使用fragment,你必須引用android-support-v4.jar這個包

然後你寫的activity不能再繼承自activity類了,而是要繼承android.support.v4.app.fragmentactivity,一些其他的父類也有相應的變化.

此處,我們關系android3.0以上自帶的fragment的管理,要管理fragment們,需使用fragmentmanager,要擷取它,需在activity中調用方法getfragmentmanager()。

你可以用fragmentmanager來做以上事情:

1. 使用方法findfragmentbyid()或findfragmentbytag(),擷取activity中已存在的fragment們。

2. 使用方法popbackstack()從activity的後退棧中彈出fragment們(這可以模拟後退鍵引發的動作)。

3. 用方法addonbackstackchangedlisterner()注冊一個偵聽器以監視後退棧的變化。

你還可以使用fragmentmanager打開一個fragmenttransaction來執行fragment的事務,比如添加或删除fragment

在activity中使用fragment的一個偉大的好處是能跟據使用者的輸入對fragment進行添加、删除、替換以及執行其它動作的能力。你送出的一組fragment的變化叫做一個事務。事務通過fragmenttransaction來執行。你還可以把每個事務儲存在activity的後退棧中,這樣就可以讓使用者在fragment變化之間導航(跟在activity之間導航一樣)。

你可以通過fragmentmanager來取得fragmenttransaction的執行個體,如下:

一個事務是在同一時刻執行的一組動作(很像資料庫中的事務)。你可以用add(),remove(),replace()等方法構成事務,最後使用commit()方法送出事務。

在調用commint()之前,你可以用addtobackstack()把事務添加到一個後退棧中,這個後退棧屬于所在的activity。有了它,就可以在使用者按下傳回鍵時,傳回到fragment們執行事務之前的狀态。

如下例:示範了如何用一個fragment代替另一個fragment,同時在後退棧中儲存被代替的fragment的狀态。

解釋:newfragment代替了控件id為r.id.fragment_container所指向的viewgroup中所含的任何fragment。然後調用addtobackstack(),此時被代替的fragment就被放入後退棧中,于是當使用者按下傳回鍵時,事務發生回溯,原先的fragment又回來了。

如果你向事務添加了多個動作,比如多次調用了add(),remove()等之後又調用了addtobackstack()方法,那麼所有的在commit()之前調用的方法都被作為一個事務。當使用者按傳回鍵時,所有的動作都被反向執行(事務回溯)。

事務中動作的執行順序可随意,但要注意以下兩點:

1. 你必須最後調用commit()。

2. 如果你添加了多個fragment,那麼它們的顯示順序跟添加順序一至(後顯示的覆寫前面的)。

如果你在執行的事務中有删除fragment的動作,而且沒有調用addtobackstack(),那麼當事務送出時,那些被删除的fragment就被銷毀了。反之,那些fragment就不會被銷毀,而是處于停止狀态。當使用者傳回時,它們會被恢複。

密技:對于fragment事務,你可以應用動畫。在commit()之前調用settransition()就行。

但是,調用commit()後,事務并不會馬上執行。它會在activity的ui線程(其實就是主線程)中等待直到線程能執行的時候才執行(廢話)。如果必要,你可以在ui線程中調用executependingtransactions()方法來立即執行事務。但一般不需這樣做,除非有其它線程在等待事務的執行。

警告:你隻能在activity處于可儲存狀态的狀态時,比如running中,onpause()方法和onstop()方法中送出事務,否則會引發異常。這是因為fragment的狀态會丢失。如果要在可能丢失狀态的情況下送出事務,請使用commitallowingstateloss()。

二、 fragment與activity通訊

與activity通訊

盡管fragment的實作是獨立于activity的,可以被用于多個activity,但是每個activity所包含的是同一個fragment的不同的執行個體。

fragment可以調用getactivity()方法很容易的得到它所在的activity的對象,然後就可以查找activity中的控件們(findviewbyid())。例如:

view listview =getactivity().findviewbyid(r.id.list);同樣的,activity也可以通過fragmentmanager的方法查找它所包含的frament們。例如:

有時,你可能需要fragment與activity共享事件。一個好辦法是在fragment中定義一個回調接口,然後在activity中實作之。

例如,還是那個新聞程式的例子,它有一個activity,activity中含有兩個fragment。fragmenta顯示新聞标題,fragmentb顯示标題對應的内容。fragmenta必須在使用者選擇了某個标題時告訴activity,然後activity再告訴fragmentb,fragmentb就顯示出對應的内容(為什麼這麼麻煩?直接fragmenta告訴fragmentb不就行了?也可以啊,但是你的fragment就減少了可重用的能力。現在我隻需把我的事件告訴宿主,由宿主決定如何處置,這樣是不是重用性更好呢?)。如下例,onarticleselectedlistener接口在fragmenta中定義:

然後activity實作接口onarticleselectedlistener,在方法onarticleselected()中通知fragmentb。當fragment添加到activity中時,會調用fragment的方法onattach(),這個方法中适合檢查activity是否實作了onarticleselectedlistener接口,檢查方法就是對傳入的activity的執行個體進行類型轉換,如下所示:

如果activity沒有實作那個接口,fragment抛出classcastexception異常。如果成功了,mlistener成員變量儲存onarticleselectedlistener的執行個體。于是fragmenta就可以調用mlistener的方法來與activity共享事件。例如,如果fragmenta是一個listfragment,每次選中清單的一項時,就會調用fragmenta的onlistitemclick()方法,在這個方法中調用onarticleselected()來與activity共享事件,如下:

onlistitemclick()傳入的參數id是清單的被選中的行id,另一個fragment用這個id來從程式的contentprovider中取得标題的内容。