跟其他視圖對像一樣,fragment既可以在XML佈局中定義,也可以通過代碼直接添加到視圖中。在如下佈局中添加fragment:
<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:orientation=\"horizontal\" android:layout_ android:layout_ > <fragment android:id=\"@+id/date_time\" android:layout_ android:layout_ /> </LinearLayout>
Activity可以通過如下方式使用該佈局:
@Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.main); }
這段代碼現在看起來應該是相當熟悉了。唯一的區別在於main.xml文件中的fragment標籤。該標籤使用class屬性指定了實現fragment的類的完整名稱。實現Fragment的類中包含一些約束條件,在這個例子中,實現Fragment的類是com.oreilly.demo.android.ch085.contactviewer.DateTime,其約束條件包括:
·指定的類必須存在,並且對於應用是可見的。
·該類必須是Fragment類的子類。
雖然靜態驗證這些約束是可行的,但是,當前的Android工具並不支持。需要手工檢查這些約束條件。
當佈局inflate時,Android框架會創建該類的新實例。其實現可能很讓人意外。首先,它表示該類必須包含沒有參數的構造函數,即Java默認提供的構造函數。實際上,Android開發者文檔強烈建議不要在Fragment的子類中定義任何構造函數,因為如果定義構造函數,則新創建的Fragment對像在創建時可能會不一致。開發者文檔建議推遲對fragment的初始化,待進入fragment的生命週期之後再執行。
無論你在應用的其他地方如何使用fragment,如果你在佈局中使用它,inflation過程必須能夠創建它,而且不需要提供任何初始化參數。因此,以這種方式創建的fragment即使沒有執行初始化,也需要執行一些操作。例如,顯示從URL接收的內容的fragment必須處理URL及其內容為空的情況。
以下是一個非常簡單的fragment:
public class DateTime extends Fragment { private String time; public void onCreate(Bundle state) { super.onCreate(state); if (null == time) { time = new SimpleDateFormat(\"d MMM yyyy HH:mm:ss\") .format(new Date); } } @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle b) { View view = inflater.inflate( R.layout.date_time, container, false); //!!! this is important ((TextView) view.findViewById(R.id.last_view_time)) .setText(time); return view; } }
這段代碼有幾個要點。首先,onCreate生命週期方法的實現應該使我們能夠想到Activity類及其具有生命週期的方法。雖然Fragment的生命週期和Activity的生命週期不完全相同,但它們包含很多相同的方法。對於Activity,fragment的onCreate方法是在fragment初始化時調用的。這是執行fragment初始化的非常好的時機。在這個例子中,很好地確保了變量time的值及fragment要顯示的都能夠正確初始化。
Fragment包含一些額外的有生命週期的方法,包括本例中所使用的onCreateView。當fragment的視圖執行初始化時,會調用onCreateView方法(和onCreate方法不同,onCreateView方法是在fragment本身執行初始化時調用的)。注意,fragment通過傳遞LayoutInflater對視圖碎片(view shard)R.layout.date_time執行實例化來創建視圖。簡單的視圖碎片,僅僅是在RelativeLayout中的一組TextView,是在自己的文件中定義的,layout/date_time.xml(這裡沒有給出),和之前顯示的主要佈局很相似。
還要注意的是(這塊有點複雜),對inflate的調用的第三個參數是布爾型的false。這一點很重要。Inflater必須能夠訪問container,即最終會新創建的視圖碎片的父親。它需要成為父視圖才能夠正確地處理佈局。例如,舉個例子,container是RelativeLayout,它通過layout_toRightOf指令創建新的碎片的位置。
另一方面,fragment框架擁有並管理著onCreateView方法所返回的視圖。在onCreateView方法中,不應該把視圖碎片附著到container中,因為在inflation階段通常會執行這個操作。第三個參數是個標誌,它表明fragment框架會控制操作,不應該把視圖碎片關聯到容器上。
一旦創建了fragment視圖,就可以使用視圖的findViewById方法找到其他嵌套的widget。這個例子使用findViewById方法找到了要顯示時間的TextView,並把其值設置成在onCreate方法中初始化的變量time的值。運行時的情況如圖7-1所示。
圖7-1:一個簡單的fragment