天天看點

Android:Fragment官方文檔

概述

Fragment表現Activity中UI的一個行為或者一部分。可以将多個fragment組合在一起,放在一個單獨的activity中來建立一個多界面區域的UI,并可以在多個activity裡重用某一個fragment。把fragment想象成一個activity的子產品化區域,有它自己的生命周期,接收屬于它自己的輸入事件,并且可以在activity運作期間添加和删除.

Fragment必須總是被嵌入到一個activity中。它們的生命周期直接受其宿主activity的生命周期影響。 例如:當activity被暫停,那麼在其中的所有fragment也被暫停;當activity被銷毀,所有隸屬于它的fragment也被銷毀。然而,當一個activity正在運作時(處于resumed狀态),我們可以獨立地操作每一個fragment,比如添加或删除它們。當處理這樣一種fragment事務時,可以将它添加到activity所管理的back stack —— 每一個activity中的back stack實體都是一個發生過的fragment事務的記錄。back stack允許使用者通過按下 BACK 按鍵從一個fragment事務後退(往後導航)。

将一個fragment作為activity布局的一部分添加進來時,它處在activity的view hierarchy中的ViewGroup中,并且定義有它自己的view布局。通過在activity的布局檔案中聲明fragment來插入一個fragment到你的activity布局中,或者可以寫代碼将它添加到一個已存在的ViewGroup。然而,fragment并不一定必須是activity布局的一部分;如果願意的話,也可以将一個fragment隐藏在activity的背景工作。

本文檔描述了如何使用fragment建立應用程式,包括:當被添加到activity的back stack後,fragment如何維護它們的狀态。在activity中,與activity和其他fragment共享事件。建構到activity的action bar。以及更多内容。

設計哲學     Android在3.0中引入了fragments的概念,主要目的是用在大螢幕裝置上——例如平闆電腦上,支援更加動态和靈活的UI設計。平闆電腦的螢幕要比手機的大得多,有更多的空間來放更多的UI元件,并且這些元件之間會産生更多的互動。Fragment允許這樣的一種設計,而不需要你親自來管理view hierarchy的複雜變化。通過将activity的布局分散到fragment中,可以在運作時修改activity的外觀,并在由activity管理的back stack中儲存那些變化。

例如,一個新聞應用可以在螢幕左側使用一個fragment來展示一個文章的清單,然後在螢幕右側使用另一個fragment來展示一篇文章 —— 2個fragment并排顯示在相同的一個activity中,并且每一個fragment擁有它自己的一套生命周期回調方法,并且處理它們自己的使用者輸入事件。是以,用這種方式來取代使用一個activity來選擇一篇文章,而另一個activity來閱讀文章的模式, 使用者可以在相同的activity中選擇一篇文章并且閱讀, 如圖所示: 

Android:Fragment官方文檔

 fragment在應用中應當是一個子產品化和可重用的元件。即,由于fragment定義了它自己的布局,以及通過使用它自己的生命周期回調方法定義了它自己的行為,是以可以将fragment包含到多個activity中。這點特别重要,因為這允許你将你的使用者體驗适配到不同的螢幕尺寸。舉個例子,你可能會僅當在螢幕尺寸足夠大時,在一個activity中包含多個fragment,當不屬于這種情況時,會啟動另一個單獨的、使用不同fragment的activity。

    繼續之前那個新聞的例子 —— 當運作在一個特别大的螢幕時(例如平闆電腦),app可以在Activity A中嵌入2個fragment。然而,在一個正常尺寸的螢幕(例如手機)上,沒有足夠的空間同時供2個fragment用,是以,Activity A 會僅包含文章清單的fragment,而當使用者選擇一篇文章時,它會啟動Activity B,它包含閱讀文章的fragment。是以,應用可以同時支援圖1中的2種設計模式。

建立Fragment     

Android:Fragment官方文檔

要建立一個fragment,必須建立一個 Fragment 的子類(或者繼承自一個已存在的它的子類)。 Fragment 類的代碼看起來很像Activity。它包含了與activity類似的回調方法,例如onCreate()、onStart()、onPause(),以及onStop()。事實上,如果你準備将一個現成的Android應用轉換到使用fragment技術,可能隻需簡單的将代碼從activity的回調函數分别移動到fragment的回調方法。

通常,應當至少實作如下的生命周期方法:

  • onCreate()

    當建立fragment時,系統調用此方法。

    在實作代碼中,應當初始化想要在fragment中保持的必要元件,當fragment被暫停或者停止後可以恢複。

  • onCreateView()

    fragment第一次繪制它的使用者界面的時候,系統會調用此方法。為了繪制fragment的UI,此方法必須傳回一個View,這個view是你的fragment布局的根view。如果fragment不提供UI,可以傳回null。 

  • onPause()

    使用者将要離開fragment時,系統調用這個方法作為第一個訓示(然而它不總是意味着fragment将被銷毀。) 在目前使用者會話結束之前,通常應當在這裡送出任何應該持久化的變化(因為使用者有可能不會傳回)。

大多數應用應當為每一個fragment實作至少這3個方法,但是還有一些其他回調方法你也應當用來去處理fragment生命周期的各種階段。全部的生命周期回調方法将會在後面章節 《處理Fragment生命周期》 中讨論。

除了繼承基類Fragment,還有一些子類你可能會繼承:

  • DialogFragment

    顯示一個浮動的對話框。

    用這個類來建立一個對話框,是除了使用Activity類的對話框工具方法之外的一個好的選擇,

    因為你可以将一個fragment對話框合并到activity管理的fragment back stack中,允許使用者傳回到一個之前曾被摒棄的fragment. 

  • ListFragment

    顯示一個由一個adapter(例如 SimpleCursorAdapter)管理的項目的清單,類似于ListActivity。

    它提供一些方法來管理一個list view,例如onListItemClick()回調來處理點選事件。

  • PreferenceFragment

    顯示一個 Preference對象的層次結構的清單,類似于 PreferenceActivity。

    這在為你的應用建立一個“設定”activity時有用處。

添加一個使用者界面

    fragment通常用來作為一個activity的使用者界面的一部分,并将它的layout提供給activity。為了給fragment提供一個layout,必須實作onCreateView()回調方法,當到了fragment繪制它自己的layout的時候,Android系統調用它。此方法的實作代碼必須傳回fragment的layout的根view。

注意:如果你的fragment是ListFragment的子類,它的預設實作是傳回從onCreateView()傳回一個ListView,是以一般情況下不必實作它。

從onCreateView()傳回的View,也可以從一個xml layout資源檔案中讀取并生成。為了幫助你這麼做,onCreateView() 提供了一個 LayoutInflater 對象。

舉個例子,這裡有一個Fragment的子類,從檔案 example_fragment.xml 加載了一個layout:

1

public

static

class

ExampleFragment 

extends

Fragment {

2

@Override

3

public

View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

4

// Inflate the layout for this fragment

5

return

inflater.inflate(R.layout.example_fragment, container, 

false

);

6

}

7

}

傳入 onCreateView() 的 container 參數是你的fragment layout将要插入的父ViewGroup(來自activity的layout)。savedInstanceState 參數是一個 Bundle,如果fragment是被恢複的,它提供關于fragment的之前的執行個體的資料。

inflate() 方法有3個參數:

  • 想要加載的layout的resource ID。
  • 加載的layout的父ViewGroup。

    傳入container是很重要的,目的是為了讓系統接受所要加載的layout的根view的layout參數,

    由它将挂靠的父view指定。

  • 布爾值訓示在加載期間,展開的layout是否應當附着到ViewGroup (第二個參數)。

    (在這個例子中, 指定了false, 因為系統已經把展開的layout插入到container – 傳入true會在最後的layout中建立一個多餘的view group。)

将fragment添加到activity

通常地,fragment為宿主activity提供UI的一部分,被作為activity的整個view hierarchy的一部分被嵌入。 有2種方法你可以添加一個fragment到activity layout:

在activity的layout檔案中聲明fragment

可以像使用View一樣,為fragment指定layout屬性。

例子是一個有2個fragment的activity:

01

<?

xml

version

=

"1.0"

encoding

=

"utf-8"

?>

02

<

LinearLayout

xmlns:android

=

"http://schemas.android.com/apk/res/android"

03

android:orientation

=

"horizontal"

04

android:layout_width

=

"match_parent"

05

android:layout_height

=

"match_parent"

>

06

<

fragment

android:name

=

"com.example.news.ArticleListFragment"

07

android:id

=

"@+id/list"

08

android:layout_weight

=

"1"

09

android:layout_width

=

"0dp"

10

android:layout_height

=

"match_parent"

/>

11

<

fragment

android:name

=

"com.example.news.ArticleReaderFragment"

12

android:id

=

"@+id/viewer"

13

android:layout_weight

=

"2"

14

android:layout_width

=

"0dp"

15

android:layout_height

=

"match_parent"

/>

16

</

LinearLayout

>

<fragment> 中的 android:name 屬性指定了在layout中執行個體化的Fragment類。

當系統建立這個activity layout時,它執行個體化每一個在layout中指定的fragment,并調用它們的onCreateView()方法,來擷取每一個fragment的layout,系統将從fragment傳回的 View 直接插入到<fragment>元素所在的地方。

注意:每一個fragment都需要一個唯一的辨別,如果activity重新開機,系統可以用來恢複fragment(并且你也可以用來捕獲fragment來處理事務,例如移除它。) 

有3種方法來為一個fragment提供一個辨別:

  • 為 android:id 屬性提供一個唯一ID。
  • 為 android:tag 屬性提供一個唯一字元串。
  • 如果以上2個你都沒有提供,系統使用容器view的ID。

撰寫代碼将fragment添加到一個已存在的ViewGroup。

當activity運作的任何時候,都可以将fragment添加到activity layout。隻需簡單的指定一個需要放置fragment的ViewGroup。為了在你的activity中操作fragment事務(例如添加、移除、或替換一個fragment),必須使用來自 FragmentTransaction 的API。

按如下方法,從Activity取得一個 FragmentTransaction 的執行個體:

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

然後使用 add() 方法添加一個fragment,指定要添加的fragment, 以及要插入的view。

1

ExampleFragment fragment = 

new

ExampleFragment();

2

fragmentTransaction.add(R.id.fragment_container, fragment);

3

fragmentTransaction.commit();

add()的第一個參數是fragment要放入的ViewGroup,由resource ID指定,第二個參數是需要添加的fragment。一旦用FragmentTransaction作了改變,為了使改變生效,必須調用commit()。

添加一個無UI的fragment

之前的例子展示了對UI的支援,如何将一個fragment添加到activity。然而,也可以使用fragment來為activity提供背景行為而不用展現額外的UI。

要添加一個無UI的fragment,需要activity使用 add(Fragment, String) 來添加 fragment (第二個參數為fragment提供一個唯一的字元串"tag",而不是一個view ID)。這麼做雖然添加了fragment,但因為它沒有關聯到一個activity layout中的某個view,是以不會接收到onCreateView()調用。是以不必實作此方法。

為fragment提供一個字元串tag并不是專門針對無UI的fragment的 —— 也可以提供字元串tag給有UI的fragment —— 但是如果fragment沒有UI,那麼這個tag是僅有的辨別它的途徑。如果随後你想從activity擷取這個fragment,需要使用 findFragmentByTag()。

管理Fragment 要在activity中管理fragment,需要使用FragmentManager。通過調用activity的getFragmentManager()取得它的執行個體。

可以通過FragmentManager做一些事情,包括:

  • 使用findFragmentById() (用于在activity layout中提供UI的fragment)或findFragmentByTag() (适用于有或沒有UI的fragment)擷取activity中存在的fragment。
  • 使用 popBackStack() (模拟使用者按下BACK 指令),将fragment從背景堆棧中彈出。
  • 使用addOnBackStackChangeListener()注冊一個監聽背景堆棧變化的listener。

處理Fragment事務 關于在activity中使用fragment的很強的一個特性是:根據使用者的互動情況,對fragment進行添加、移除、替換、以及執行其他動作。送出給activity的每一套變化被稱為一個事務,使用FragmentTransaction API 處理。我們也可以儲存每一個事務到一個activity管理的back stack,允許使用者經由fragment的變化往回導航(類似于通過activity往後導航)。

從 FragmentManager 獲得一個FragmentTransaction的執行個體:

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

每一個事務都是同時要執行的一套變化。可以在一個給定的事務中設定你想執行的所有變化,使用諸如 add()、remove()、以及replace()。然後要給activity應用事務,必須調用 commit()。

在調用commit()之前,你可能想調用addToBackStack(),将事務添加到一個fragment事務的back stack。這個back stack由activity管理,并允許使用者通過按下 BACK 按鍵傳回到前一個fragment狀态。

舉個例子,這裡是如何将一個fragment替換為另一個,并在背景堆棧中保留之前的狀态:

1

// Create new fragment and transaction

2

Fragment newFragment = 

new

ExampleFragment();

3

FragmentTransaction transaction = getFragmentManager().beginTransaction();

4

// Replace whatever is in the fragment_container view with this fragment,

5

// and add the transaction to the back stack

6

transaction.replace(R.id.fragment_container, newFragment);

7

transaction.addToBackStack(

null

);

8

// Commit the transaction

9

transaction.commit();

在這個例子中,newFragment 替換了目前layout容器中的由R.id.fragment_container辨別的fragment。通過調用addToBackStack(),replace事務被儲存到back stack,是以使用者可以回退事務,并通過按下BACK按鍵帶回前一個fragment。

如果添加多個變化到事務(例如add()或remove())并調用addToBackStack(),然後在你調用commit()之前的所有應用的變化會被作為一個單個事務添加到背景堆棧,BACK按鍵會将它們一起回退。

添加變化到 FragmentTransaction的順序不重要,除以下例外:

  • 必須最後調用 commit()。
  • 如果添加多個fragment到同一個容器,那麼添加的順序決定了它們在view hierarchy中顯示的順序。

當執行一個移除fragment的事務時,如果沒有調用 addToBackStack(),那麼當事務送出後,那個fragment會被銷毀,并且使用者不能導航回到它。有鑒于此,當移除一個fragment時,如果調用了 addToBackStack(),那麼fragment會被停止,如果使用者導航回來,它将會被恢複。

提示: 對于每一個fragment事務,你可以應用一個事務動畫,通過在送出事務之前調用setTransition()實作。

調用 commit() 并不立即執行事務。恰恰相反,它将事務安排排期,一旦準備好,就在activity的UI線程上運作(主線程)。如果無論如何有必要,你可以從你的UI線程調用 executePendingTransactions() 來立即執行由commit()送出的事務。但這麼做通常不必要,除非事務是其他線程中的job的一個從屬。

警告: 你隻能在activity儲存它的狀态(當使用者離開activity)之前使用commit()送出事務。

如果你試圖在那個點之後送出,會抛出一個異常。這是因為如果activity需要被恢複,送出之後的狀态可能會丢失。對于你覺得可以丢失送出的狀況,使用 commitAllowingStateLoss()。

與Activity通信 盡管Fragment被實作為一個獨立于Activity的對象,并且可以在多個activity中使用,但一個給定的fragment執行個體是直接綁定到包含它的activity的。特别的,fragment可以使用 getActivity() 通路Activity執行個體,并且容易地執行比如在activity layout中查找一個view的任務。

View listView = getActivity().findViewById(R.id.list);      

同樣地,activity可以通過從FragmentManager獲得一個到Fragment的引用來調用fragment中的方法,使用 findFragmentById() 或 findFragmentByTag()。

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);      

為Activity建立事件回調方法

在一些情況下,你可能需要fragment與activity分享事件。一個好的方法是在fragment中定義一個回調的interface,并要求宿主activity實作它。當activity通過interface接收到一個回調,必要時它可以和在layout中的其他fragment分享資訊。

例如,如果一個新的應用在activity中有2個fragment —— 一個用來顯示文章清單(framgent A),另一個顯示文章内容(fragment B) —— 然後 framgent A必須告訴activity何時一個list item被選中,然後它可以告訴fragment B去顯示文章。

在這個例子中,OnArticleSelectedListener 接口在fragment A中聲明:

1

public

static

class

FragmentA 

extends

ListFragment {

2

...

3

// Container Activity must implement this interface

4

public

interface

OnArticleSelectedListener {

5

public

void

onArticleSelected(Uri articleUri);

6

}

7

...

8

}

然後fragment的宿主activity實作 OnArticleSelectedListener 接口,并覆寫 onArticleSelected() 來通知fragment B,從fragment A傳來了事件。為了確定宿主activity實作這個接口,fragment A的 onAttach() 回調方法(當添加fragment到activity時由系統調用)通過将作為參數傳入onAttach()的Activity做類型轉換來執行個體化一個OnArticleSelectedListener執行個體。

01

public

static

class

FragmentA 

extends

ListFragment {

02

OnArticleSelectedListener mListener;

03

...

04

@Override

05

public

void

onAttach(Activity activity) {

06

super

.onAttach(activity);

07

try

{

08

mListener = (OnArticleSelectedListener) activity;

09

catch

(ClassCastException e) {

10

throw

new

ClassCastException(activity.toString() + 

" must implement OnArticleSelectedListener"

);

11

}

12

}

13

...

14

}

如果activity沒有實作接口,fragment會抛出 ClassCastException 異常。正常情形下,mListener成員會保持一個到activity的OnArticleSelectedListener實作的引用,是以fragment A可以通過調用在OnArticleSelectedListener接口中定義的方法分享事件給activity。例如,如果fragment A是一個 ListFragment的子類,每次使用者點選一個清單項,系統調用 在fragment中的onListItemClick(),然後後者調用 onArticleSelected() 來配置設定事件給activity。

01

public

static

class

FragmentA 

extends

ListFragment {

02

OnArticleSelectedListener mListener;

03

...

04

@Override

05

public

void

onListItemClick(ListView l, View v, 

int

position, 

long

id) {

06

// Append the clicked item's row ID with the content provider Uri

07

Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);

08

// Send the event and Uri to the host activity

09

mListener.onArticleSelected(noteUri);

10

}

11

...

12

}

傳給 onListItemClick() 的 id 參數是被點選的項的行ID,activity(或其他fragment)用來從應用的 ContentProvider 擷取文章。

添加項目到Action Bar

你的fragment可以通過實作 onCreateOptionMenu() 提供菜單項給activity的選項菜單(以此類推, Action Bar也一樣)。為了使這個方法接收調用,無論如何,你必須在 onCreate() 期間調用 setHasOptionsMenu() 來指出fragment願意添加item到選項菜單(否則, fragment将接收不到對 onCreateOptionsMenu()的調用)。

随後從fragment添加到Option菜單的任何項,都會被追加到現有菜單項的後面。當一個菜單項被選擇,fragment也會接收到 對 onOptionsItemSelected() 的回調。也可以在你的fragment layout中通過調用 registerForContextMenu() 注冊一個view來提供一個環境菜單。當使用者打開環境菜單,fragment接收到一個對 onCreateContextMenu() 的調用。當使用者選擇一個項目,fragment接收到一個對onContextItemSelected() 的調用。

注意:盡管你的fragment會接收到它所添加的每一個菜單項被選擇後的回調,但實際上當使用者選擇一個菜單項時,activity會首先接收到對應的回調。如果activity的on-item-selected回調函數實作并沒有處理被選中的項目。然後事件才會被傳遞到fragment的回調。

這個規則适用于選項菜單和環境菜單。

處理Fragment生命周期

Android:Fragment官方文檔

管理fragment的生命周期,大多數地方和管理activity生命周期很像。和activity一樣,fragment可以處于3種狀态:

  • Resumed

    在運作中的activity中fragment可見。

  • Paused

    另一個activity處于前台并擁有焦點,但是這個fragment所在的activity仍然可見(前台activity局部透明或者沒有覆寫整個螢幕)。

  • Stopped

    要麼是宿主activity已經被停止,要麼是fragment從activity被移除但被添加到背景堆棧中。

    停止狀态的fragment仍然活着(所有狀态和成員資訊被系統保持着)。然而,它對使用者不再可見,并且如果activity被殺死,它也會被殺死。

仍然和activity一樣,可以使用Bundle保持fragment的狀态,萬一activity的程序被殺死,并且當activity被重新建立的時候,需要恢複fragment的狀态時就可以用到。可以在fragment的 onSaveInstanceState() 期間儲存狀态,并可以在 onCreate()、onCreateView() 或 onActivityCreated() 期間恢複它。

生命周期方面,activity和fragment之間最重要的差別是各自如何在它的背景堆棧中儲存。預設地,activity在停止後,它會被放到一個由系統管理的用于儲存activity的背景堆棧。(是以使用者可以使用BACK按鍵導航回退到它)。

然而,當你在一個事務期間移除fragment時,隻有顯式調用addToBackStack()請求儲存執行個體,才會被放到一個由宿主activity管理的背景堆棧。

另外,管理fragment的生命周期和管理activity生命周期非常類似。是以,《管理activity生命周期》一章中的相同實踐也同樣适用于fragment。你需要了解的是,activity的生命期如何影響fragment的生命期.

與activity生命周期的協調工作

fragment所生存的activity的生命周期,直接影響fragment的生命周期,每一個activity的生命周期的回調行為都會引起每一個fragment中類似的回調。

例如,當activity接收到onPause()時,activity中的每一個fragment都會接收到onPause()。

為了執行例如建立和銷毀fragment的UI的動作,Fragment有一些額外的生命周期回調方法,它們是處理與activity互動的唯一管道。這些額外的回調方法是:

  • onAttach()

    當fragment被綁定到activity時被調用(Activity會被傳入)。

  • onCreateView()

    建立和fragment關聯的view hierarchy時被調用。

  • onActivityCreated()

    當activity的onCreate()方法傳回時被調用。

  • onDestroyView()

    當和fragment關聯的view hierarchy被移除時調用。

  • onDetach()

    當fragment從activity解除關聯時被調用。

fragment生命周期的流程,以及宿主activity對它的影響,在圖3中顯示。在這個圖中,可以看到activity依次的每個狀态是如何決定fragment可能接收到的回調方法。例如,當activity接收到它的onCreate(),activity中的fragment接收到最多是onActivityCreated()。

一旦activity到達了resumed狀态,你可以自由地在activity添加和移除fragment。是以,僅當activity處于resumed狀态時,fragment的生命周期才可以獨立變化。 無論如何,當activity離開resumed狀态,fragment再次被activity帶入其生命周期過程。