溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android開發(fā)教程之Fragment定義、創(chuàng)建與使用方法詳解【包含Activity通訊,事務(wù)執(zhí)行等】

發(fā)布時(shí)間:2020-09-27 19:47:14 來源:腳本之家 閱讀:153 作者:時(shí)之沙 欄目:移動開發(fā)

本文實(shí)例講述了Android開發(fā)教程之Fragment定義、創(chuàng)建與使用方法。分享給大家供大家參考,具體如下:

概述

Fragment是activity的界面中的一部分或一種行為。你可以把多個(gè)Fragment們組合到一個(gè)activity中來創(chuàng)建一個(gè)多面界面并且你可以在多個(gè)activity中重用一個(gè)Fragment。你可以把Fragment認(rèn)為模塊化的一段activity,它具有自己的生命周期,接收它自己的事件,并可以在activity運(yùn)行時(shí)被添加或刪除。

Fragment不能獨(dú)立存在,它必須嵌入到activity中,而且Fragment的生命周期直接受所在的activity的影響。例如:當(dāng)activity暫停時(shí),它擁有的所有的Fragment們都暫停了,當(dāng)activity銷毀時(shí),它擁有的所有Fragment們都被銷毀。然而,當(dāng)activity運(yùn)行時(shí)(在onResume()之后,onPause()之前),你可以單獨(dú)地操作每個(gè)Fragment,比如添加或刪除它們。當(dāng)你在執(zhí)行上述針對Fragment的事務(wù)時(shí),你可以將事務(wù)添加到一個(gè)棧中,這個(gè)棧被activity管理,棧中的每一條都是一個(gè)Fragment的一次事務(wù)。有了這個(gè)棧,就可以反向執(zhí)行Fragment的事務(wù),這樣就可以在Fragment級支持“返回”鍵(向后導(dǎo)航)。

當(dāng)向activity中添加一個(gè)Fragment時(shí),它須置于ViewGroup控件中,并且需定義Fragment自己的界面。你可以在layoutxml文件中聲明Fragment,元素為:<fragment>;也可以在代碼中創(chuàng)建Fragment,然后把它加入到ViewGroup控件中。然而,F(xiàn)ragment不一定非要放在activity的界面中,它可以隱藏在后臺為actvitiy工作。

接下來講如何使用fragment,包括fragment在加入activity的后退棧中時(shí)如何保持自己的狀態(tài),如何與activity以及其它fragment們共享事件,如何顯示在activity的動作欄,等等。

Android從3.0開始引入fragment,主要是為了支持更動態(tài)更靈活的界面設(shè)計(jì),比如在平板上的應(yīng)用。平板機(jī)上擁有比手機(jī)更大的屏幕空間來組合和交互界面組件們。Fragment使你在做那樣的設(shè)計(jì)時(shí),不需應(yīng)付view樹中復(fù)雜的變化。通過把a(bǔ)ctivity的layout分成fragment,你可以在activity運(yùn)行時(shí)改變它的樣子,并且可以在activity的后退棧中保存這些改變。

例如:寫一個(gè)讀新聞的程序,可以用一個(gè)fragment顯示標(biāo)題列表,另一個(gè)fragment顯示選中標(biāo)題的內(nèi)容,這兩個(gè)fragment都在一個(gè)activity上,并排顯示。那么這兩個(gè)fragment都有自己的生命周期并響應(yīng)自己感興趣的事件。于是,不需再像手機(jī)上那樣用一個(gè)activity顯示標(biāo)題列表,用另一個(gè)activity顯示新聞內(nèi)容;現(xiàn)在可以把兩者放在一個(gè)activity上同時(shí)顯示出來。

Fragment必須被寫成可重用的模塊。因?yàn)閒ragment有自己的layout,自己進(jìn)行事件響應(yīng),擁有自己的生命周期和行為,所以你可以在多個(gè)activity中包含同一個(gè)Fragment的不同實(shí)例。這對于讓你的界面在不同的屏幕尺寸下都能給用戶完美的體驗(yàn)尤其重要。比如你可以在程序運(yùn)行于大屏幕中時(shí)啟動包含很多fragment的activity,而在運(yùn)行于小屏幕時(shí)啟動一個(gè)包含少量fragment的activity。

舉個(gè)例子--還是剛才那個(gè)讀新聞的程序-當(dāng)你檢測到程序運(yùn)行于大屏幕時(shí),啟動activityA,你將標(biāo)題列表和新聞內(nèi)容這兩個(gè)fragment都放在activityA中;當(dāng)檢測到程序運(yùn)行于小屏幕時(shí),還是啟動activityA,但此時(shí)A中只有標(biāo)題列表fragment,當(dāng)選中一個(gè)標(biāo)題時(shí),activityA啟動activityB,B中含有新聞內(nèi)容fragment。

Fragments的生命周期

    每一個(gè)fragments 都有自己的一套生命周期回調(diào)方法和處理自己的用戶輸入事件。 對應(yīng)生命周期可參考下圖:

Android開發(fā)教程之Fragment定義、創(chuàng)建與使用方法詳解【包含Activity通訊,事務(wù)執(zhí)行等】

創(chuàng)建片元(Creating a Fragment)

To create a fragment, you must create a subclass of Fragment (or an existing subclass of it). The Fragment class has code that looks a lot like an Activity. It contains callback methods similar to an activity, such as onCreate(), onStart(), onPause(), and onStop(). In fact, if you're converting an existing Android application to use fragments, you might simply move code from your activity's callback methods into the respective callback methods of your fragment.

要創(chuàng)建一個(gè)fragment,必須創(chuàng)建一個(gè)fragment的子類(或是繼承自它的子類)。fragment類的代碼看起來很像activity。它與activity一樣都有回調(diào)函數(shù),例如onCreate(),onStart()onPause(),和onStop()。事實(shí)上,如果你正在將一個(gè)現(xiàn)成的Android應(yīng)用轉(zhuǎn)而使用Fragment來實(shí)現(xiàn),可以簡單的將代碼從activity的回調(diào)函數(shù)移植到各自的fragment回調(diào)函數(shù)中。

Usually, you should implement at least the following lifecycle methods:
一般情況下,你至少需要實(shí)現(xiàn)以下幾個(gè)生命周期方法:
onCreate()
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
在創(chuàng)建fragment時(shí)系統(tǒng)會調(diào)用此方法。在實(shí)現(xiàn)代碼中,你可以初始化想要在fragment中保持的那些必要組件(這里的組件是指除了view之外的東西,比如需要進(jìn)行界面展示的關(guān)鍵數(shù)據(jù)),當(dāng)fragment處于暫?;蛘咄V?fàn)顟B(tài)之后可重新啟用它們。

onCreateView()
The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI.
在第一次為fragment繪制用戶界面時(shí)系統(tǒng)會調(diào)用此方法。為fragment繪制用戶界面,這個(gè)函數(shù)必須要返回所繪出的fragment的根View。如果fragment沒有用戶界面可以返回空。

onPause()
The system calls this method as the first indication that the user is leaving the fragment (though it does not always mean the fragment is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).
系統(tǒng)回調(diào)用該函數(shù)作為用戶離開fragment的第一個(gè)預(yù)兆(盡管這并不總意味著fragment被銷毀)。在當(dāng)前用戶會話結(jié)束之前,通常要在這里提交任何應(yīng)該持久化的變化(因?yàn)橛脩艨赡懿辉俜祷兀?/p>

Most applications should implement at least these three methods for every fragment, but there are several other callback methods you should also use to handle various stages of the fragment lifecycle. All the lifecycle callback methods are discussed more later, in the section about Handling the Fragment Lifecycle.
大部分應(yīng)用程序都應(yīng)該至少為每個(gè)fragment實(shí)現(xiàn)這三個(gè)方法,但是還有許多其他用以操縱fragment生命周期中各個(gè)階段的回調(diào)函數(shù)。所有生命周期中的回調(diào)函數(shù)在操縱fragment生命周期一節(jié)中稍后再做討論。

There are also a few subclasses that you might want to extend, instead of the base Fragment class:
除了基類fragment,這里還有幾個(gè)你可能會繼承的子類:

DialogFragment
Displays a floating dialog. Using this class to create a dialog is a good alternative to using the dialog helper methods in the Activity class, because you can incorporate a fragment dialog into the back stack of fragments managed by the activity, allowing the user to return to a dismissed fragment.
顯示一個(gè)浮動的對話框。使用這個(gè)類創(chuàng)建對話框是使用Activity類對話框工具方法之外的另一個(gè)不錯(cuò)的選擇,因?yàn)槟憧梢园裦ragment對話框并入到由activity管理的fragments后臺棧中,允許用戶返回到一個(gè)已經(jīng)摒棄的fragment。

ListFragment
Displays a list of items that are managed by an adapter (such as a SimpleCursorAdapter), similar to ListActivity. It provides several methods for managing a list view, such as the onListItemClick() callback to handle click events.
顯示一個(gè)由適配器管理的條目列表(例如SimpleCursorAdapter),類似于ListActivity。并且提供了許多管理列表視圖的函數(shù),例如處理點(diǎn)擊事件的onListItemClick()回調(diào)函數(shù)。

PreferenceFragment
Displays a hierarchy of Preference objects as a list, similar to PreferenceActivity. This is useful when creating a "settings" activity for your application.
顯示一個(gè)Preference對象的體系結(jié)構(gòu)列表,類似于preferenceActivity。這在為應(yīng)用程序創(chuàng)建“設(shè)置”activity時(shí)是很實(shí)用的。

實(shí)現(xiàn)Fragment的界面

為fragment添加用戶界面:

Fragment一般作為activity的用戶界面的一部分,把它自己的layout嵌入到activity的layout中。 一個(gè)
要為fragment提供layout,你必須實(shí)現(xiàn)onCreateView()回調(diào)方法,然后在這個(gè)方法中返回一個(gè)View對象,這個(gè)對象是fragment的layout的根。

注:如果你的fragment是從ListFragment中派生的,就不需要實(shí)現(xiàn)onCreateView()方法了,因?yàn)槟J(rèn)的實(shí)現(xiàn)已經(jīng)為你返回了ListView控件對象。

要從onCreateView()方法中返回layout對象,你可以從layoutxml中生成layout對象。為了幫助你這樣做,onCreateView()提供了一個(gè)LayoutInflater對象。

舉例:以下代碼展示了一個(gè)Fragment的子類如何從layoutxml文件example_fragment.xml中生成對象。

publicstaticclassExampleFragmentextendsFragment{
  @Override
 publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){
    //Inflate the layout for this fragment
    returninflater.inflate(R.layout.example_fragment,container,false);
  }
}

onCreateView()參數(shù)中的container是存放fragment的layout的ViewGroup對象。savedInstanceState參數(shù)是一個(gè)Bundle,跟activityonCreate()中Bundle差不多,用于狀態(tài)恢復(fù)。但是fragment的onCreate()中也有Bundle參數(shù),所以此處的Bundle中存放的數(shù)據(jù)與onCreate()中存放的數(shù)據(jù)還是不同的。至于詳細(xì)信息,請參考“操控fragment的生命周期”一節(jié)。

Inflate()方法有三個(gè)參數(shù):

1 layout的資源ID。

2 存放fragment的layout的ViewGroup。

3 布爾型數(shù)據(jù)表示是否在創(chuàng)建fragment的layout期間,把layout附加到container上(在這個(gè)例子中,因?yàn)橄到y(tǒng)已經(jīng)把layout插入到container中了,所以值為false,如果為true會導(dǎo)至在最終的layout中創(chuàng)建多余的ViewGroup(這句我看不明白,但我翻譯的應(yīng)該沒錯(cuò)))。

現(xiàn)在你看到如何為fragment創(chuàng)建layout了,下面講述如何把它添加到activity中。

把fragment添加到activity

一般情況下,fragment把它的layout作為activitiy的loyout的一部分合并到activity中,有兩種方法將一個(gè)fragment添加到activity中:

方法一:在activity的layoutxml文件中聲明fragment

如下代碼,一個(gè)activity中包含兩個(gè)fragment:

<?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>中聲明一個(gè)fragment。
當(dāng)系統(tǒng)創(chuàng)建上例中的layout時(shí),它實(shí)例化每一個(gè)fragment,然后調(diào)用它們的onCreateView()方法,以獲取每個(gè)fragment的layout。系統(tǒng)把fragment返回的view對象插入到<fragment>元素的位置,直接代替<fragment>元素。

注:每個(gè)fragment都需要提供一個(gè)ID,系統(tǒng)在activity重新創(chuàng)建時(shí)用它來恢復(fù)fragment們,你也可以用它來操作fragment進(jìn)行其它的事物,比如刪除它。有三種方法給fragment提供ID:

1 為android:id屬性賦一個(gè)特定的標(biāo)識符。
2 為android:tag屬性賦一個(gè)標(biāo)記名稱。
3如果你沒有使用上述任何一種方法,系統(tǒng)將使用fragment的容器的ID。

方法二:在代碼中添加fragment到一個(gè)ViewGroup

這種方法可以在運(yùn)行時(shí),把fragment添加到activity的layout中。你只需指定一個(gè)要包含fragment的ViewGroup。

為了完成fragment的事務(wù)(比如添加,刪除,替換等),你必須使用FragmentTransaction的方法。你可以從activity獲取到FragmentTransaction,如下:

FragmentManagerfragmentManager =getFragmentManager()
FragmentTransactionfragmentTransaction =fragmentManager.beginTransaction();

然后你可以用add()方法添加一個(gè)fragment,它有參數(shù)用于指定容納fragment的ViewGroup。如下:

ExampleFragmentfragment =newExampleFragment();
fragmentTransaction.add(R.id.fragment_container,fragment);
fragmentTransaction.commit();

Add()的第一個(gè)參數(shù)是容器ViewGroup,第二個(gè)是要添加的fragment。一旦你通過FragmentTransaction對fragment做出了改變,你必須調(diào)用方法commit()提交這些改變。

不僅在無界面的fragment中,在有界面的fragment中也可以使用tag來作為為一標(biāo)志,這樣在需要獲取fragment對象時(shí),要調(diào)用findFragmentTag()

添加一個(gè)沒有界面的fragment

上面演示了如何添加fragment來提供界面,然而,你也可以使用fragment為activity提供后臺的行為而不用顯示fragment的界面。

要添加一個(gè)沒有界面的fragment,需在activity中調(diào)用方法add(Fragment,String)(它支持用一個(gè)唯一的字符串做為fragment的”tag”,而不是viewID)。這樣添加的fragment由于沒有界面,所以你在實(shí)現(xiàn)它時(shí)不需調(diào)用實(shí)現(xiàn)onCreateView()方法。

使用tag字符串來標(biāo)識一個(gè)fragment并不是只能用于沒有界面的fragment上,你也可以把它用于有界面的fragment上,但是,如果一個(gè)fragment沒有界面,tag字符串將成為它唯一的選擇。獲取以tag標(biāo)識的fragment,需使用方法findFragmentByTab()。

管理Fragment

要管理fragment們,需使用FragmentManager,要獲取它,需在activity中調(diào)用方法getFragmentManager()

你可以用FragmentManager來做以上事情:

1 使用方法findFragmentById()findFragmentByTag(),獲取activity中已存在的fragment們。
2 使用方法popBackStack()從activity的后退棧中彈出fragment們(這可以模擬后退鍵引發(fā)的動作)。
3 用方法addOnBackStackChangedListerner()注冊一個(gè)偵聽器以監(jiān)視后退棧的變化。

你還可以使用FragmentManager打開一個(gè)FragmentTransaction來執(zhí)行fragment的事務(wù),比如添加或刪除fragment。

執(zhí)行Fragment的事務(wù)

在activity中使用fragment的一個(gè)偉大的好處是能跟據(jù)用戶的輸入對fragment進(jìn)行添加、刪除、替換以及執(zhí)行其它動作的能力。你提交的一組fragment的變化叫做一個(gè)事務(wù)。事務(wù)通過FragmentTransaction來執(zhí)行。你還可以把每個(gè)事務(wù)保存在activity的后退棧中,這樣就可以讓用戶在fragment變化之間導(dǎo)航(跟在activity之間導(dǎo)航一樣)。

你可以通過FragmentManager來取得FragmentTransaction的實(shí)例,如下:

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

一個(gè)事務(wù)是在同一時(shí)刻執(zhí)行的一組動作(很像數(shù)據(jù)庫中的事務(wù))。你可以用add(),remove(),replace()等方法構(gòu)成事務(wù),最后使用commit()方法提交事務(wù)。

在調(diào)用commit()之前,你可以用addToBackStack()把事務(wù)添加到一個(gè)后退棧中,這個(gè)后退棧屬于所在的activity。有了它,就可以在用戶按下返回鍵時(shí),返回到fragment們執(zhí)行事務(wù)之前的狀態(tài)。

如下例:演示了如何用一個(gè)fragment代替另一個(gè)fragment,同時(shí)在后退棧中保存被代替的fragment的狀態(tài)。

//Create new fragment and transaction
Fragment newFragment = newExampleFragment();
FragmentTransaction transaction=getFragmentManager().beginTransaction();
//Replace whatever is in the fragment_container view with thisfragment,
//and add the transaction to the backstack
transaction.replace(R.id.fragment_container,newFragment);
transaction.addToBackStack(null);
//Commit the transaction
transaction.commit();

解釋:newFragment代替了控件IDR.id.fragment_container所指向的ViewGroup中所含的任何fragment。然后調(diào)用addToBackStack(),此時(shí)被代替的fragment就被放入后退棧中,于是當(dāng)用戶按下返回鍵時(shí),事務(wù)發(fā)生回溯,原先的fragment又回來了。

如果你向事務(wù)添加了多個(gè)動作,比如多次調(diào)用了add(),remove()等之后又調(diào)用了addToBackStack()方法,那么所有的在commit()之前調(diào)用的方法都被作為一個(gè)事務(wù)。當(dāng)用戶按返回鍵時(shí),所有的動作都被反向執(zhí)行(事務(wù)回溯)。

事務(wù)中動作的執(zhí)行順序可隨意,但要注意以下兩點(diǎn):

1. 你必須最后調(diào)用commit()。

2. 如果你添加了多個(gè)fragment,那么它們的顯示順序跟添加順序一至(后顯示的覆蓋前面的)。

如果你在執(zhí)行的事務(wù)中有刪除fragment的動作,而且沒有調(diào)用addToBackStack(),那么當(dāng)事務(wù)提交時(shí),那些被刪除的fragment就被銷毀了。反之,那些fragment就不會被銷毀,而是處于停止?fàn)顟B(tài)。當(dāng)用戶返回時(shí),它們會被恢復(fù)。

密技:對于fragment事務(wù),你可以應(yīng)用動畫。在commit()之前調(diào)用setTransition()就行。――一般銀我不告訴他哦。

但是,調(diào)用commit()后,事務(wù)并不會馬上執(zhí)行。它會在activity的UI線程(其實(shí)就是主線程)中等待直到線程能執(zhí)行的時(shí)候才執(zhí)行(廢話)。如果必要,你可以在UI線程中調(diào)用executePendingTransactions()方法來立即執(zhí)行事務(wù)。但一般不需這樣做,除非有其它線程在等待事務(wù)的執(zhí)行。

警告:你只能在activity處于可保存狀態(tài)的狀態(tài)時(shí),比如running中,onPause()方法和onStop()方法中提交事務(wù),否則會引發(fā)異常。這是因?yàn)閒ragment的狀態(tài)會丟失。如果要在可能丟失狀態(tài)的情況下提交事務(wù),請使用commitAllowingStateLoss()。

Fragment與Activity通訊

盡管fragment的實(shí)現(xiàn)是獨(dú)立于activity的,可以被用于多個(gè)activity,但是每個(gè)activity所包含的是同一個(gè)fragment的不同的實(shí)例。

Fragment可以調(diào)用getActivity()方法很容易的得到它所在的activity的對象,然后就可以查找activity中的控件們(findViewById())。例如:

ViewlistView =getActivity().findViewById(R.id.list);

同樣的,activity也可以通過FragmentManager的方法查找它所包含的frament們。例如:

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

activity響應(yīng)fragment的事件

有時(shí),你可能需要fragment與activity共享事件。一個(gè)好辦法是在fragment中定義一個(gè)回調(diào)接口,然后在activity中實(shí)現(xiàn)之。

例如,還是那個(gè)新聞程序的例子,它有一個(gè)activity,activity中含有兩個(gè)fragment。fragmentA顯示新聞標(biāo)題,fragmentB顯示標(biāo)題對應(yīng)的內(nèi)容。fragmentA必須在用戶選擇了某個(gè)標(biāo)題時(shí)告訴activity,然后activity再告訴fragmentB,fragmentB就顯示出對應(yīng)的內(nèi)容(為什么這么麻煩?直接fragmentA告訴fragmentB不就行了?也可以啊,但是你的fragment就減少了可重用的能力?,F(xiàn)在我只需把我的事件告訴宿主,由宿主決定如何處置,這樣是不是重用性更好呢?)。如下例,OnArticleSelectedListener接口在fragmentA中定義:

public static class FragmentA extends ListFragment{
 ...
 //Container Activity must implement this interface
 public interface OnArticleSelectedListener{
   public void onArticleSelected(Uri articleUri);
 }
 ...

然后activity實(shí)現(xiàn)接口OnArticleSelectedListener,在方法onArticleSelected()中通知fragmentB。當(dāng)fragment添加到activity中時(shí),會調(diào)用fragment的方法onAttach(),這個(gè)方法中適合檢查activity是否實(shí)現(xiàn)了OnArticleSelectedListener接口,檢查方法就是對傳入的activity的實(shí)例進(jìn)行類型轉(zhuǎn)換,如下所示:

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 implement OnArticleSelectedListener");
   }
 }
 ...

如果activity沒有實(shí)現(xiàn)那個(gè)接口,fragment拋出ClassCastException異常。如果成功了,mListener成員變量保存OnArticleSelectedListener的實(shí)例。于是fragmentA就可以調(diào)用mListener的方法來與activity共享事件。例如,如果fragmentA是一個(gè)ListFragment,每次選中列表的一項(xiàng)時(shí),就會調(diào)用fragmentA的onListItemClick()方法,在這個(gè)方法中調(diào)用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()傳入的參數(shù)id是列表的被選中的行ID,另一個(gè)fragment用這個(gè)ID來從程序的ContentProvider中取得標(biāo)題的內(nèi)容。

Fragement應(yīng)用示例

把條目添加到動作欄

你的fragment們可以向activity的菜單(按Manu鍵時(shí)出現(xiàn)的東西)添加項(xiàng),同時(shí)也可向動作欄(界面中頂部的那個(gè)區(qū)域)添加條目,這都需通過實(shí)現(xiàn)方法onCreateOptionManu()來完成。

你從fragment添加到菜單的任何條目,都會出現(xiàn)在現(xiàn)有菜單項(xiàng)之后。Fragment之后可以通過方法onOptionsItemSelected()來響應(yīng)自己的菜單項(xiàng)被選擇的事件。

你也可以在fragemnt中注冊一個(gè)view來提供快捷菜單(上下文菜單)。當(dāng)用戶要打開快捷菜單時(shí),fragment的onCreateContextMenu()方法會被調(diào)用。當(dāng)用戶選擇其中一項(xiàng)時(shí),fragemnt的onContextItemSelected()方法會被調(diào)用。

注:盡管你的fragment可以分別收到它所添加的菜單項(xiàng)的選中事件,但是activity才是第一個(gè)接收這些事件的家伙,只有當(dāng)activity對某個(gè)事件置之不理時(shí),fragment才能接收到這個(gè)事件,對于菜單和快捷菜單都是這樣。

下例中實(shí)驗(yàn)了之前所講的所有內(nèi)容。此例有一個(gè)activity,其含有兩個(gè)fragment。一個(gè)顯示莎士比亞劇的播放曲目,另一個(gè)顯示選中曲目的摘要。此例還演示了如何跟據(jù)屏幕大小配置fragment。

MainActivity:

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.fragment_layout);
}

Layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="match_parent" android:layout_height="match_parent">
  <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
      android:id="@+id/titles" android:layout_weight="1"
      android:layout_width="0px" android:layout_height="match_parent" />
  <FrameLayout android:id="@+id/details" android:layout_weight="1"
      android:layout_width="0px" android:layout_height="match_parent"
      android:background="?android:attr/detailsElementBackground" />
</LinearLayout>

系統(tǒng)在activity加載此layout時(shí)初始化TitlesFragment(用于顯示標(biāo)題列表),TitlesFragment的右邊是一個(gè)FrameLayout,用于存放顯示摘要的fragment,但是現(xiàn)在它還是空的,fragment只有當(dāng)用戶選擇了一項(xiàng)標(biāo)題后,摘要fragment才會被放到FrameLayout中。

然而,并不是所有的屏幕都有足夠的寬度來容納標(biāo)題列表和摘要。所以,上述layout只用于橫屏,現(xiàn)把它存放于ret/layout-land/fragment_layout.xml。

之外,當(dāng)用于豎屏?xí)r,系統(tǒng)使用下面的layout,它存放于ret/layout/fragment_layout.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent" android:layout_height="match_parent">
  <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
      android:id="@+id/titles"
      android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

這個(gè)layout只包含TitlesFragment。這表示當(dāng)使用豎屏?xí)r,只顯示標(biāo)題列表。當(dāng)用戶選中一項(xiàng)時(shí),程序會啟動一個(gè)新的activity去顯示摘要,而不是加載第二個(gè)fragment。

下一步,你會看到Fragment類的實(shí)現(xiàn)。第一個(gè)是TitlesFragment,它從ListFragment派生,大部分列表的功能由ListFragment提供。

當(dāng)用戶選擇一個(gè)Title時(shí),代碼需要做出兩種行為,一種是在同一個(gè)activity中顯示創(chuàng)建并顯示摘要fragment,另一種是啟動一個(gè)新的activity。

public static class TitlesFragment extends ListFragment {
  boolean mDualPane;
  int mCurCheckPosition = 0;
  @Override
  public 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 = detailsFrame != 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);
    }
  }
  @Override
  public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("curChoice", mCurCheckPosition);
  }
  @Override
  public 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 == null || details.getShownIndex() != 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);
    }
  }
}

第二個(gè)fragment,DetailsFragment顯示被選擇的Title的摘要:

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);
  }
  @Override
  public 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;
  }
}

如果當(dāng)前的layout沒有R.id.detailsView(它被用于DetailsFragment的容器),那么程序就啟動DetailsActivity來顯示摘要。

下面是DetailsActivity,它只是簡單地嵌入DetailsFragment來顯示摘要。

public static class DetailsActivity extends Activity {
  @Override
  protected 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();
    }
  }
}

注意這個(gè)activity在檢測到是豎屏?xí)r會結(jié)束自己,于是主activity會接管它并顯示出TitlesFragment和DetailsFragment。這可以在用戶在豎屏?xí)r顯示在TitleFragment,但用戶旋轉(zhuǎn)了屏幕,使顯示變成了橫屏。

更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android基本組件用法總結(jié)》、《Android開發(fā)入門與進(jìn)階教程》、《Android布局layout技巧總結(jié)》、《Android視圖View技巧總結(jié)》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android資源操作技巧匯總》及《Android控件用法總結(jié)》

希望本文所述對大家Android程序設(shè)計(jì)有所幫助。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI