[转]使用fragments
原帖:http://wiki.eoe.cn/page/Fragments.html
一個**碎片**在一個活動中代表一個行為或用戶界面的一部分。 你可以在一個單一的活動中組合使用多個碎片以建立一個多窗格的UI,并且可以在多個活動中重用一個碎片。你可以認為是一個擁有獨立生命周期、能夠獨立接受輸入事件、并且可以在活動運行時添加或移除的碎片作為一個活動的模塊化部分(有點像一個你可以在不同活動中重用的子活動)。
在一個Activity中,一個Fragment代表一個用戶接口的行為或者部分行為.你可以把幾個Fragment混合到一個Activity中,這樣你就可以創建一個多個頁面的UI并可以在多個Activity中復用一個Fragment.
一個碎片必須總是嵌入到一個活動(activity)中,并且它的生命周期直接受到住活動得生命周期的影響。例如:當活動暫停或銷毀時時,它里邊的所有碎片也是如此。然而,當一個活動運行時,(它在 resumed(重新開始)**生命周期狀態**,你可以單獨的操作每個碎片,例如添加或移除它們。當你執行了這樣的碎片事務,你也可以將它添加到一個后臺堆棧所管理的活動——該活動中的每個后臺堆棧條目都記錄著已發生的片段事務。后臺堆棧允許用戶通過按返回按鈕退一個碎片事務(向后導航)。
當你增加一個Fragment來作為Activity界面布局的一部分時,他存在在Activity視圖層里面的一個ViewGroup中,這個Fragment定義了自己的視圖.你可以通過在Activity的布局文件中用聲明這個fragment來把這個fragment插入你的activity中,或者你可以在應用的代碼中把他添加添加到一個現存的ViewGroup中.然而,Fragment不必是一個activity布局的一部分,你可以使用一個Fragment而不實用他的UI,這樣可以讓Fragment作為Activity的一個不可見部分來工作.
這個文檔介紹了增加使用Fragment創建你的應用,包括怎樣使fragment在增加到返回棧的時候保持他們的狀態,和activity以及該activity的其他fragment的共享事件,組成Activity的ActionBar,等等.
Design Philosophy-設計理念?
Android在Android3.0(API等級11)中引入了Fragment,主要是為了在大屏幕上(比如平板)支持更多動態的靈活的UI設計.因為平板的屏幕比其他手持設備大多了,有更多的空間來組合,交換UI組件.Fragment使你在View層不必進行很復雜變化就可以就可以實現這些設計.通過把Activity的布局分解成很多Fragment,你可以在運行時改動activity的界面并且可以把這些變化保存在activity管理的返回棧中.
比如,一個新聞應用可以使用一個Fragment在左邊顯示一列文章標題而在左邊的另一個Fragment顯示文章詳細內容.這兩個Fragment都在同一個Activity中,他們并排著,每個Fragment有他自己的生命周期回調方法,處理他們各自的輸入事件.那么,不需要在一個activity中選擇在另一個activity中閱讀,用戶可以選擇一篇文章在同一個activity中閱讀這個新聞的內容.如圖1所示:
你應該把fragment設計成模塊化的,可復用的Activity組件.就是說,每個Fragment定義了他自己的布局和他自己的,擁有自己生命周期回調的行為,你可以在多個activity中包含一個Fragment,所以你應該把Fragment設計成可以復用的,并且需要避免從一個fragment直接操縱另一個fragment.這一點是非常重要的因為一個fragment模塊允許你針對不同的屏幕尺寸變化你的fragment組合形式.在設計應用來支持平板和手持設備時,你可以在不同的布局配置中重用你的fragment來針對屏幕空間優化用戶體驗.比如,在一個手持設備上,可能需要分開的fragment來提供一個單獨窗口UI而不是使很多fragment在同一個activity中放不開.
圖例1 平板上,在一個activity中Fragment怎么定義兩個UI模塊,但是在手持設備上他們將分開.
繼續以上面的新聞為例,當在平板大小的設備上運行的時候,這個應用可以在Activity中嵌入兩個Fragment.然而,在手持設備上的時候,由于沒有足夠的空間盛放這兩個fragment,所以ActivityA只顯示了其中的一個Fragment(新聞列表),當用戶選擇新聞標題的時候,他跳轉到ActvityB,ActivityB中顯示第二個Fragment(新聞詳細信息).那么通過復用不同組合的Fragment這個應用就可以同時支持平板和手持設備了,如圖1.
更多關于使用不同的Fragment組合來設計適應不同屏幕應用的信息,請參閱Supporting Tablets and Handsets一章.
Creating a Fragment-創建一個Fragment
創建一個Fragment,你應該創建一個Fragment的子類(或者他的一個現有子類).Fragment類的代碼很像Activity.它還有和activity相似的回調方法,比如onCreate(), onStart(), onPause(), 和 onStop().實際上,如果你在使用Fragment來轉換一個現成的應用,你可能只是簡單的從你的activity回調方法中移動代碼到fragment相應的回調方法中.
一般的,你至少應該實現下面的生命周期方法:
-
onCreate()
-
- 創建fragment的時候,系統會調用這個方法.在你實現過程中,當fragment暫停(pause),停止(stop)然后恢復(resume)時,你應該初始化你想要保持的,fragment的必要的組件.
-
onCreateView()
-
- 在fragment第一次繪制他的用戶界面的時候系統會調用這個方法.如果你想為你的fragment繪制界面,你必須從這個方法中返回一個View,這個View是你fragment布局的基礎.如果這個Fragment不提供UI,你可以返回空.
-
onPause()
-
- 系統調用這個方法作為用戶離開這個fragment的第一標志(雖然這不總是意味著這個Fragment被摧毀了).通常是你需要做一些改變,這些改變超出了當前的用戶會話(因為用戶有可能不會回到這個界面來).
大多數應用應該至少為每個fragment實現上述的三個方法,但還有些回調函數你需要去實現,用來處理fragment生命周期中的不同狀態。所有有關fragment生命周期的回調函數稍后將會在章節Handling the Fragment Lifecycle中討論
也有有些子類(不是基本的Fragment類)你可能想要繼承來實現Fragment:
-
DialogFragment
-
- 顯示一個浮動的對話框.使用這個類來創建一個對話框是和使用對話Helper方法在Activity類中創建對話框都是很好的方法,因為你可以把Fragment對話框包含在activity管理的Fragment返回棧中,允許用戶返回到關閉的Fragment中.
-
ListFragment
-
- 展示一列被adapter(比如SimpleCursorAdapter)管理的項,和ListActivity很相似.它提供了一些管理一個列表視圖的方法,比如處理點擊事件的onListItemClick()方法.
-
PreferenceFragment
-
- 用一個列表來顯示一組偏好設置對象,類似于PreferenceActivity. 在創建設置型的activity時會用到.
圖2 Fragment的生命周期(Activity 正在運行時).
Adding a user interface-添加一個用戶接口
一個Fragment經常被用作activity界面的一部分,為activity貢獻自己的界面.
為了給fragment提供一個布局,你必須實現onCreateView()方法,Android系統在Fragment繪制他的界面的時候調用這個方法.你對這個方法的實現必須返回一個View,這個View是你Fragment布局的基礎.
注意:如果你的Fragment是一個ListFragment類的子類,默認會從onCreateView()返回一個Listview,所以你不需要實現它.
?
為了從onCreateView()方法返回一個布局,你可以用一個xml布局文件來填充它.為了幫助你做這個事情,onCreateView() 方法提供了一個LayoutInflater對象.
比如,這個一個Fragment的子類,他是從example_fragment.xml文件載入的布局:
| 1 2 3 4 5 6 7 8 | public static class ExampleFragment extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.example_fragment, container, false);} } |
Creating a layout-創建一個布局
和上面差不多,R.layout.example_fragment是在系統保存的example_fragment.xml這個布局資源的引用.更多關于使用一個xml文件創建一個布局的信息,參考User Interface 文檔.
傳遞給onCreateView()的容器參數是fragment鎖插入的activity的父ViewGroup(來自對應的activity布局).savedInstanceState的參數是一個提供關于之前Fragment狀態數據的Bundle,如果這個Fragment被恢復了(resume,恢復數據在處理Fragment生命周期這一節有更多介紹)
inflate()方法接收三個參數:
-
- 你想要添加的layout的資源ID.
-
- 將作為填充布局的父容器的ViewGroup.傳遞容器參數是非常重要的,只用這樣才能使系統應用布局參數到填充視圖的根視圖,從而被它的父視圖所確定.
-
- 一個boolean類型的參數,用于在填充時指明填充的布局是否應該附加在ViewGroup(第二個參數)上.(如果系統已經插入這個填充布局到容器了就返回false,如果將要在最終布局中創建一個多余的viewgroup,那就返回true)
現在你已經知道如何創建一個有布局的fragment,接下來,你需要將fragment加到你的activity中
Adding a fragment to an activity-給一個Activity添加一個Fragment
一般的一個fragment提供了Activity UI的一部分,他作為Activity全局視圖層的一部分而嵌入.有兩種方法可以把fragment嵌入到Activity布局中:
- * Declare the fragment inside the activity's layout file-在Activity布局文件中聲明Fragment.*
這樣的話,你可以把Fragment當作一個視圖,比如,這是一個嵌入兩個Fragmet的Activity:
| 123456789 10 11 12 13 14 15 16 | <?xml version"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> |
的android:name屬性指定了在布局中要實例化的Fragment類.
在系統創建這個Activity布局的時候,他會實例化每個布局中的每個fragment,調用每個fragment的onCreateView()方法來取回每個fragment的視圖.系統把fragment返回的視圖直接插入到標簽所在的地方.
注意.每個fragment需要一個獨一無二的id以便系統可以在activity restartt時恢復fragment(你也可以用它來進行fragment的事務處理,例如將它刪除)。有三種方法為一個fragment提供id
-
使用android:id來指定它唯一的ID.
-
使用android:tag來指定一個唯一的字符串標志
-
如果上面兩個你都不指定,系統會使用容器視圖的ID.
-
或者,機械性的把fragment添加到ViewGroup中.
在任何你Activity運行的時候,你都可以把fragment添加到Activity的視圖中.你只需要指定一個用于盛放Fragment的ViewGroup.為了讓fragment可以被管理(比如添加,刪除,替換fragment),你必須使用來自FragmentTransaction的API.你可以像下面這樣在Activity中獲取一個FragmentTransaction的實例:
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
你可以使用Add()方法添加一個Fragment,指定要添加的Fragment和目標View,如下:
| 1 2 3 | ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit(); |
add()方法中的第一個參數是Fragment所要放置的目標ViewGroup,通過資源ID指定,第二個參數是要添加的Fragment.只要你使用FragmentTransaction做了修改,你必須調用commit()方法來使修改生效.
Adding a fragment without a UI-添加一個沒有UI的Fragment
上面的例子想你展示了怎么添加一個含有UI的Fragment到你的Actvity.然而,對于不想增加而外UI的Activity來說,你也可以使用Fragment來進行后臺行為.
為了添加一個沒有UI的Fragment.需要使用add(Fragment, String) 方法,其中,你需要為Fragment提供一個字符串的標志而不是一個視圖ID.這樣增加的Fragment,由于沒有涉及到Activity的視圖,所以不會調用onCreateView()方法.所以你不需要實現這個方法.
為Fragment提供一個字符串標志不一定只局限于沒有UI的Fragment,你也可以為有UI的Fragment指定一個字符串標志,但是如果這個Fragment真的沒有UI,那這個字符串標志是確定它的唯一標志.如果你想在后面從Activity中獲取到這個fragment,你需要使用findFragmentByTag()方法.
在FragmentRetainInstance.java文件的例子展示了Actvity怎么使用一個沒有UI的fragment來出來后臺工作.
Managing Fragments-管理fragment
為了管理你Activity中的fragment,你需要使用FragmentManager.你可以通過你Activity中的getFragmentManager()來獲取它.
使用FragmentManager你可以:
-
使用findFragmentById()(提供UI的Fragment)或者findFragmentByTag()(沒有提供UI的Fragment) 獲取你Activity存在的Fragment,
-
使用popBackStack()把Fragment從返回棧中彈出(模擬用戶的返回命令).
-
使用addOnBackStackChangedListener()方法為返回棧的變化注冊監聽器.
請參考文檔的 FragmentManager 類來查看過于這些方法(還有其他方法)的更多內容.
正如前面的文檔所講的,你也可以使用FragmentManager來打開FragmentTransaction,FragmentTransaction允許你執行添加,刪除Fragment的事務.
Performing Fragment Transactions-執行Fragment事務
在你的Activity中使用Fragment的最大好處就是可以針對用戶的操作,進行對Fragment的添加,移除,替換等等其他操作.你提交給Activity的每個變化稱為一個事務,這些事務你可以使用FragmenTransaction的API來實現.你也可以在Activity管理的返回棧中保存每個事務,使用戶可以在Fragmen的變化后返回之前的狀態(類似于在Activity跳轉后的返回).
你可以像這樣從FragmentManager中取得一個FragmentTransaction的實例:
| 1 2 | FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); |
每個事務是一系列你想要同時執行的Fragmen的變化.你可以使用像add(),remove(),replace()這樣的方法來為一個事務設定你想要執行的操作.為了使Activity的事務生效,你必須執行commit()方法.
在你調用commit()方法的之前,為了添加這個事務到一個Fragmen事務的返回棧,你可能想要調用addToBackStack()方法.這個返回棧被Activity管理,允許用戶通過按下返回按鍵返回之前的Fragmen狀態.
這里展示了怎么使用一個Fragmen替換另一個,然后在返回棧中返回到之前的狀態.
| 123456789 10 11 12 13 14 15 | // Create new fragment and transaction //創建一個新的Fragmen和事務 Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction();// Replace whatever is in the fragment_container view with this fragment, //使用這個Fragment替換在Fragmen容器中的Fragmet // and add the transaction to the back stack //添加這個事務到返回棧 transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null);// Commit the transaction //提交這個事務. transaction.commit(); |
在這個例子中,新的Fragmen替換了R.id.fragment_container ID指定的布局容器中當前存在的fragment(如果存在的話).通過調用addToBackStack()方法,替換事務被保存在了返回棧中,這樣用戶可以回退這個事務,通過按下返回鍵返回到以前的fragment.
如果你在事務中添加了多個變化(比如另一個add()方法或者remove()方法),然后調用了addToBackStack()方法,那在你調用commit()方法之前的所有變化都會作為單獨的事務被添加到返回棧中,返回鍵將會把他們全部回退.
除了下面這些,其他的情況和你在FragmentTransaction中添加的順序沒有關系
-
你不行在最后調用commit()方法
-
如果你在向同一個容器添加多個fragment,那么你添加的順序決定了他們在視圖層出現的順序.
如果在你執行一個移除所有fragment的事務的時候沒有調用addToBackStack()方法,那么這個fragment將會在事務提交后被摧毀,用戶不能再返回到之前的fragment.如果你在移除fragment的時候調用了addToBackStack()方法,那這個fragment會被停止,并可以在用戶按返回的時候恢復.
小貼士:對于每個fragment事務,你可以在提交之前通過調用setTransition()來應用一個fragment動畫.
調用commit()方法不能立即執行事務而是安排它運行在Activity的UI線程中("主"線程)---如果這線程可以這么做的話.如果需要,你可以在你UI線程中調用executePendingTransactions()方法來直接執行commit()方法提交的事務.這么多一般不必要除非事務依賴于其他線程的工作.
注意:你只可以在Activity保存他狀態之前(在用戶離開這個Actvity的時候)使用commit()方法來提交一個事務.如果你在這個時間點之后提交,系統會拋出一個異常.這是因為如果Activity需要恢復,在提交之后的狀態可能會丟失.對于允許丟失提交的情況,請使用commitAllowingStateLoss()方法.
Communicating with the Activity-與Activity的通訊
即使fragment是作為一個object實現的,獨立于Activity的并且可以在那多個Activity中使用,但是一個fragment實例還是和它所在的容器有直接的關系.
特別的,fragment可以通過getActivity()方法來訪問Activity實例并可以輕易的執行像在activity視圖中查找View的任務.
| 1 | View listView = getActivity().findViewById(R.id.list); |
Likewise, your activity can call methods in the fragment by acquiring a reference to the Fragment from FragmentManager, using findFragmentById() or findFragmentByTag(). For example:
同樣的,使用findFragmentById()或findFragmentByTag()通過從FragmentManager獲取一個對這個Fragment的引用,你的Activity可以調用fragment中的方法
| 1 | ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); |
Creating event callbacks to the activity-為Activity創建時間回調
在一些情況下,你可惜需要一個Fragment和Activity共享事件.一個好的方法是在Fragment中定義一個回調接口然后讓承載他的Activity實現它.當Activity通過接口接收到調用時,必要時他可以和視圖中的其他Fragment共享信息.
舉個例子,如果一個新的應用在一個Activity中有兩個Fragment,一個顯示一列文章標題(FragmentA),另一列顯示文章內容(FragmentB),那么在一列被選中的時候,FragmentA必須告訴Actvity那一列被選中了,這樣Actvity就可以告訴FragmentB顯示哪一篇文章.在這種情況下,OnArticleSelectedListener 接口會在FragmentA中聲明.
| 1 2 3 4 5 6 7 8 | public static class FragmentA extends ListFragment {...// Container Activity must implement this interfacepublic interface OnArticleSelectedListener {public void onArticleSelected(Uri articleUri);}... } |
然后承載Fragment的Activity實現OnArticleSelectedListener接口并重寫onArticleSelected()方法來通知FragmentB響應FragmentA的事件.為了保證這個Activity實現了這個接口,FragmentA的onAttach()方法(系統在添加Fragment到這個Activity的時候調用)通過把Activity參數傳遞到onAttach()方法傳遞實例化一個OnArticleSelectedListener實例.
| 123456789 10 11 12 13 14 | public static class FragmentA extends ListFragment {OnArticleSelectedListener mListener;...@Overridepublic void onAttach(Activity activity) {super.onAttach(activity);try {mListener = (OnArticleSelectedListener) activity;} catch (ClassCastException e) {throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");}}... } |
如果Activity沒有實現這個接口,那么Fragment會拋出ClassCastException異常.上面的成功例子中,mListener成員有一個Activity實現的OnArticleSelectedListener的引用.這樣FragmentA可以通過調用OnArticleSelectedListener接口定義的方法來共享事件.比如:如果FragmentA是listFragment的擴展,用戶每次點擊list的一項,系統會調用Fragment的onListItemClick()方法,然后調用onArticleSelected() 方法來和Activity分享事件信息.
| 123456789 10 11 12 | public static class FragmentA extends ListFragment {OnArticleSelectedListener mListener;...@Overridepublic void onListItemClick(ListView l, View v, int position, long id) {// Append the clicked item's row ID with the content provider UriUri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);// Send the event and Uri to the host activitymListener.onArticleSelected(noteUri);}... } |
onListItemClick()方法傳遞的參數是點擊項的行ID,Activity(或Fragment)可以用它來從應用的ContentProvider填充文章信息.
更多關于使用content provider的信息請參閱Content Providers文檔.
Adding items to the Action Bar-在Action Bar上添加項
你的Fragment可以通過實現onCreateOptionsMenu()來為Activity的Options Menu創建菜單項(結果就是形成ActionBar).為了讓這個方法接收到調用,你必須在onCreate()方法中調用setHasOptionsMenu()方法來表明這個Fragment允許在Options Menu中增加項(否則,Fragment將不能接收onCreateOptionsMenu()的調用).
你從Fragment 添加到 Options Menu的任何項都是現存菜單項的附加項.在一個菜單項選中的時候,Fragment也接收響應onOptionsItemSelected()方法的調用.
你也可以在你的Fragment視圖中通過調用registerForContextMenu()方法來注冊一個視圖,從而提供一個上下文菜單.當用戶打開上下文菜單時,Fragment會接收一個onCreateContextMenu()的調用,當用戶選擇一項的時候,Fragment接收一個onContextItemSelected()的調用.
注意.即使你的Fragment在每個添加的菜單項接收了一個on-item-selected調用,在用戶選擇一個菜單項的時候,Activity是第一個接受各自調用的組件.如果Activity實現的on-item-selected調用沒有處理選擇項后的事件,那這個事件會傳遞到Fragment的回調中.這對 Options Menu 和上下文菜單都是適用的.
更多關于菜單的信息,參考Menus and Action Bar一文.
Handling the Fragment Lifecycle-處理Fragment的生命周期
Figure 3. The effect of the activity lifecycle on the fragment lifecycle.
圖三 Activity生命周期做Fragment生命周期的影響
處理Fragment的生命周期和處理Activity的生命周期很相似.和Activity一樣,Fragment的生命周期有一下三個狀態:
Resumed
- * Fragment在運行中的Activity中可見
Paused
- * 另一個Activity在前臺或者獲得了焦點,但是Fragment所在的Activity仍然可以看到(可能是前臺Activity占據了屏幕的一部分或者是半透明的)
Stopped
- * Fragment不可見.宿主Activity可能已經被停止了或者這個Fragment已經從這個Activity中移除了并被添加到了返回棧.一個停止的Fragment仍然是存活的(所有的狀態和成員信息被系統保存著).然而他不再對Activity可見,如果宿主Activity被殺死了,他也會被殺死.
和Activity一樣,在這個Activity所在的進程被殺死或者你需要在Activity重新創建的時候保存Fragment的狀態,你可以用Bundle來做這個工作.你可以在Fragment執行onSaveInstanceState()方法的時候保存它的狀態,然后在onCreate()或者onCreateView(),onActivityCreated()方法的時候恢復這些狀態.更多關于保存狀態的內容參考Activity文檔.
Activity和Fragment最大的不同是他們在返回棧中的存在形式.默認的,Activity在停止的時候,是放在一個被系統管理的返回棧中(這樣用戶可以使用back按鈕返回,就像在Tasks and Back Stack一章中談論的那樣).然而在一個移除Fragment的事務中,只有在你通過調用addToBackStack()明確的指明這個Fragment不要被保存,這個Fragment才會被放在被宿主Activity管理的返回棧中.
另外,管理Fragment的生命周期和管理Activity的生命周期很相似.所以,當管理Activity生命周期的方法也適于管理Fragment的生命周期.當然你也需要明確Activity對Fragment生命周期的影響.
注意,如果在你的Fragment中需要一個context對象,你可以調用getActivity.然而,只有這個Fragment附在這個Activity上的時候,才可以調用getActivity().如果Fragment還沒有附加在Activity上,或者在最后的生命周期和Activity分離了,那getActivity()方法將會返回null.
Coordinating with the activity lifecycle-和Activity生命周期的協調
擁有Fragment的Activity的生命周期會直接影響Fragment的生命周期,每個Activity生命周期方法會影響到每個Fragment.舉個例子,當一個Activity執行onPause()方法的時候,它里面的每個Fragment也會執行onPause().
Fragment有一些額外的生命周期,用來處理和Activity的特殊交換,從而可以執行形如創建和銷毀FragmentUI的事情.這些額外的回調方法有:
onAttach()
- * 當Fragment和Activity鏈接起來的時候調用(Activity在這里傳送過來).
onCreateView()
- * 創建Fragment的視圖層.
onActivityCreated()
- * 當Activity的onCreate返回的時候執行.
onDestroyView()
- * 當Fragment的試圖層被移除的時候執行.
onDetach()
- * 當Fragment和Activity分離的時候執行.
Fragment生命周期的流圖,由于被宿主Activity影響,可以用圖三表示.在這個表中,你可以知道每個Activity的每個狀態是怎樣決定一個Fragment收到的回調方法的.比如,當一個Activity收到他的onCreate()方法的時候,他里嗎的Fragment不會再收到onActivityCreated()方法的回調.
一旦Activity到達了resume狀態,你可以隨意添加和移除Activity中的Fragment.當然,只有這個Activity在resume狀態的時候,Fragment的生命周期才可以獨立的變化.
然而,當activity離開了resume狀態,Fragment會再一次被activity推到它的生命周期中.
Example-例子
為了把上面介紹的知識匯總,這里有個使用兩個Fragment組成兩個視圖布局的例子.下面的activity包含兩個Fragment,一個用來顯示Shakespeare話劇的標題,另一個用來顯示選中話劇的簡介.也演示了怎么根據屏幕的不同為這兩個Fragment提供不同的配置.
注意:完整代碼在FragmentLayout.java中.
主activity用平常的方式生成布局,在onCreate()方法的時候:
| 1 2 3 4 5 6 | @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.fragment_layout); } |
The layout applied is fragment_layout.xml:
fragment_layout.xml如下:
| 123456789 10 11 12 13 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width"match_parent"><fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"android:id"1"android:layout_width"match_parent" /><FrameLayout android:id"1"android:layout_width"match_parent"android:background="?android:attr/detailsElementBackground" /></LinearLayout> |
通過布局文件我們知道,系統在activity載入布局的時候實例化TitlesFragment(話劇的標題),FragmentLLayout(顯示話劇內容簡介的Fragment)占據右邊的屏幕但是現在沒有內容.就像你下面看到的那樣,直到用戶選擇了標題一個Fragment才會被放到FrameLayout.
然而,不是多有的屏幕配置都足夠顯示這兩個Fragment視圖.按照res/layout-land/fragment_layout.xml文件,上面的布局只適合橫屏.
那么當屏幕在豎屏的時候,系統會使用下面的布局,保存在res/layout/fragment_layout.xml中.
| 1 2 3 4 5 6 | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width"match_parent"><fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"android:id="@+id/titles"android:layout_width"match_parent" /> </FrameLayout> |
這個布局值包含TitlesFragment.這意味著當設備在豎屏的時候,只有話劇的標題是可見的.所以,當用戶點擊列表的一項的時候,應用將會開始一個新的activity來顯示簡介而不是載入第二個Fragment.
接下來,你將看到這在Fragment類中是怎么實現的.首先是TitleFragment,顯示了莎士比亞話劇的標題.這個Fragment繼承自ListFragment,可以通過它實現大多數顯示列表信息操作.
正如你看到的那樣,注意在用戶點擊列表響的時候,有兩個可能的行為:如果這兩個視圖存在,將在這個activity中創建并顯示一個新的Fragment(把Fragment添加到FragmentLayout中);如果只有一個視圖(豎屏),那會啟動一個新的activity(Fragment在這個activity中顯示).
| 123456789 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | public static class TitlesFragment extends ListFragment {boolean mDualPane;int mCurCheckPosition = 0;@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);// Populate list with our static array of titles.setListAdapter(new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));// Check to see if we have a frame in which to embed the details// fragment directly in the containing UI.View detailsFrame = getActivity().findViewById(R.id.details);mDualPane null && detailsFrame.getVisibility() == View.VISIBLE;if (savedInstanceState != null) {// Restore last state for checked position.mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);}if (mDualPane) {// In dual-pane mode, the list view highlights the selected item.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);// Make sure our UI is in the correct state.showDetails(mCurCheckPosition);}}@Overridepublic void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);outState.putInt("curChoice", mCurCheckPosition);}@Overridepublic void onListItemClick(ListView l, View v, int position, long id) {showDetails(position);}/* * * Helper function to show the details of a selected item, either by* displaying a fragment in-place in the current UI, or starting a* whole new activity in which it is displayed.* /void showDetails(int index) {mCurCheckPosition = index;if (mDualPane) {// We can display everything in-place with fragments, so update// the list to highlight the selected item and show the data.getListView().setItemChecked(index, true);// Check what fragment is currently shown, replace if needed.DetailsFragment details = (DetailsFragment)getFragmentManager().findFragmentById(R.id.details);if (details index) {// Make new fragment to show this selection.details = DetailsFragment.newInstance(index);// Execute a transaction, replacing any existing fragment// with this one inside the frame.FragmentTransaction ft = getFragmentManager().beginTransaction();ft.replace(R.id.details, details);ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);ft.commit();}} else {// Otherwise we need to launch a new activity to display// the dialog fragment with selected text.Intent intent = new Intent();intent.setClass(getActivity(), DetailsActivity.class);intent.putExtra("index", index);startActivity(intent);}} } |
第二個Fragment,DetailsFragment顯示了在TitleFragment中選中的話劇簡介.
| 123456789 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public static class DetailsFragment extends Fragment {/* * * Create a new instance of DetailsFragment, initialized to* show the text at 'index'.* /public static DetailsFragment newInstance(int index) {DetailsFragment f = new DetailsFragment();// Supply index input as an argument.Bundle args = new Bundle();args.putInt("index", index);f.setArguments(args);return f;}public int getShownIndex() {return getArguments().getInt("index", 0);}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {if (container == null) {// We have different layouts, and in one of them this// fragment's containing frame doesn't exist. The fragment// may still be created from its saved state, but there is// no reason to try to create its view hierarchy because it// won't be displayed. Note this is not needed -- we could// just run the code below, where we would create and return// the view hierarchy; it would just never be used.return null;}ScrollView scroller = new ScrollView(getActivity());TextView text = new TextView(getActivity());int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,4, getActivity().getResources().getDisplayMetrics());text.setPadding(padding, padding, padding, padding);scroller.addView(text);text.setText(Shakespeare.DIALOGUE[getShownIndex()]);return scroller;} } |
來自TitleFragment的調用,如果用戶點擊列表項的時候當前布局不包含R.id.details視圖(DetailsFragment 所在的視圖),那應用將會啟動DetailsActivity 來顯示選中項的內容簡介.
這里是DetailsActivity,在屏幕是豎屏的時候,簡單的嵌入在了Fragment中來顯示選中的話劇簡介.
| 123456789 10 11 12 13 14 15 16 17 18 19 20 21 22 | public static class DetailsActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE) {// If the screen is now in landscape mode, we can show the// dialog in-line with the list so we don't need this activity.finish();return;}if (savedInstanceState == null) {// During initial setup, plug in the details fragment.DetailsFragment details = new DetailsFragment();details.setArguments(getIntent().getExtras());getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();}} } |
注意activity會在橫屏的時候結束自己,這樣主activity可以接管并顯示DetailsFragment旁邊的TitlesFragment.如果用戶在豎屏的時候啟動DetailsActivity,然后把設備轉到橫屏(將會重啟當前的activity).
更多使用Fragment的例子(包括這個例子的全部代碼),請參考API Demo(可以在SDK例子那里下載).
?
轉載于:https://www.cnblogs.com/yangwuweiyouwei/p/3485242.html
總結
以上是生活随笔為你收集整理的[转]使用fragments的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链接器怎样使用静态库来解决符号引用
- 下一篇: sum() over() 函数的使用