說到管理Activity中的Fragment食零,自然就要重點(diǎn)說一下FragmentManager困乒,之前已經(jīng)說過了,getFragmentManager()獲取到的FragmentManager支持原生的Fragment贰谣,而getSupportFragmentManager()支持的是v4包的Fragment娜搂。
獲取Fragment的方法
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_study1"
android:name="com.example.study.StudyFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<fragment
android:id="@+id/fragment_study2"
android:name="com.example.study.StudyFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<fragment
android:tag="fragment_second"
android:name="com.example.study.SecondFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
獲取Fragment的三種方式
fragmentManager = getSupportFragmentManager();
// 通過id查找對應(yīng)的Fragment實(shí)例
fragment1 = (StudyFragment) fragmentManager.findFragmentById(R.id.fragment_study1);
// 通過tag找到對應(yīng)的Fragment,在動態(tài)加載和靜態(tài)加載中都可以使用
secondFragment = (SecondFragment) fragmentManager.findFragmentByTag("fragment_second");
// 獲取添加到FragmentManager的所有Fragment吱抚,通過index訪問百宇,0代表最早添加的Fragment
fragmentManager.getFragments()
FragmentManager是如何管理Fragment的
每次我們對Fragment的操作都需要通過FragmentTransaction,到了這里估計很多博客都已經(jīng)說到了FragmentTransaction里面的幾個操作方法秘豹,雖然在下也是要說的携御,順便說一下Fragment管理的設(shè)計吧,不看不知道既绕,看了才知道可牛逼哄哄了啄刹。
我們知道,沒有FragmenActivity就沒有Fragment凄贩。
那么為什么我們必須要繼承自FragmentActivity呢誓军,這就是Fragment設(shè)計上的便利了,開發(fā)者希望我們在使用Fragment的時候只需要關(guān)注對Fragment的操作怎炊,而Fragment的管理則交由FragmentActivity的FragmentManager來實(shí)現(xiàn)谭企。
在FragmentAactivity這個類下面,當(dāng)我們調(diào)用getSupportManager的時候
public class FragmentActivity extends BaseFragmentActivityJB implements
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompatApi23.RequestPermissionsRequestCodeValidator {
···
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
···
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
···
}
我們可以看到FragmentActivity里面有一個FragmentController评肆,這個FragmentController定義了所有對Fragment的管理操作债查,包括我們的Activity在onCreate,onResume瓜挽,onDestroy等各種生命周期或回調(diào)對Fragment的影響盹廷,都是由這個類來控制的。
public class FragmentController {
private final FragmentHostCallback<?> mHost;
/**
* Returns a {@link FragmentController}.
*/
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks);
}
/**
* Returns a {@link FragmentManager} for this controller.
*/
public FragmentManager getSupportFragmentManager() {
//獲取到FragmentManager對象
return mHost.getFragmentManagerImpl();
}
FragmentHostCallback是一個抽象類久橙,負(fù)責(zé)調(diào)用各種各樣的回調(diào)俄占,這樣的話,當(dāng)Avtivity的狀態(tài)淆衷,生命周期發(fā)生改變的時候缸榄,就可以通過這個回調(diào)接口進(jìn)行統(tǒng)一管理,在上面提到的HostCallbacks是FragmentActivity里面的一個繼承FragmentHostCallback的內(nèi)部類祝拯。下面我們來看看FragmentHostCallback的默認(rèn)實(shí)現(xiàn)
public abstract class FragmentHostCallback<E> extends FragmentContainer {
private final Activity mActivity;
···
// 實(shí)例化FragmentManager對象甚带,F(xiàn)ragmentManagerImpl是繼承自FragmentManager抽象類的她肯,對FragmentManager的各種方法提供具體實(shí)現(xiàn)
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
···
}
FragmentManagerImpl里面的具體實(shí)現(xiàn)就是有關(guān)Fragment是如何運(yùn)行的,各種各樣的生命周期鹰贵,判斷Fragment的不同狀態(tài)晴氨,切換狀態(tài),Transaction只是用作記錄對Fragment的操作記錄碉输,最終調(diào)用commit的時候籽前,實(shí)際上調(diào)用的還是FragmentManagerImpl的方法
// FragmentManager 的實(shí)現(xiàn)類
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
···
@Override
public FragmentTransaction beginTransaction() {
// 每次的FragmentTransaction都是獨(dú)立的
return new BackStackRecord(this);
}
···
}
// Transaction的實(shí)現(xiàn)類
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
// 初始化的時候傳入FragmentManagerImpl 的實(shí)例
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
@Override
public int commit() {
//返回棧id,要是不添加進(jìn)棧敷钾,返回-1
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
// 提交以后無法再次提交
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
}
mCommitted = true;
//是否要添加到回退棧
if (mAddToBackStack) {
// 在回退棧中分配棧ID
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
//執(zhí)行這個Transaction
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
}
FragmentManagerImpl的部分實(shí)現(xiàn)
/**
* 添加一個操作到待操作隊(duì)列中
*
* @param action 添加的操作
* @param allowStateLoss 是否允許丟失狀態(tài)信息
* @throws 如果Activity已經(jīng)銷毀了拋出IllegalStateException異常
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
// 檢查狀態(tài)是否丟失枝哄,默認(rèn)的commit實(shí)現(xiàn)會執(zhí)行這一步
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
// 添加到待操作隊(duì)列中
mPendingActions.add(action);
scheduleCommit(); //執(zhí)行Commit操作
}
}
private void scheduleCommit() {
synchronized (this) {
// 是否有延時的事務(wù)
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
// 是否有待執(zhí)行的事務(wù)
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
// 執(zhí)行這個Runnable
mHost.getHandler().post(mExecCommit);
}
}
}
//通過handler調(diào)用,在主線程運(yùn)行
public boolean execPendingActions() {
//收集和執(zhí)行延時的操作闰非,這種延時是因?yàn)檫€沒準(zhǔn)備好膘格??
ensureExecReady(true);
boolean didSomething = false;
//根據(jù)事務(wù)對象生成待執(zhí)行的操作财松,這個事務(wù)對象是FragmentTransaction的實(shí)現(xiàn)
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
//優(yōu)化執(zhí)行事務(wù),里面的處理邏輯相當(dāng)復(fù)雜
optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
} finally {
//清空緩存事務(wù)隊(duì)列
cleanupExec();
}
didSomething = true;
}
//判斷FragmentList是否需要延時纱控,進(jìn)而調(diào)用moveToState修改Fragment的狀態(tài)辆毡,根據(jù)狀態(tài)來觸發(fā)Fragment的不同生命周期
doPendingDeferredStart();
return didSomething;
}
上面的就是我們通過getSupportFragmentManager()獲取到FragmentManager,然后再開啟事務(wù)甜害,提交事務(wù)所經(jīng)歷的代碼流程舶掖。控制Fragment的生命周期的回調(diào)尔店,通過FragmentManager的moveToState方法眨攘。
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)
關(guān)于Fragment的生命周期,要改篇再學(xué)習(xí)∠荩現(xiàn)在繼續(xù)來學(xué)習(xí)一下FragmentTransaction的一些常用方法
#將一個fragment實(shí)例添加到Activity里面指定id的容器中
add(Fragment fragment, String tag)
add(int containerViewId, Fragment fragment)
add(int containerViewId, Fragment fragment, String tag);
#將一個fragment實(shí)例從FragmentManager的FragmentList中移除
remove(Fragment fragment);
#只控制Fragment的隱藏
hide(Fragment fragment)
#只控制Fragment的顯示
show(Fragment fragment)
#清除視圖鲫售,從containerid指定的Added列表移除,F(xiàn)ragmentList依然保留
detach(Fragment fragment)
#創(chuàng)建視圖该肴,添加到containerid指定的Added列表情竹,F(xiàn)ragmentList依然保留
attach(Fragment fragment)
#替換containerViewId中的fragment,它會把containerViewId中所有fragment刪除匀哄,然后添加當(dāng)前的fragment
replace(int containerViewId, Fragment fragment)
replace(int containerViewId, Fragment fragment, String tag)
FragmentTransaction的用例
之前的例子都已經(jīng)對FragmentTransaction的用法有過一些介紹了
//添加Fragment到FragmentList中
private void addFragment(Fragment fragment, String tag){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.fragment_container,fragment,tag);
transaction.commit();
}
// 清空fragmentList的所有Fragment秦效,替換成新的Fragment,注意Fragment里面的坑
private void replaceFragment(Fragment fragment, String tag){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragment_container,fragment,tag);
transaction.commit();
}
//移除指定的Fragment
private void removeFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.remove(fragment);
transaction.commit();
}
//把Fragment設(shè)置成顯示狀態(tài)涎嚼,但是并沒有添加到FragmentList中
private void showFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.show(fragment);
transaction.commit();
}
//把Fragment設(shè)置成顯示狀態(tài)阱州,但是并沒有添加到FragmentList中
private void hideFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.hide(fragment);
transaction.commit();
}
// 效果和show相近,創(chuàng)建視圖法梯,添加到containerid指定的Added列表苔货,F(xiàn)ragmentList依然保留,但是會引起生命周期的變化
private void attachFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.attach(fragment);
transaction.commit();
}
// 效果和hide相近,清除視圖蒲赂,從containerid指定的Added列表移除阱冶,F(xiàn)ragmentList依然保留,但是會引起生命周期的變化
private void detachFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.detach(fragment);
transaction.commit();
}
雖然新的Android版本中滥嘴,F(xiàn)ragment在replace的時候不再重新實(shí)例化了(據(jù)說在舊的版本會出現(xiàn)這個問題木蹬,目前我使用的API 25 ),但是我們還是建議采用show/hide的方式來控制Fragment的顯示和隱藏若皱。還有一點(diǎn)需要注意的是镊叁,attach和detach會改變Fragment在Added列表中的順序,從而也會改變Fragment顯示的順序走触。
FragmentTransaction的事務(wù)回退棧
上面介紹了常用的用法晦譬,接下來要介紹的是FragmentTransaction的事務(wù)回滾,FragmentTransaction把事務(wù)添加到回退棧中,只需要在調(diào)用 transaction.commit()之前互广,調(diào)用以下代碼
//tag標(biāo)記這個用于標(biāo)記這個事務(wù)
transaction.addToBackStack(String tag);
加入回退棧的時候敛腌,調(diào)用commit方法會返回一個index,作為事務(wù)的id惫皱,否則返回-1像樊。使用popBackStack方法進(jìn)行回退,彈出回退棧旅敷。
//默認(rèn)將最上層的操作彈出回退棧
popBackStack()
//使用commit返回的事務(wù)id
popBackStack(int id, int flags);
//使用加入回退棧時的tag值
popBackStack(String name, int flags);
flag的取值生棍,當(dāng)取值0時,表示除了指定這一層之上的所有層都退出棧媳谁,指定的這一層為棧頂層涂滴;當(dāng)取值POP_BACK_STACK_INCLUSIVE時,表示連著指定的這一層一起退出棧晴音;
需要注意的是柔纵,使用popBackStack()來彈出棧內(nèi)容的話,調(diào)用該方法后會將事物操作插入到FragmentManager的操作隊(duì)列段多,只有當(dāng)輪詢到該事物時才能執(zhí)行首量。如果想立即執(zhí)行事物的話,需要使用下面幾個對應(yīng)的方法:
popBackStackImmediate()
popBackStackImmediate(String tag)
popBackStackImmediate(String tag, int flag)
popBackStackImmediate(int id, int flag)
在FragmentActivity的onBackPressed()方法內(nèi)可以看到进苍,當(dāng)popBackStackImmediate返回true的情況加缘,則不會執(zhí)行Activity的onBackPressed()方法
@Override
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
super.onBackPressed();
}
}
2、回退棧(back stack)狀態(tài)改變監(jiān)聽
FragmentManager還為我們提供了監(jiān)控回退棧狀態(tài)改變的方法:
addOnBackStackChangedListener(listener);//添加監(jiān)聽器
removeOnBackStackChangedListener(listener);//移除監(jiān)聽器
通過添加監(jiān)聽器觉啊,就可以在回退棧內(nèi)容改變時拣宏,及時收到通知;
(1)杠人、OnCreate()中:
為fragmentManger添加一個監(jiān)聽器:
FragmentManager manager = getSupportFragmentManager();
listener = new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
// TODO Auto-generated method stub
Log.d("qijian","backstack changed");
}
};
manager.addOnBackStackChangedListener(listener);
(2)勋乾、當(dāng)onDestory()中將監(jiān)聽器remove掉:
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
FragmentManager manager = getSupportFragmentManager();
manager.removeOnBackStackChangedListener(listener);
}
大家一定要注意宋下,不管是這里的回退棧的監(jiān)聽還是其它的監(jiān)聽器,在頁面對應(yīng)的銷毀時辑莫,都要記得remove掉学歧,不然會造成頁面不釋放,這也是造成OOM的問題之一各吨。