Fragment學習理解筆記

Fragment(片段)

  1. Fragment的簡介
    在官方文檔中,fragment被定義爲activity的一個模塊零件,它有自己的生命週期,接收它自己的輸入事件,並且可以在activity運行時添加或者刪除。實際意義上它可以算是Android系統中的第五大組件。有人說Fragment是一個veiw,它綁定在activity之上,可以在佈局文件中創建也可以通過代碼生成進而展示在界面上。但它又不是一個view,它有自己的生命週期,它處理自己的事件。
    對於fragment的學習和理解我們應該注意以下幾個概念:
    (1).Fragment必須總是被嵌入到一個activity之中,並且fragment的生命週期直接受其宿主activity的生命週期的影響。
    (2).可以在一個單獨的activity上把多個fragment組合成爲一個多區域的UI,並且可以在多個activity中再使用該UI。
    (3). 當activity的生命週期處於resume的時候,可以操作基於其之上的fragment(add or delete)
    (4). 當你添加一個fragment作爲某個activity佈局的一部分時,它類似於view存在於這個activity視圖體系內部的ViewGroup之中 。你可以通過在activity佈局文件中聲明fragment,用元素把fragment插入到activity的佈局中,或者是用應用程序源碼將它添加到一個存在的ViewGroup中。fragment同樣可以爲activity隱身工作。
  2. Fragment對比activity的生命週期
Frament lifecycle Activity lifecycle
這裏寫圖片描述 這裏寫圖片描述

對比上圖,我們可以發現fragment的生命週期和activity的大同小異,fragment看起來還要多一些生命週期方法。在activity的創建期間,fragment會經歷onAttach(),onCreate(),onCreateView()和onActivityCreated()。這種情況可以理解爲fragment作爲activity的view來處理,當activity創建之初需要加載視圖。之後的生命週期一一對應,在最後的onDestroy()中,fragment多了onDestroyView()和OnDetach()。
3. Fragment的創建
要創建一個fragment,必須創建一個fragment的子類(或是繼承自它的子類)。
除了基類fragment,這裏還有幾個你可能會繼承的子類:
(1).DialogFragment
顯示一個浮動的對話框。使用這個類創建對話框是使用Activity類對話框幫助方法之外的另一個不錯的選擇,因爲你可以把fragment對話框併入到由activity管理的fragments後臺棧中,允許用戶返回到一個已經摒棄的fragment。
(2).ListFragment
顯示一個由適配器管理的條目列表(例如SimpleCursorAdapter),類似於ListActivity。並且提供了許多管理列表視圖的函數,例如處理點擊事件的onListItemClick()回調函數。
(3).PreferenceFragment
顯示一個Preference對象的體系結構列表,類似於preferenceActivity。這在爲應用程序創建“設置”activity時是很實用的。
4. Fragment添加到用戶界面
fragment常被用作activity用戶界面的一部分,並且將本身的佈局構建到activity中去。
爲了給fragment提供一個佈局,必須實現onCreateView()回調函數,在繪製fragment佈局時Android系統會調用它。實現這個函數時需要返回fragment所屬的根View。

public static class ExampleFragment extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.example_fragment, container, false);
        }
    }

5.將fragment添加到activity之中
(1).在activity的佈局文件裏聲明fragment

<?xml version="1.0" encoding="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>

fragment中的android:name 屬性指定了佈局中實例化的Fragment類。
當系統創建activity佈局時,它實例化了佈局文件中指定的每一個fragment,併爲它們調用onCreateView()函數,以獲取每一個fragment的佈局。系統直接在元素的位置插入fragment返回的View。
(2).通過編碼將fragment添加到已存在的ViewGroup中
在activity運行的任何時候,你都可以將fragment添加到activity佈局中。你僅需要簡單指定用來放置fragment的ViewGroup。
你應當使用FragmentTransaction的API來對activity中的fragment進行操作(例如添加,移除,或者替換fragment)。你可以像下面這樣從Activity中取得FragmentTransaction的實例:

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

可以用add()函數添加fragment,並指定要添加的fragment以及要將其插入到哪個視圖(view)之中:

    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container,fragment);fragmentTransaction.commit();

傳入add()函數的第一個參數是fragment被放置的ViewGroup,它由資源ID(resource ID)指定,第二個參數就是要添加的fragment。

一旦通過FragmentTransaction 做了更改,都應當使用commit()使變化生效。
6. 處理Fragment事務
在activity中使用fragment的一大特點是具有添加、刪除、替換,和執行其它動作的能力,以響應用戶的互動。提交給activity的每一系列變化被稱爲事務,並且可以用FragmentTransaction 中的APIs處理。你也可以將每一個事務保存在由activity管理的後臺棧中,並且允許用戶導航回退fragment變更(類似於activity的導航回退)。
舉個例子,下面的代碼是如何使用另一個fragment代替一個fragment,並且將之前的狀態保留在後臺棧中:

// Create new fragment and transaction
 Fragment newFragment = new ExampleFragment();
 FragmentTransaction transaction = getFragmentManager().beginTransaction();

 // Replace whatever is in the fragment_container view with this fragment,
 // and add the transaction to the back stack
 transaction.replace(R.id.fragment_container, newFragment);
 transaction.addToBackStack(null);

 // Commit the transaction
 transaction.commit();

將變更添加到FragmentTransaction中的順序注意以下兩點:
(1).必須要在最後調用commit()
(2).如果你正將多個fragment添加到同一個容器中,那麼添加順序決定了它們在視圖層次(view hierarchy)裏顯示的順序。
7. 與activity交互,創建activity事件回調函數
舉個例子,如果新聞應用的actvity中有兩個fragment——一個顯示文章列表(fragment A),另一個顯示一篇文章(fragment B)——然後fragment A 必須要告訴activity列表項何時被選種,這樣,activity可以通知fragment B顯示這篇文章。這種情況下,在fragment A內部聲明接口OnArticleSelectedListener:

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

如果activity沒有實現這個接口,那麼fragment會拋出一個ClassCaseException異常。一旦成功,mListener成員會保留一個activity的OnArticleSelectedListener實現的引用,由此fragment A可以通過調用由OnArticleSelectedListener接口定義的方法與activity共享事件。例如,如果fragment A是ListFragment的子類,每次用戶點擊列表項時,系統都會調用fragment的onListItemClick()事件,然後fragment調用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);
    }
    ...
}

以上過程種“如果activity沒有實現這個接口,那麼fragment會拋出一個ClassCaseException異常。”這個設計是一個很有意思的點,巧妙的運用了接口的特性來達到判斷的目的。因爲如果你的activity實現了該接口,那麼你的activity就可被強制轉換爲該接口。其實是多態的應用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章