標籤:
分類:C#、Android、VS2015;
建立日期:2016-02-22 一、簡介
想要管理activity中的fragment,可以用FragmentManager類來實現。通過在activity中調用GetFragmentManager()可獲得該類的執行個體。
使用FragmentManager可以做的事情有:
l 使用FindFragmentById()(用於在activity布局中提供有介面的fragment)或者FindFragmentByTag()擷取activity中存在的fragment(用於有介面或者沒有介面的fragment)。
l 使用PopBackStack()(模仿使用者的BACK命令)從後台棧彈出fragment。
l 使用AddOnBackStackChangedListener()註冊一個監聽後台棧變化的監聽器。
關於這些函數和其它的更多資訊,可參考FragmentManager類的文檔。 二、建立Fragment
1、添加Example_Fragment.axml檔案
通常,fragment構建了其宿主activity的部分介面,它被作為activity全部視圖層次體系的一部分被嵌入進去。
下面是含有兩個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.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
<fragment>中的android:name屬性指定了布局中執行個體化的Fragment類。
注意:Java不允許使用大寫字母的包名(Java的“包”在C#中稱為“命名空間”),但是在C#中沒有這個限制。例如,下面兩種方式都是合法的:
<fragment android:name="com.example.DetailsFragment"
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
也可以這樣寫:
<fragment android:name="Com.Example.DetailsFragment"
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
當系統建立activity布局時,它執行個體化了布局檔案中指定的每一個fragment,並為它們調用OnCreateView()函數,以擷取每一個fragment的布局。系統直接在<fragment>元素的位置插入fragment返回的View。
注意:每個fragment都需要一個唯一的標識(ID),其用途是:如果重啟activity,系統可用來恢複fragment(並且可用來捕捉fragment的交易處理,例如移除)。
為fragment提供ID有三種方法:
- 用android:id屬性提供一個唯一的標識。
- 用android:tag屬性提供一個唯一的字串。
- 如果上述兩個屬性都沒有,系統會使用其[內容] 檢視(view)的ID。
也可以通過C#代碼將fragment添加到已存在的ViewGroup中。
在activity啟動並執行任何時候,可以將fragment添加到activity布局中,此時僅需要簡單指定用來放置fragment的ViewGroup。
2、在.cs檔案中建立Fragment
要在.cs檔案中建立一個自訂的fragment類,必須讓其繼承自fragment類或其子類。在自訂的fragment類中實現的代碼看起來很像activity,例如重寫OnCreate()、OnStart()、OnPause()、OnStop()、……等。事實上,如果你將一個現成的Android應用程式改為用Fragment來實現,甚至可以直接將代碼從activity的回調方法中移植到fragment的回調方法中。
下面這段代碼通過Inflate()方法Resource/Layout檔案夾下的Example_Fragment.axml填充到視圖中,並將它作為一個子視圖添加到分組容器內。
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.Example_Fragment, container, false);
}
注意:繼承自fragment的子視圖必須包含一個預設的無參數的建構函式。
3、需要重寫哪些方法
在fragment生命週期內,一般情況下,你至少需要重寫(override)以下三個回調方法:Infalate()方法、OnCreate()方法、OnPause()方法。
大部分應用程式都應該至少為每個fragment實現這三個方法,但是還有許多其他用以操縱fragment生命週期中各個階段的回呼函數。所有生命週期中的回呼函數在操縱fragment生命週期一節中稍後再做討論。
除了基類fragment,這裡還有幾個你可能會繼承的子類:
DialogFragment:顯示一個浮動的對話方塊。使用這個類建立對話方塊是使用Activity類對話方塊協助方法之外的另一個不錯的選擇,因為你可以把fragment對話方塊併入到由activity管理的fragments後台棧中,允許使用者返回到一個已經摒棄的fragment。
ListFragment:顯示一個由適配器管理的條目列表(例如SimpleCursorAdapter),類似於ListActivity。它提供了許多管理列表視圖的方法,例如OnListItemClick()。
PreferenceFragment:顯示一個Preference對象的體繫結構列表,類似於preferenceActivity。這在為應用程式建立“設定”activity時是很實用的。
4、將fragment添加到activity布局中
fragment常被用作activity使用者介面的一部分,並且將本身的布局構建到activity中去。
為了給fragment提供一個布局,你必須實現OnCreateView()方法,在繪製fragment布局時Android系統會調用它。實現這個方法時需要返回fragment所屬的根View。
注意:如果fragment是ListFragment的子類,它會預設實現從OnCreateView()返回一個ListView,所以不需要再實現它。
通常,fragment構建了其宿主activity的部分介面,它被作為activity全部視圖層次體系的一部分被嵌入進去。在acitivity布局中添加fragment有兩種方式:
(1)方式1--在布局檔案中添加fragment
可以像為view指定布局屬性那樣為fragment指定布局屬性。例如:
<?xml version="1.0" encoding="utf-8"?>
<fragment android:name="FragmentDemo.TitlesFragment"
android:id="@+id/titles_fragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<fragment>中的android:name屬性指定了布局中執行個體化的Fragment類。
當系統建立activity布局時,它執行個體化了布局檔案中指定的每一個fragment,並為它們調用OnCreateView()函數,以擷取每一個fragment的布局。系統直接在<fragment>元素的位置插入fragment返回的View。
注意:每個fragment都需要一個唯一的標識(ID),其用途是:如果重啟activity,系統可用來恢複fragment(並且可用來捕捉fragment的交易處理,例如移除)。
為fragment提供ID有三種方法:
- 用android:id屬性提供一個唯一的標識。
- 用android:tag屬性提供一個唯一的字串。
- 如果上述兩個屬性都沒有,系統會使用其[內容] 檢視(view)的ID。
也可以通過C#代碼將fragment添加到已存在的ViewGroup中。
在activity啟動並執行任何時候,可以將fragment添加到activity布局中,此時僅需要簡單指定用來放置fragment的ViewGroup。
(2)方式2--在.cs檔案中添加fragment
通過代碼添加fragment時,應該使用FragmentTransaction的API來對activity中的fragment進行操作(例如添加、移除,或者替換fragment)。可以像下面這樣從Activity中取得FragmentTransaction的執行個體:
FragmentManager fragmentManager = new FragmentManager()
FragmentTransaction fragmentTransaction = this.FragmentManager.BeginTransaction();
可以用Add()函數添加fragment,並指定要添加的fragment以及要將其插入到哪個視圖(view)之中:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.Add(Resource.id.fragment_container, fragment);
fragmentTransaction.Commit();
傳入Add()函數的第一個參數是fragment被放置的ViewGroup,它由資源ID(resource ID)指定,第二個參數就是要添加的fragment。
上面的代碼也可以這樣寫:
ExampleFragment fragment = new ExampleFragment()
.Add(Resource.id.fragment_container, fragment);
.Commit();
一旦通過FragmentTransaction 做了更改,都應當調用Commit()使變化生效。
另外,也可以使用fragment為activity提供後台動作,卻不呈現多餘的使用者介面。
添加沒有介面的fragment時,可通過調用Add(Fragment, String)方法來實現,該方法為fragment提供一個唯一的字串“tag”,而不是視圖(view)ID。
用這種辦法添加fragment後,因為沒有將其關聯到activity布局中的視圖(view),因此活動不會調用OnCreateView()方法,所以程式中不需要重寫OnCreateView()。
為無介面fragment提供字串標籤並不是專門針對無介面fragment的——也可以為有介面fragment提供字串標籤——但是對於無介面fragment,字串標籤是唯一識別它的方法。如果之後想從activity中取到fragment,需要使用FindFragmentByTag()。二、處理Fragment事務
在activity中使用fragment的一大特點是具有添加、刪除、替換,和執行其它動作的能力,以響應使用者的互動。提交給activity的一系列變化被稱為事務,並且可以用FragmentTransaction 中的APIs處理。
你也可以將每一個事務儲存在由activity管理的後台棧中,並且允許使用者導航回退fragment變更(類似於activity的回退導航)。
可以從FragmentManager中擷取FragmentTransaction執行個體,像這樣:
FragmentManager fragmentManager = new FragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.BeginTransaction();
由於每項事務都是在同一時間內要執行的一系列的變更,因此你可以為一個給定的事務用相關方法設定想要執行的所有變化,例如add()、remove()、replace()。然後,用commit()將事務提交給activity。
但是,在調用Commit()之前,為了將事務添加到fragment的事務後台棧中,你可能希望調用AddToBackStatck()。這個後台棧由activity管理,並且允許使用者通過按【Back】鍵回退到前一個fragment狀態。
下面的代碼示範如何使用另一個fragment代替一個fragment,並且將之前的狀態保留在後台棧中:
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = this.FragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(Resources.id.fragment_container, newFragment);
transaction.AddToBackStack(null);
// Commit the transaction
transaction.Commit();
在這個例子中,newFragment替換了當前在版面配置容器中用Resources.id.fragment_container標識的所有的fragment(如果有的話),替代的事務被儲存在後台棧中,因此使用者可以回退該事務,可通過按【Back】鍵還原之前的fragment。
如果添加多個變更事務,例如另一個Add()或者Remove(),並調用AddToBackStack(),那麼在調用Commit()之前的所有應用的變更被作為一個單獨的事務添加到後台棧中,並且【Back】鍵可以將它們一起回退。
將變更添加到FragmentTransaction中的順序注意以下兩點:
- 必須要在最後調用Commit()。如果你正將多個fragment添加到同一個容器中,那麼添加順序決定了它們在視圖層次(view hierarchy)裡顯示的順序。
- 在執行刪除fragment事務時,如果沒有調用AddToBackStack(),那麼事務一提交fragment就會被銷毀,而且使用者也無法回退它。然而,當移除一個fragment時,如果調用了AddToBackStack(),那麼之後fragment會被停止,如果使用者回退,它將被恢複過來。
提示:對於每一個fragment事務,在提交之前通過調用SetTransition()來應用一系列事務動作。
調用Commit()並不立刻執行事務,相反,而是採取預約方式,一旦activity的介面線程(主線程)準備好便可運行起來。然而,如果有必要的話,你可以從介面線程調用ExecutePendingTransations()立即執行由Commit()提交的事務。但這樣做,通常是沒有必要的,除非其它線程的工作依賴與該項事務。
警告:只能在activity儲存狀態之前用Commit()提交事務。如果你嘗試在那時之後提交,會拋出一個異常。這是因為如果activity需要被恢複,提交後的狀態會被丟失。對於這類丟失提交的情況,可通過調用CommitAllowingStateLoss()來解決。
1、與Activity的互動
儘管Fragment被實現為一個對象,它獨立於Activity並可以在多個Activity中使用,一個給定的fragment執行個體直接被捆綁在包含它的Activity中。
特別是,fragment可以通過GetActivity()函數訪問Activity,並且很容易的執行類似於尋找activity布局中的視圖的任務:
View listView = Activity.FindViewById(Resources.id.list);
同樣的,activity能夠調用fragment的函數FindFragmentById()或者FindFragmentByTag(),從FragmentManager中擷取Fragment的索引,例如:
var fragment = (ExampleFragment) FragmentManager.FindFragmentById(Resources.id.example_fragment);
2、建立activity的事件回呼函數
在一些情況下,你可能需要fragment與activity共用事件。這樣做的一個好方法是在fragment內部定義一個回調介面,並需要宿主activity實現它。當activity通過介面接收到回調時,可以在必要時與布局中的其它fragment共用資訊。
舉個例子,如果新聞應用的actvity中有兩個fragment——一個顯示文章列表(fragment A),另一個顯示一篇文章(fragment B)——然後fragment A 必須要告訴activity清單項目何時被選中,這樣,activity可以通知fragment B顯示這篇文章。這種情況下,在fragment A內部聲明介面OnArticleSelectedListener:
public static class FragmentA : ListFragment
{
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener
{
public void OnArticleSelected(Uri articleUri);
}
...
}
然後fragment的宿主activity實現OnArticleSelectedListener介面,並且重寫OnArticleSelected()以通知fragment B來自於fragment A的事件。為了確保宿主activity實現了這個介面,fragment A的OnAttach()回呼函數(當添加fragment到activity中時系統會調用它)通過作為參數傳入onAttach()的activity的類型轉換來執行個體化一個OnArticleSelectedListener執行個體。
public static class FragmentA : ListFragment
{
OnArticleSelectedListener mListener;
...
public override void OnAttach(Activity activity)
{
base.OnAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}
如果activity沒有實現這個介面,那麼fragment會拋出一個ClassCaseException異常。一旦成功,mListener成員會保留一個activity的OnArticleSelectedListener實現的引用,由此fragment A可以通過調用由OnArticleSelectedListener介面定義的方法與activity共用事件。例如,如果fragment A是ListFragment的子類,每次使用者點擊清單項目時,系統都會調用fragment的OnListItemClick()事件,然後fragment調用OnArticleSelected()來與activity共用事件。
public static class FragmentA : ListFragment
{
OnArticleSelectedListener mListener;
...
public override void OnListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item‘s row ID with the content provider Uri
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.OnArticleSelected(noteUri);
}
...
}
傳遞給OnListItemClick()的參數id是點擊的清單項目行id,activity(或者其它fragment)用以從應用的ContentProvider擷取文章。
關於使用content provider的更多資料可以在在Content Providers文檔擷取。
3、添加items到功能表項目中
你的fragments可以通過實現OnCreateOptionsMenu()來構建功能表項目到activity的Options Menu(Action Bar也一樣)。為了使用這個方法接收到調用,不管怎樣,你必須在OnCreate()期間調用SetHasOptionsMenu(),來指明想要添加項目到Options Menu的那個fragment(否則,fragment將接收不到onCreateOptionsMenu()的調用)。
任何想要在fragment中的Options Menu添加的項目都追加到已有的功能表項目後面。當功能表項目被選中時,fragment也會接收到對OnOptionsItemSelected()的回調。
你也可以通過調用registerForContextMenu()在fragment布局中註冊一個view以提供一個右鍵捷徑功能表(context menu)。當使用者開啟context menu時,fragment接收到對OnCreateContextMenu()的回調。當使用者選中一個項目時,fragment接收到對OnContextItemSelected()的回調。
注意:儘管你的fragment會接收到為添加到每個功能表項目被選擇功能表項目的回調,但當使用者選擇一個功能表項目時,activity會首先接收到對應的回調。如果activity的選擇功能表項目回調的實現沒有處理被選中的項目,那麼該事件被傳遞給fragment的回調。這同樣適用於Options Menu和context menu。
關於菜單的更多資訊,請參考菜單和Action Bar開發指南。三、處理Fragment的生命週期
管理fragment生命週期與管理activity生命週期很相像。像activity一樣,fragment也有三種狀態:
- Resumed:fragment在運行中的activity可見。
- Paused:另一個activity處於前台且得到焦點,但是這個fragment所在的activity仍然可見(前台activity部分透明,或者沒有覆蓋全屏)。
- Stopped:fragment不可見。要麼宿主activity已經停止,要麼fragment已經從activity上移除,但已被添加到後台棧中。一個停止的fragment仍然活著(所有狀態和成員資訊仍然由系統保留著)。但是,它對使用者來講已經不再可見,並且如果activity被殺掉,它也將被殺掉。
同activity類似的還有,你也可以用Bundle儲存fragment狀態,萬一activity的進程被殺掉了,並且在activity被重新建立時,你需要恢複fragment狀態。在回調執行fragment的OnSaveInstanceState()期間可以儲存狀態,在OnCreate(),OnCreateView(),或者OnActvityCreate()中可以恢複狀態。更多關於儲存狀態的資訊,參考Activities文檔。
1、管理fragment的生命週期
在生命週期方面,activity與fragment之間一個很重要的不同,就是在各自的後台棧中是如何儲存的。當activity停止時,預設情況下,activity被安置在由系統管理的activity後台棧中(因此使用者可以按【Back】鍵回退導航,就像在Tasks和後台棧中討論的那樣)。但是,僅當你在一個事務被移除時,通過顯式調用AddToBackStack()請求儲存的執行個體,該fragment才被置於由宿主activity管理的後台棧。
除此之外,管理fragment的生命週期與管理activity的生命週期非常相似。所以,管理activity生命週期的實踐同樣也適用於fragment。你需要瞭解的,僅僅是activity的生命週期如何影響fragment的的。
2、與activity生命週期的協調合作
fragment所生存的activity生命週期直接影響著fragment的生命週期,由此針對activity的每一個生命週期回調都會引發一個fragment類似的回調。例如,當activity接收到OnPause()時,這個activity之中的每個fragment都會接收到onPause()。
Fragment有一些額外的生命週期回調方法,然而,為了處理像是建立和銷毀fragment介面,它與activity進行獨特的互動。這些額外的回調方法是:
OnAttach():當fragment被綁定到activity時調用(Activity會被傳入)。
OnCreateView():建立與fragment相關的視圖體系時被調用。
OnActivityCreated():當activity的OnCreate()函數返回時被調用。
OnDestroyView():當與fragment關聯的視圖體系正被移除時被調用。
OnDetach():當fragment正與activity解除關聯時被調用。
這些方法的詳細介紹見11.4節。
fragment的生命週期流程實際上是受其宿主activity影響。例如,當activity接收到它的OnCreate()回調時,activity之中的fragment接收到的僅僅是OnActivityCreated()回調。
一旦activity處於Resumed狀態,則可以在activity中自由的添加或者移除fragment。因此,只有當activity處於resumed狀態時,fragment的生命週期才可以獨立變化。
然而,當activity離開恢複狀態時,fragment再一次被activity推入它的生命週期中。四、幾個特殊的Fragment類
Fragments API提供了以下子類,利用這些類可實現一些通用的功能。這些類有:
- ListFragment – 將Fragment作為清單項目來使用。
- DialogFragment – 將Fragment作為對話方塊來顯示。
- PreferenceFragment – 將Fragment作為一系列偏好項來顯示。
下一節我們將用具體例子示範Fragment的基本用法。
【Android】11.5 建立和管理Fragments