Android 碎片 Fragment

Android Fragment 解決手機上不一樣分辨率UI差別變化的問題

標籤: android 教程 中文文檔java


一個Fragment看起來就是一個和Activity同樣的用戶界面。你能夠結合多個Fragments到一個activity中,來構建一個有多方面功能的UI,還能夠重用同一個Fragment在多個activities中。你能夠把它當成是activity的一個組件,每一個Fragment有單獨的生命週期,你能夠在activity運行時進行進行添加和移除Fragment。相比較於activity,Fragment更加輕量級,更加靈活。android

一個Fragment老是被植入在一個activity中,而且其生命週期受其父activity直接影響,好比activity處於暫停,則其中的Fragment都暫停,activity銷燬,則全部Fragment都銷燬。然而,當一個activity運行時,你能夠獨立的操做每個Fragment,好比添加和刪除他們。當你進行相似的操做時,你能夠將Fragment添加入被activity管理的後退棧中,這樣用戶能夠經過點擊返回按鈕來返回以前打開的Fragment。ide

你能夠將一個Fragment做爲activity的一部分添加到其佈局文件中,經過聲明<fragment>元素做爲ViewGroup的一部分。固然,這不是必須的,你也能夠將Fragment做爲一個沒有本身的UI的不可見的activity的工人。函數

設計原理

Android在Android3.0(API 11)上介紹了fragmens,主要是支持在大屏幕上更動態和靈活的UI設計,好比平板。佈局

平板上兩個模塊定義在一個activity中,而手機是分離的

建立一個Fragment

fragment的生命週期

爲了建立一個Fragment,你必須建立一個Fragment的子類,Fragment編碼就像是一個Activity。包含一些相同的回調函數,好比onCreate(),onStart(),onPause(),和onStop()。事實上,若是你覆蓋一個已有的Android應用來使用Fragment,你只用簡單的移動對應的代碼到對應的回調函數中。動畫

一般狀況下,你應該至少實現如下的生命週期函數:
onCreate()
系統在建立fragment時調用。
你應該在這裏初始化你想要在暫停或者中止恢復時保持的fragment部分。
onCreateView()
系統在fragment第一次構造用戶界面時調用。
爲了給你的fragment構造一個UI,你必定要記得return一個View,這個View是來自你的fragment的根佈局,你也能夠return null,若是這個fragment沒有UI。
onPause()
系統會在用戶產生離開這個fragment的跡象時調用。
這裏一般是你提交變化的地方。由於用戶或許不會回來。this

大多數的應用都應該爲至少每個fragment實現這三個方法,但還有其餘的回調函數,你也應該用他們來處理fragment的各個階段。編碼

這裏還有一些fragment的子類:
DialogFragment
顯示一個懸浮的對話框。好處是你能將其加入後退棧中,容許用戶返回。
ListFragment
顯示一個經過adapter管理的列表,相似ListActivity,它提供許多方法管理一個列表view。
PreferenceFragment
在一個列表中顯示一個分級Preference對象,相似PreferenceActivity。當你爲你的應用建立「設置」activity時頗有用。spa

添加用戶界面

一個fragment常做爲activity用戶界面的一部分。
要使用一個fragment的佈局文件,你必須實現onCreateView()回調函數,return根佈局。.net

若是你的fragment是ListFragment的子類,它會在onCreateView()中自動返回一個ListView。你就不用去實現了。

爲了返回一個佈局,能夠從一個定義在xml中的佈局資源中載入來提供幫助。onCreateView()提供一個LayoutInflater對象

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 載入佈局資源
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

container參數是存放fragment的佈局的ViewGroup對象,savedInstanceState參數是一個Bundle,跟activity的onCreate()中Bundle差很少,用於狀態恢復。可是fragment的onCreate()中也有Bundle參數,因此此處的Bundle中存放的數據與onCreate()中存放的數據仍是不一樣的。至於詳細信息,請參考「操控fragment的生命週期「。

Inflate()方法有三個參數:
1.layout的資源ID。
2.存放fragment的layout的ViewGroup。
3.布爾型數據表示是否在建立fragment的layout期間,把layout附加到container上(在這個例子中, 指定了false, 由於系統已經把展開的layout插入到container –傳入true會在最後的layout中建立一個多餘的view group.)

將fragment添加到activity

通常狀況下,fragment把它的layout做爲activitiy的layout的一部分合併到activity中,有兩種方法將一個fragment添加到activity中:

  • 在activity的layoutxml文件中聲明fragment:

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

<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="horizontal"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
   <fragmentandroid:name="com.example.news.ArticleListFragment"
           android:id="@+id/list"
           android:layout_weight="1"
           android:layout_width="0dp"
          android:layout_height="match_parent"/>
   <fragmentandroid:name="com.example.news.ArticleReaderFragment"
           android:id="@+id/viewer"
           android:layout_weight="2"
           android:layout_width="0dp"
          android:layout_height="match_parent"/>
</LinearLayout
  • 在代碼中添加fragment到一個ViewGroup:
    <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.

ExampleFragment fragment = newExampleFragment();  
fragmentTransaction.add(R.id.fragment_container,fragment);  
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實例 : 

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

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

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

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

// Create new fragment and transaction  
Fragment newFragment = newExampleFragment();  
FragmentTransaction transaction =getFragmentManager().beginTransaction();  
// Replace whatever is in thefragment_container view with this fragment,  
// and add the transaction to the backstack  
transaction.replace(R.id.fragment_container,newFragment);  
transaction.addToBackStack(null);  
// Commit the transaction  
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的任務.

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

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

public static class FragmentA extends 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 extends ListFragment {  
    OnArticleSelectedListener mListener;  
    ...  
    @Override  
    public void onAttach(Activity activity) {  
        super.onAttach(activity);  
        try {  
            mListener = (OnArticleSelectedListener) activity;  
         } catch (ClassCastException e) {  
            throw new ClassCastException(activity.toString() + " must implementOnArticleSelectedListener");  
        }  
    }  
   
    ...  
   
}

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

public static class FragmentA extends ListFragment {  
    OnArticleSelectedListener mListener;  
    ...  
    @Override  
    public 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 獲取文章.

添加項目到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和fragment生命週期關係

和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對它的影響,在圖activity_fragment_lifecycle中顯示.在這個圖中,能夠看到activity依次的每一個狀態是如何決定fragment可能接收到的回調方法.
例如, 當activity接收到它的onCreate(),activity中的fragment接收到最可能是onActivityCreated().
一旦activity到達了resumed狀態, 你能夠自由地在activity添加和移除fragment.所以,僅當activity處於resumed狀態時, fragment的生命週期才能夠獨立變化.
不管如何, 當activity離開resumed狀態,fragment再次被activity的推入它本身的生命週期過程.

以上部分參考傲慢的上校的博客

例子

(未完待續)

相關文章
相關標籤/搜索