【Android】11.5 建立和管理Fragments

來源:互聯網
上載者:User

標籤:

分類: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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.