建立片段

若要建立 Fragment,類別必須繼承自 Android.App.Fragment ,然後覆寫 OnCreateView 方法。 OnCreateView 當載入活動將片段放在畫面上時,會由主控活動呼叫,並會傳回 View。 一般 OnCreateView 會藉由擴充版面配置檔案,然後將它附加至父容器來建立此 View 檔案。 容器的特性很重要,因為Android會將父系的配置參數套用至 Fragment 的 UI。 下面這個範例可說明這點:

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    return inflater.Inflate(Resource.Layout.Example_Fragment, container, false);
}

上述程式代碼會擴充檢視 Resource.Layout.Example_Fragment,並將它新增為容器的 ViewGroup 子檢視。

注意

片段子類別必須具有公用預設無自變數建構函式。

將片段新增至活動

片段可以裝載於活動內的方式有兩種:

  • 以宣告方式 – 片段可以使用 標籤,以宣告方式在版面配置檔案內 .axml 使用 <Fragment>

  • 以程序設計方式 – 片段也可以使用 類別的 API 以動態 FragmentManager 方式具現化。

本指南稍後將討論透過 FragmentManager 類別的程序設計使用方式。

以宣告方式使用片段

在版面配置中新增片段需要使用 <fragment> 標記,然後藉由提供 class 屬性或 android:name 屬性來識別 Fragment。 下列代碼段示範如何使用 class 屬性來宣告 fragment

<?xml version="1.0" encoding="utf-8"?>
<fragment class="com.xamarin.sample.fragments.TitlesFragment"
            android:id="@+id/titles_fragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

下一個代碼段示範如何使用 屬性來宣告 fragmentandroid:name Fragment 類別:

<?xml version="1.0" encoding="utf-8"?>
<fragment android:name="com.xamarin.sample.fragments.TitlesFragment"
            android:id="@+id/titles_fragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

建立活動時,Android 會具現化配置檔案中指定的每個片段,並插入從 OnCreateViewFragment 建立的檢視取代 專案。 以宣告方式新增至活動的片段是靜態的,且會保留在活動上,直到終結為止;在活動附加的存留期內,無法動態取代或移除這類片段。

每個片段都必須指派唯一識別碼:

  • android:id – 如同版面配置檔案中的其他 UI 元素,這是唯一標識符。

  • android:tag – 此屬性是唯一的字串。

如果上述兩種方法都未使用,則 Fragment 會假設容器檢視的標識碼。 在下列未 android:id 提供 或 android:tag 的範例中,Android 會將識別碼 fragment_container 指派給片段:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="+@id/fragment_container"
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

        <fragment class="com.example.android.apis.app.TitlesFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
</LinearLayout>

套件名稱大小寫

Android 不允許套件名稱中的大寫字元;如果套件名稱包含大寫字元,它會在嘗試擴大檢視時擲回例外狀況。 不過,Xamarin.Android 更寬容,而且會容許命名空間中的大寫字元。

例如,下列兩個代碼段都會使用 Xamarin.Android。 不過,第二個代碼段會導致 android.view.InflateException 由純 Java 型 Android 應用程式擲回 。

<fragment class="com.example.DetailsFragment" android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="match_parent" />

OR

<fragment class="Com.Example.DetailsFragment" android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="match_parent" />

片段生命週期

片段有自己的生命週期,與裝載活動的生命週期有些獨立,但仍受其影響。 例如,當活動暫停時,其所有相關聯的片段都會暫停。 下圖概述片段的生命週期。

Flow diagram illustrating the Fragment lifecycle

片段建立生命週期方法

下列清單顯示片段生命週期中各種回呼的流程,因為片段正在建立:

  • OnInflate() – 當片段建立為檢視配置的一部分時呼叫。 從 XML 版面設定檔案以宣告方式建立片段之後,可能會立即呼叫此專案。 片段尚未與其活動相關聯,但 檢視階層中的 ActivityBundleAttributeSet 會以參數的形式傳入。 這個方法最適合用來剖析 AttributeSet ,以及儲存片段稍後可能使用的屬性。

  • OnAttach() – 在片段與活動相關聯之後呼叫。 這是當 Fragment 準備好使用時要執行的第一個方法。 一般而言,片段不應該實作建構函式或覆寫預設建構函式。 片段所需的任何元件都應該在此方法中初始化。

  • OnCreate() – 由活動呼叫以建立片段。 呼叫這個方法時,主控活動的檢視階層可能不會完全具現化,因此在片段生命週期稍後之前,片段不應依賴活動檢視階層的任何部分。 例如,請勿使用此方法對應用程式的UI執行任何調整或調整。 這是片段最早可能開始收集數據的時間。 片段目前在UI線程中執行,因此請避免任何冗長的處理,或在背景線程上執行該處理。 如果 呼叫 SetRetainInstance(true), 可能會略過這個方法。 以下將詳細說明此替代方案。

  • OnCreateView() – 建立片段的檢視。 一旦 Activity 的 OnCreate() 方法完成,就會呼叫這個方法。 此時,與活動的檢視階層互動是安全的。 這個方法應該會傳回 Fragment 將使用的檢視。

  • OnActivityCreated()– 在裝載活動完成 Activity.OnCreate 之後呼叫。 此時應該執行使用者介面的最終調整。

  • OnStart() – 在包含的活動恢復之後呼叫。 這可讓使用者看到片段。 在許多情況下,Fragment 會包含程式代碼,否則會位於 Activity 的 OnStart() 方法中。

  • OnResume() – 這是使用者與 Fragment 互動之前所呼叫的最後一個方法。 這個方法中應該執行的程式代碼類型範例,就是啟用使用者可能與之互動的裝置功能,例如定位服務的相機。 不過,這類服務可能會導致過多的電池耗盡,而應用程式應儘可能減少其用來保留電池使用時間。

片段解構生命週期方法

下一個清單說明正在終結作為片段呼叫的生命週期方法:

  • OnPause() – 用戶無法再與片段互動。 這種情況存在,因為其他片段作業正在修改此片段,或主控活動已暫停。 裝載此片段的活動可能仍然可見,也就是說,焦點中的活動部分透明或未佔用全螢幕。 當此方法變成作用中時,這是用戶離開 Fragment 的第一個指示。 片段應該儲存任何變更。

  • OnStop() – 片段已不再顯示。 主機活動可能會停止,或片段作業正在活動中修改它。 此回呼的作用與 Activity.OnStop 相同。

  • OnDestroyView() – 呼叫這個方法以清除與檢視相關聯的資源。 當與片段相關聯的檢視已終結時,就會呼叫這個值。

  • OnDestroy() – 當片段不再使用時,會呼叫這個方法。 它仍然與活動相關聯,但片段已不再運作。 這個方法應該釋放 Fragment 使用的任何資源,例如 可能用於相機的 SurfaceView 。 如果 呼叫 SetRetainInstance(true), 可能會略過這個方法。 以下將詳細說明此替代方案。

  • OnDetach() – 這個方法會在片段不再與活動相關聯之前呼叫。 片段的檢視階層已不存在,且片段所使用的所有資源此時都應該釋出。

使用 SetRetainInstance

片段可以指定,如果重新建立活動,則不應該完全終結該片段。 類別 Fragment 會針對此目的提供方法 SetRetainInstance 。 如果 true 傳遞至這個方法,則當活動重新啟動時,將會使用相同的 Fragment 實例。 如果發生這種情況,則會叫用所有回呼方法,但和 OnDestroy 生命週期回呼除外OnCreate。 此程序顯示在上圖的生命週期圖表中(由綠色虛線)。

片段狀態管理

片段可以使用 的 Bundle實例,在片段生命週期期間儲存和還原其狀態。 套件組合可讓片段將數據儲存為索引鍵/值組,而且對於不需要太多記憶體的簡單數據很有用。 Fragment 可以儲存其狀態,並呼叫 OnSaveInstanceState

public override void OnSaveInstanceState(Bundle outState)
{
    base.OnSaveInstanceState(outState);
    outState.PutInt("current_choice", _currentCheckPosition);
}

建立片段的新實例時,儲存在 中的 Bundle 狀態將會透過 OnCreate新實例的、 OnCreateViewOnActivityCreated 方法,供新實例使用。 下列範例示範如何從 Bundle擷取 值current_choice

public override void OnActivityCreated(Bundle savedInstanceState)
{
    base.OnActivityCreated(savedInstanceState);
    if (savedInstanceState != null)
    {
        _currentCheckPosition = savedInstanceState.GetInt("current_choice", 0);
    }
}

覆寫 OnSaveInstanceState 是一種適當的機制,可在片段中儲存跨方向變更的暫時性數據,例如 current_choice 上述範例中的值。 不過,的預設實 OnSaveInstanceState 作會負責針對已指派標識碼的每個檢視,在 UI 中儲存暫時性數據。 例如,查看 XML EditText 中定義元素的應用程式,如下所示:

<EditText android:id="@+id/myText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>

EditText由於控件已id指派,因此當呼叫 時OnSaveInstanceState,Fragment 會自動將數據儲存在小工具中。

套件組合限制

雖然 使用 OnSaveInstanceState 可讓您輕鬆地儲存暫時性數據,但使用此方法有一些限制:

  • 如果未將片段新增至返回堆疊,當使用者按下 [上一頁 ] 按鈕時,將不會還原其狀態。

  • 當套件組合用來儲存數據時,該數據會串行化。 這可能會導致處理延遲。

參與功能表

片段可能會將項目貢獻至其主控活動的功能表。 活動會先處理功能表項。 如果活動沒有處理程式,則會將事件傳遞至 Fragment,然後處理該片段。

若要將專案新增至活動功能表,片段必須執行兩項作業。 首先,Fragment 必須實作 方法 OnCreateOptionsMenu ,並將其專案放入功能表中,如下列程式代碼所示:

public override void OnCreateOptionsMenu(IMenu menu, MenuInflater menuInflater)
{
    menuInflater.Inflate(Resource.Menu.menu_fragment_vehicle_list, menu);
    base.OnCreateOptionsMenu(menu, menuInflater);
}

上一個代碼段中的功能表會從下列 XML 擴充,位於檔案 menu_fragment_vehicle_list.xml中:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/add_vehicle"
        android:icon="@drawable/ic_menu_add_data"
        android:title="@string/add_vehicle" />
</menu>

接下來,Fragment 必須呼叫 SetHasOptionsMenu(true)。 呼叫此方法時,會向Android宣告 Fragment 有功能表項,以參與選項功能表。 除非呼叫此方法,否則不會將片段的功能表項新增至活動的選項功能表。 這通常是在生命週期方法 OnCreate()中完成,如下一個代碼段所示:

public override void OnCreate(Bundle savedState)
{
    base.OnCreate(savedState);
    SetHasOptionsMenu(true);
}

下列畫面顯示此功能表的外觀:

Example screenshot of My Trips app displaying menu items