android之Fragment(官網資料翻譯)

官網html

目錄(?)[-]java

  1. Fragment要點android

  2. 設計哲學        設計模式

  3. 建立Fragmentide

    1. 添加一個用戶界面   模塊化

    2. 將fragment添加到activity函數

    3. 撰寫代碼將fragment添加到一個已存在的ViewGroup工具

    4. 添加一個無UI的fragment佈局

  4. 管理Fragment動畫

  5. 處理Fragment事務

  6. 與Activity通訊

    1. 爲Activity建立事件回調方法

    2. 添加項目到ActionBar

  7. 處理fragment的生命週期

    1. 與activity生命週期的協調工做

Fragment要點

  1. Fragment做爲Activity界面的一部分組成出現

  2. 能夠在一個Activity中同時出現多個Fragment,而且,一個Fragment亦可在多個Activity中使用。

  3. 在Activity運行過程當中,能夠添加、移除或者替換Fragment(add()、remove()、replace())

  4. Fragment能夠響應本身的輸入事件,而且有本身的生命週期,固然,它們的生命週期直接被其所屬的宿主activity的生命週期影響。


設計哲學        

        Android在3.0中引入了fragments的概念,主要目的是用在大屏幕設備上--例如平板電腦上,支持更加動態和靈活的UI設計。平板電腦的屏幕要比手機的大得多,有更多的空間來放更多的UI組件,而且這些組件之間會產生更多的交互。Fragment容許這樣的一種設計,而不須要你親自來管理 viewhierarchy的複雜變化。 經過將activity的佈局分散到fragment中, 你能夠在運行時修改activity的外觀,並在由activity管理的back stack中保存那些變化.(http://developer.android.com/guide/topics/fundamentals/fragments.html  

         例如, 一個新聞應用能夠在屏幕左側使用一個fragment來展現一個文章的列表,而後在屏幕右側使用另外一個fragment來展現一篇文章--2個fragment並排顯示在相同的一個activity中,而且每個fragment擁有它本身的一套生命週期回調方法,而且處理它們本身的用戶輸入事件。 所以, 取代使用一個activity來選擇一篇文章而另外一個activity來閱讀文章的方式,用戶能夠在同一個activity中選擇一篇文章而且閱讀, 如圖所示:


     fragment在你的應用中應當是一個模塊化和可重用的組件.即,由於fragment定義了它本身的佈局, 以及經過使用它本身的生命週期回調方法定義了它本身的行爲,你能夠將fragment包含到多個activity中. 這點特別重要, 由於這容許你將你的用戶體驗適配到不一樣的屏幕尺寸.舉個例子,你可能會僅當在屏幕尺寸足夠大時,在一個activity中包含多個fragment,而且,當不屬於這種狀況時,會啓動另外一個單獨的,使用不一樣fragment的activity.

     繼續以前那個新聞的例子 -- 當運行在一個特別大的屏幕時(例如平板電腦),應用能夠在Activity A中嵌入2個fragment。然而,在一個正常尺寸的屏幕(例如手機)上,沒有足夠的空間同時供2個fragment用, 所以, Activity A會僅包含文章列表的fragment, 而當用戶選擇一篇文章時, 它會啓動ActivityB,它包含閱讀文章的fragment.所以, 應用能夠同時支持上圖中的2種設計模式。


建立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生命週期的各類階段.所有的生命週期回調方法將會在後面章節 Handlingthe Fragment Lifecycle 中討論.

      除了繼承基類 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, 也能夠從一個layout的xml資源文件中讀取並生成. 爲了幫助你這麼作, onCreateView() 提供了一個LayoutInflater 對象.

      舉個例子, 這裏有一個Fragment的子類, 從文件 example_fragment.xml 加載了一個layout:

[java] view plaincopy

  1. public static class ExampleFragment extends Fragment {  

  2.     @Override  

  3.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  

  4.                              Bundle savedInstanceState) {  

  5.         // Inflate the layout for this fragment  

  6.         return inflater.inflate(R.layout.example_fragment, container, false);  

  7.     }  

  8. }  


         傳入onCreateView()的container參數是你的fragmentlayout將被插入的父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的整個viewhierarchy的一部分被嵌入. 有2種方法你能夠添加一個fragment到activity layout:

在activity的layout文件中聲明fragment
       在這種狀況下,你能夠像爲View同樣, 爲fragment指定layout屬性.例子是一個有2個fragment的activity的layout:

 

[html] view plaincopy

  1. <?xml version="1.0" encoding="utf-8"?>  

  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  

  3.     android:orientation="horizontal"  

  4.     android:layout_width="match_parent"  

  5.     android:layout_height="match_parent">  

  6.     <fragment android:name="com.example.news.ArticleListFragment"  

  7.             android:id="@+id/list"  

  8.             android:layout_weight="1"  

  9.             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 的實例:

[java] view plaincopy

  1. FragmentManager fragmentManager =getFragmentManager();  

  2. FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();  

       而後你可使用 add() 方法添加一個fragment, 指定要添加的fragment和要插入的view.

[java] view plaincopy

  1. ExampleFragment fragment = newExampleFragment();  

  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

  • 將fragment從後臺堆棧中彈出, 使用 popBackStack() (模擬用戶按下BACK 命令).

  • 使用addOnBackStackChangeListener()註冊一個監聽後臺堆棧變化的listener.


處理Fragment事務

   

        關於在activity中使用fragment的很強的一個特性是:根據用戶的交互狀況,對fragment進行添加,移除,替換,以及執行其餘動做.提交給activity的每一套變化被稱爲一個事務,可使用在FragmentTransaction中的 API 處理.咱們也能夠保存每個事務到一個activity管理的backstack,容許用戶經由fragment的變化往回導航(相似於經過 activity日後導航).

      從 FragmentManager 得到一個FragmentTransaction實例 : 

[java] view plaincopy

  1. FragmentManager fragmentManager =getFragmentManager();  

  2. FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();  

         每個事務都是同時要執行的一套變化.能夠在一個給定的事務中設置你想執行的全部變化,使用諸如 add()、remove()和 replace().而後, 要給activity應用事務, 必須調用 commit().

  在調用commit()以前, 你可能想調用 addToBackStack(),將事務添加到一個fragment事務的backstack. 這個back stack由activity管理, 並容許用戶經過按下 BACK按鍵返回到前一個fragment狀態.

       舉個例子, 這裏是如何將一個fragment替換爲另外一個, 並在後臺堆棧中保留以前的狀態:

[java] view plaincopy

  1. // Create new fragment and transaction  

  2. Fragment newFragment = newExampleFragment();  

  3. FragmentTransaction transaction =getFragmentManager().beginTransaction();  

  4. // Replace whatever is in thefragment_container view with this fragment,  

  5. // and add the transaction to the backstack  

  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()提交的事務. 但這麼作一般沒必要要,除非事務是其餘線程中的任務的一個從屬.

     警告:你只能在activity保存它的狀態(當用戶離開activity)以前使用commit()提交事務.

與Activity通訊

         儘管Fragment被實現爲一個獨立於Activity的對象,而且能夠在多個activity中使用,但一個給定的fragment實例是直接綁定到包含它的activity的. 特別的,fragment可使用 getActivity() 訪問Activity實例, 而且容易地執行好比在activity layout中查找一個view的任務.

[java] view plaincopy

  1. View listView =getActivity().findViewById(R.id.list);<span style="font-family:System;"> </span>  

          一樣地,activity能夠經過從FragmentManager得到一個到Fragment的引用來調用fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().

 

[java] view plaincopy

  1. 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被選中,而後它能夠告訴fragmentB去顯示文章.
    在這個例子中, OnArticleSelectedListener 接口在fragment A中聲明:

 

[java] view plaincopy

  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.     ...  

  9. }  

           而後fragment的宿主activity實現 OnArticleSelectedListener 接口, 並覆寫 onArticleSelected() 來通知fragment B,從fragment A到來的事件.爲了確保宿主activity實現這個接口, fragment A的 onAttach() 回調方法(當添加fragment到activity時由系統調用) 經過將做爲參數傳入onAttach()的Activity作類型轉換來實例化一個OnArticleSelectedListener實例.

 

[java] view plaincopy

  1. public static class FragmentA extends ListFragment {  

  2.     OnArticleSelectedListener mListener;  

  3.     ...  

  4.     @Override  

  5.     public void onAttach(Activity activity) {  

  6.         super.onAttach(activity);  

  7.         try {  

  8.             mListener = (OnArticleSelectedListener) activity;  

  9.          } catch (ClassCastException e) {  

  10.             throw new ClassCastException(activity.toString() + " must implementOnArticleSelectedListener");  

  11.         }  

  12.     }  

  13.    

  14.     ...  

  15.    

  16. }  


          若是activity沒有實現接口, fragment會拋出 ClassCastException 異常. 正常情形下,mListener成員會保持一個到activity的OnArticleSelectedListener實現的引用, 所以fragment A能夠經過調用在OnArticleSelectedListener接口中定義的方法分享事件給activity.例如, 若是fragment A是一個 ListFragment的子類, 每次用戶點擊一個列表項, 系統調用在fragment中的onListItemClick(),而後後者調用 onArticleSelected() 來分配事件給activity.


[java] view plaincopy

  1. public static class FragmentA extends ListFragment {  

  2.     OnArticleSelectedListener mListener;  

  3.     ...  

  4.     @Override  

  5.     public void onListItemClick(ListView l, View v, int position, long id) {  

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

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

  8.         // Send the event and Uri to the host activity  

  9.        mListener.onArticleSelected(noteUri);  

  10.     }  

  11.    

  12.     ...  

  13.    

  14. }  

    傳給 onListItemClick() 的 id 參數是被點擊的項的行ID, activity(或其餘fragment)用來從應用的 ContentProvider 獲取文章.

 

添加項目到ActionBar

          你的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的生命週期


   管理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生命週期很是相似.所以, "managing the activitylifecycle"中的相同實踐也一樣適用於fragment. 你須要理解的是,  activity的生命如何影響fragment的生命.

與activity生命週期的協調工做

       fragment所生存的activity的生命週期,直接影響fragment的生命週期,每個activity的生命週期的回調行爲都會引發每個fragment中相似的回調.
       例如,當activity接收到onPause()時,activity中的每個fragment都會接收到onPause().
       Fragment 有一些額外的生命週期回調方法, 那些是處理與activity的惟一的交互,爲了執行例如建立和銷燬fragment的UI的動做. 這些額外的回調方法是:

  • 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的推入它本身的生命週期過程.

    (關於Example,後續)

相關文章
相關標籤/搜索