讀古今文學網 > Android程序設計:第2版 > 創建Fragment >

創建Fragment

跟其他視圖對像一樣,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