其實 Fragment 的實作上步驟都是差不多的 :
第一步 : 實作繼承自 Fragment 的類別
package com.example; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class MyFragment extends Fragment { ...... }
第二步 : 若是想要載入自己設計好的畫面, 在 res/layout 資料夾中新增一個 Layout 的 XML 檔, 例如 fragment_a.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</LinearLayout>
第三步 : 在 onCreateView() 中透過 LayoutInflater 載入
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_a, container, false); return view; }
第四步 : 將訂好的 Fragment 物件放到 Activity 的畫面中
這裡有兩種不同的作法, 第一種是直接用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<fragment android:id="@+id/myFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:name="com.example.MyFragment" />
</LinearLayout>
第二種是在 Activity 的 layout 中不直接放入
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout android:id="@+id/layout_fragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
</LinearLayout>
然後再由 Activity 的程式中將 Fragment 放置於指定 layout 中FragmentManager fm = getFragmentManager();
MyFragment fragment = (MyFragment) fm.findFragmentById(R.id.layout_fragment);
if (fragment==null || ! fragment.isInLayout()) {
fragment = new MyFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.layout_fragment, fragment);
ft.commit();
}
這兩種方式的效果看起來差不多, 不過我個人比較偏愛後者, 原因如下 :1. 前者的方式 fragment 是一開始就嵌死在 Activity 中的, 後者可動態寫在任何事件中, 較靈活。
2. 前者的 fragment 種類無法抽換, 後者可以動態決定要使用哪個 Fragment。
3. 前者至少必須使用 Android API Level 11 以上, 後者有在 Support Library 中可向前相容。
讓我們來認識幾個重要的元件
FragmentManager
故名思義, FragmentManager 是管理 Activity 與 Fragment 互動的類別, 可以在 Activity 中透過 getFragmentManager() 方式取得。最常用的幾個方法
Fragment findFragmentById(int id) :
透過指定 Layout 的 resource id 來取得該 Layout 中存在的 fragment 實體。
FragmentTransaction beginTransaction() :
傳回 FragmentTransaction 物件, 表示要開始一系列 Fragment 與 FragmentManager 間的互動。
FragmentTransaction
一般我們工程師看到 Transaction 這個詞, 就會聯想到資料庫的 Transaction。也就是一次執行多個動作, 然後最後執行送交(commit)後要嘛一次全部執行完畢, 要不然就是中途發生錯誤我們可以回復(rollback)到最初始的資料狀態 , 不會有執行到一半的中間狀態。其實 FragmentTransaction 也差不多是這個意思, 我們可以交代給這個物件一系列的動作請他執行, 只是這些動作相對單純也不需要做資料回復, 所以少了 rollback 這樣的機制。
我們看一下 FragmentTransaction 常執行哪些動作 :
FragmentTransaction add(int containerViewId, Fragment fragment) :
將指定的 fragment 加入指定的 container 中, 這裡的 container 其實指的就是 Layout)。FragmentTransaction remove(Fragment fragment) :
移除指定的 fragment, 此 fragment 內的所有狀態也會一併被刪除, 也就是這個 fragment 已不能再被使用了。如果這個 fragment 已經被加入某個 container 中, 則也會從該 container 中一併被清除。FragmentTransaction detach(Fragment fragment) :
將指定的 fragment 從其所依附的 container 中移除。與 remove 方法不同的是, 該 fragment 的狀態仍會保留, 這個實體可以繼續透過 attach 方式使用。另外要注意的是, 當我們在程式中使用 detach 方式將 fragment 從畫面上拿下來, 也會一併將此 fragment 從 back stack 的階層中移除。
FragmentTransaction attach(Fragment fragment) :
將被 detach 過的 fragment 重新裝回原本的 container 中顯示, 當然也會將此動作加回 back stack 階層中。FragmentTransaction replace(int containerViewId, Fragment fragment) :
將指定的 container 中存在的 fragment 進行 remove() 動作, 然後將指定的 fragment 透過 add() 的方式裝回該 container 中。要注意的是這裡是 remove + add, 而不是 detach + attach, 所以被移除的 fragment 是不能再繼續使用的。
FragmentTransaction addToBackStack(String name) :
將這個 Transaction 所執行的動作在 commit 後加入 back stack 階層中, 也就是當使用者按下返回鍵時, 這一連串的動作都會被復原。FragmentTransaction setCustomAnimations(int enter, int exit) :
當開始執行這個 Transaction 時所使用的動畫效果(animation), enter 與 exit 的數字是指該動畫在 resource 中的 id 編號。int commit() :
送交執行。但其實也不一定就會馬上被執行, 它會先去排隊, 等到主執行緒有空的時候才會執行。說是這樣說啦, 但除非主執行緒卡太多需要長時間的工作 (例如 ImageView 中指定 src 來自於一個 URL 之類的), 不然一般來說當我們呼叫 commit() 時幾乎都是會立刻反應在畫面上。在這邊附上一個範例, 可以做到兩個 Button 按下時切換不同的 Fragment, 有興趣的話可以下載回去看看
SimpleFragmentSample 範例
向前相容
向前相容總是個很讓人玩味的課題, 雖然說 Android 3.0 以後的版本到目前為止(2013年8月)已經佔了 Android 總市佔的 63.1% (資料來源)。但工程師總是會被要求產品要想辦法支援到另外的那30%呀 .....。所以就會發現雖然有愈來愈多的 API 出來, 但大多數的專案都還是得用舊的寫法, 或者必須使用有相容的 API 在開發。
Google 很有善意的提供了很有名的 android-support-v4.jar 可以做到向前相容。但這裡的向前相容只是做到『本質不同, 但效果一樣』。舉例 Fragment 的例子來說, 原生的 library 中所使用的類別是 android.app.Fragment 類別, 但 support library 中所提供的卻是 android.support.v4.app.fragment 類別。這兩個類別所提供的方法幾乎一模一樣, 但兩者間並沒有任何繼承或共用的介面, 所以無法轉型。
在 Fragment 的向前相容部份, 其實我們只要做以下幾個小改變 :
- 原繼承自 android.app.Fragment 的類別, 改成繼承自 android.support.v4.app.Fragment 類別。
- 管理 Activity 的類別, 從原本繼承自 android.app.Activity 類別, 改成繼承自 android.support.v4.app.FragmentActivity 類別。
- 取出 FragmentManager 的方法, 從原本使用 getFragmentManager(), 改成 getSupportFragmentManager()。
- 而回傳的 android.app.FragmentManager 類別, 改成 android.support.v4.app.FragmentManager 類別。
- 原本的 android.app.FragmentTransaction 類別, 改成 android.support.v4.app.FragmentTransaction 類別。
以上除了 1,2,3 需要稍微動手修改之外, 改好後 4,5兩項只需按個 ctrl+shift+o 就可以改完了。一整個方便。
不過經驗上是, 就算 Fragment 可以往前相容了, 還是很可能遇到其它搭配的 API 讓你面臨到可能無法向前相容的窘境。例如 ActionBar .....。
Fragment 的子類別
Fragment 就像是張白紙, 裡頭要塞什麼自己可以決定, 非常自由。但有許多的時候一個 Fragment 可能就只放一個控制項, 而這種情境又非常常出現 (例如一個 Fragment 裡頭只放一個 ListView), 而我們還是得設定好一個 layout 的 xml 檔。然後想辦法透過 id 把 control 取出來塞值, 既花時間又花精神。
所以 Android 官方就提供了幾個子類別, 讓你直接繼承這些子類別就可以使用了, 包含了 :
- ListFragment : 裡頭預設就有 ListView, 並提供方法與事件存取或控制 ListView。
- DialogFragment : 包含了一個 Dialog 物件, 並提供控制 Dialog 的方法。
- PreferenceFragment : 裡頭預設提供幾個方法讓你輕鬆存取 SharedPreference。
- WebViewFragment : 和 ListFragment 的意思差不多, 裡頭就放了一個 WebView。
參考資料
Building a Dynamic UI with Fragments
Fragment
FragmentManager
FragmentTransaction
Best Casinos in Miami, FL - JetBlue Casino Resort
回覆刪除Discover and play at the 서울특별 출장샵 best hotels in Miami, FL with JT 익산 출장마사지 Marriott Miami, an 강릉 출장샵 award-winning hotel and casino, 수원 출장마사지 near Miami International Airport, 진주 출장마사지