簡介
Fragment (簡稱碎片)是 Android 3.0(API 11)提出的。為了相容低版本 support-v4 庫中也開發了一套Fragment API 最低相容到 Android 1.6 的版本。
過去 support-v4 庫是一個 jar 包,從 24.2.0 版本開始,將 support-v4 庫模組化為多個 jar 包。包含 support-fragment、 support-ui、support-media-compat 等。這麼做是為了減少 APK 包大小,專案中需要用哪個模組就引入哪個模組。
// 引入整個 support-v4 庫
compile 'com.android.support:support-v4:24.2.1'
//只引入 support-fragment 庫
compile 'com.android.support:support-fragment:24.2.1'
複製程式碼
因為 support 庫是不斷更新的,因此推薦使用 support 庫中的 android.support.v4.app.Fragment
,而不要用系統自帶的 android.app.Fragment
。如果使用 support 庫的 Fragment,Activity 就必須要繼承 FragmentActivity(AppCompatActivity 是 FragmentActivity 的子類)。
Fragment 的特點
- Fragment 是依賴於 Activity 的,不能獨立存在的。
- 一個 Activity 裡可以有多個 Fragment。
- 一個 Fragment 可以被多個 Activity 重用。
- Fragment 有自己的生命週期,並能接收輸入事件。
- 可以在 Activity 執行時動態地新增或刪除 Fragment。
Fragment 的優勢
- 模組化(Modularity):我們不必把所有程式碼全部寫在 Activity 中,可以把程式碼寫在各自的 Fragment 中。
- 可重用(Reusability):多個 Activity 可以重用一個 Fragment。
- 可適配(Adaptability):根據硬體的螢幕尺寸、螢幕方向,能夠方便地實現不同的佈局,這樣使用者體驗更好。
生命週期
Fragment 與 Activity 生命週期很相似,與 Activity 一樣,Fragment 也有三種狀態:
- Resumed:Fragment 在執行中的 Activity 中可見。
- Paused:另一個 Activity 處於最頂層,但是 Fragment 所在的 Activity 並沒有被完全覆蓋(頂層的 Activity 是半透明的或不佔據整個螢幕)。
- Stoped:Fragment 不可見,可能是它所在的 Activity 處於 stoped 狀態或是 Fragment 被刪除並新增到後退棧中了,此狀態的 Fragment 仍然存在於記憶體中。
Activity 直接影響它所包含的 Fragment 的生命週期,所以對 Activity 的某個生命週期方法的呼叫也會產生對Fragment 相同方法的呼叫。例如:當 Activity 的 onPause() 方法被呼叫時,它所包含的所有的 Fragment 的onPause() 方法都會被呼叫。
Fragment 比 Activity 還要多出幾個生命週期回撥方法,這些額外的方法是為了與 Activity 的互動,如下:
- onAttach()
當 Fragment 被加入到 Activity 時呼叫(在這個方法中可以獲得所在的 Activity)。
- onCreateView()
當 Activity 要得到 Fragment 的 layout 時,呼叫此方法,Fragment 在其中建立自己的 layout (介面)。
- onActivityCreated()
當 Activity 的 onCreated() 方法返回後呼叫此方法。
- onDestroyView()
當 Fragment 的 layout 被銷燬時被呼叫。
- onDetach()
當 Fragment 被從 Activity 中刪掉時被呼叫。
一旦 Activity 進入 resumed 狀態(也就是 running 狀態),你就可以自由地新增和刪除 Fragment 了。因此,只有當 Activity 在 resumed 狀態時,Fragment 的生命週期才能獨立的運轉,其它時候是依賴於 Activity 的生命週期變化的。
使用方式
這裡給出 Fragment 最基本的使用方式。首先,建立繼承 Fragment 的類,名為 BlankFragment:
public class BlankFragment extends Fragment {
private static final String ARG_PARAM = "param_key";
private String mParam;
public BlankFragment() { }
public static BlankFragment newInstance(String param) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM, param);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam = getArguments().getString(ARG_PARAM);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_blank, container, false);
// View 初始化,findViewById() 等操作
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// 初始化資料,載入資料等...
}
}
複製程式碼
靜態新增
通過 xml 的方式新增,缺點是一旦新增就不能在執行時刪除。
<fragment
android:id="@+id/fg_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.jeanboy.text.ui.fragment.BlankFragment" />
複製程式碼
動態新增
執行時新增,這種方式比較靈活,因此建議使用這種方式。
這裡只給出動態新增的方式。首先 Activity 需要有一個容器存放 Fragment,一般是 FrameLayout,因此在 Activity 的佈局檔案中加入 FrameLayout:
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
複製程式碼
然後在 onCreate()
中,通過以下程式碼將 Fragment 新增進Activity中。
getSupportFragmentManager().beginTransaction()
.add(R.id.container, BlankFragment.newInstance("hello world"), "f1")
.commit();
複製程式碼
這裡需要注意幾點:
-
因為我們使用了support庫的Fragment,因此需要使用
getSupportFragmentManager()
獲取 FragmentManager。 -
add()
是對 Fragment 眾多操作中的一種,還有remove()
,replace()
等。第一個引數是根容器的 id(FrameLayout 的 id,即
@id/container
),第二個引數是 Fragment 物件,第三個引數是 Fragment 的 tag 名,指定 tag 的好處是後續我們可以通過:Fragment1 frag = getSupportFragmentManager().findFragmentByTag("f1"); 複製程式碼
從 FragmentManager 中查詢 Fragment 物件。
-
在一次事務中,可以做多個操作,比如同時做
add().remove().replace()
。 -
commit()
操作是非同步的,內部通過mManager.enqueueAction()
加入處理佇列。對應的同步方法為
commitNow()
,commit()
內部會有checkStateLoss()
操作,如果開發人員使用不當(比如commit()
操作在onSaveInstanceState()
之後),可能會丟擲異常。而commitAllowingStateLoss()
方法則是不會丟擲異常版本的commit()
方法,但是儘量使用commit()
,而不要使用commitAllowingStateLoss()
。 -
addToBackStack("fname")
是可選的。FragmentManager 擁有回退棧(BackStack),類似於 Activity 的任務棧,如果新增了該語句,就把該事務加入回退棧,當使用者點選返回按鈕,會回退該事務(回退指的是如果事務是
add(frag1)
,那麼回退操作就是remove(frag1)
);如果沒新增該語句,使用者點選返回按鈕會直接銷燬 Activity。
Fragment 通訊
Fragment 向 Activity 傳遞資料
首先,在 Fragment中 定義介面,並讓 Activity 實現該介面。
public interface OnFragmentCallback {
void onCallback(String value);
}
複製程式碼
在 Fragment 的 onAttach()
中,將引數 Context 強轉為 OnFragmentCallback 物件:
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentCallback) {
callback = (OnFragmentCallback) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentCallback");
}
}
複製程式碼
Activity 向 Fragment 傳遞資料
Activity 向 Fragment 傳遞資料比較簡單,獲取 Fragment 物件,並呼叫 Fragment 的方法即可。比如要將一個字串傳遞給 Fragment,則在 Fragment 中定義方法:
public void setString(String data) {
this.data = data;
}
複製程式碼
並在 Activity 中呼叫 fragment.setString("hello")
即可。
Fragment 之間通訊
由於 Fragment 之間是沒有任何依賴關係的,因此如果要進行 Fragment 之間的通訊,建議通過 Activity 作為中介,不要 Fragment 之間直接通訊。
DialogFragment
DialogFragment 是 Android 3.0 提出的,代替了 Dialog,用於實現對話方塊。它的優點是:即使旋轉螢幕,也能保留對話方塊狀態。
如果要自定義對話方塊樣式,只需要繼承 DialogFragment,並重寫 onCreateView()
,該方法返回對話方塊 UI。這裡我們舉個例子,實現進度條樣式的圓角對話方塊。
public class ProgressDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//消除Title區域
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
//將背景變為透明
getDialog().getWindow()
.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
//點選外部不可取消
setCancelable(false);
View root = inflater.inflate(R.layout.fragment_progress_dialog, container);
return root;
}
public static ProgressDialogFragment newInstance() {
return new ProgressDialogFragment();
}
}
複製程式碼
然後通過下面程式碼顯示對話方塊:
ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "tag");//顯示對話方塊
fragment.dismiss();//關閉對話方塊
複製程式碼
我的公眾號
歡迎關注我的公眾號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。
如果你有什麼疑問或者問題,可以 點選這裡 提交 issue,也可以發郵件給我 jeanboy@foxmail.com。
來一起交流學習,群裡有很多大牛和學習資料,相信一定能幫助到你!