引言
前一篇文章总结了Fragment 的基本概念和基本的用法,相信大家也能够掌握一些知识了,但是对于一些操作可能还是不知其所以然,说实话曾经很长一段时间为也是晕乎乎的,后来才慢慢重视去学习了解,才略知一二,遂分享之。
一、管理Fragement所涉及到的关键类
应用Fragment的过程中涉及到的关键类主要有:FragmentManager和、FragmentManagerImpl、FragmentTransaction和BackStackRecord等。
二、Fragment操作原理详述
1、FragmentManager和FragmentManagerImpl
FragmentManager是一个抽象类,定了获取指定Fragement对象findFragmentById(id)或findFragmentByTag(tag)、从后台栈中弹出(模拟用户按下BACK键)popBackStack(),注册监听addOnBackStackChangeListner和移除监听removeOnBackStackChangedListener以及开启事务的方法beginTransaction,但直接与Activity交互的并承担实际Fragment管理工作的是FragmentManagerImpl。他们的部分源码结构【FragmentManager.java (frameworks\base\core\java\android\app)
】如下:
public abstract class FragmentManager {
public interface BackStackEntry {
public int getId();
public String getName();
...
}
/**
* Interface to watch for changes to the back stack.
*/
public interface OnBackStackChangedListener {
/**
* Called whenever the contents of the back stack change.
*/
public void onBackStackChanged();
}
public abstract FragmentTransaction beginTransaction();
public abstract Fragment findFragmentById(int id);
public abstract Fragment findFragmentByTag(String tag);
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
public abstract void popBackStack();
public abstract void popBackStack(String name, int flags);
public abstract void popBackStack(int id, int flags);
public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
public abstract Fragment getFragment(Bundle bundle, String key);
public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
}
/**
* Container for fragments associated with an activity.
*/
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
ArrayList<BackStackRecord> mBackStackIndices;//BackStackRecord实现了FragmentTransaction
static class AnimateOnHWLayerIfNeededListener implements Animator.AnimatorListener {
private boolean mShouldRunOnHWLayer = false;
private View mView;
public AnimateOnHWLayerIfNeededListener(final View v) {
if (v == null) {
return;
}
mView = v;
}
@Override
public void onAnimationStart(Animator animation) {
mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation);
if (mShouldRunOnHWLayer) {
mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (mShouldRunOnHWLayer) {
mView.setLayerType(View.LAYER_TYPE_NONE, null);
}
mView = null;
animation.removeListener(this);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
}
...
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
@Override
public void popBackStack() {
enqueueAction(new Runnable() {
@Override public void run() {
popBackStackState(mHost.getHandler(), null, -1, 0);
}
}, false);
}
@Override
public void popBackStack(final String name, final int flags) {
enqueueAction(new Runnable() {
@Override public void run() {
popBackStackState(mHost.getHandler(), name, -1, flags);
}
}, false);
}
@Override
public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
if (mBackStackChangeListeners == null) {
mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
}
mBackStackChangeListeners.add(listener);
}
@Override
public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
if (mBackStackChangeListeners != null) {
mBackStackChangeListeners.remove(listener);
}
}
Animator loadAnimator(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animator animObj = fragment.onCreateAnimator(transit, enter,
fragment.mNextAnim);
if (animObj != null) {
return animObj;
}
if (fragment.mNextAnim != 0) {
Animator anim = AnimatorInflater.loadAnimator(mHost.getContext(), fragment.mNextAnim);
if (anim != null) {
return anim;
}
}
if (transit == 0) {
return null;
}
int styleIndex = transitToStyleIndex(transit, enter);
if (styleIndex < 0) {
return null;
}
if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
transitionStyle = mHost.onGetWindowAnimations();
}
if (transitionStyle == 0) {
return null;
}
TypedArray attrs = mHost.getContext().obtainStyledAttributes(transitionStyle,
com.android.internal.R.styleable.FragmentAnimation);
int anim = attrs.getResourceId(styleIndex, 0);
attrs.recycle();
if (anim == 0) {
return null;
}
return AnimatorInflater.loadAnimator(mHost.getContext(), anim);
}
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
if (DEBUG) Log.v(TAG, "add: " + fragment);
makeActive(fragment);
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
final boolean inactive = !fragment.isInBackStack();
if (!fragment.mDetached || inactive) {
if (false) {
// Would be nice to catch a bad remove here, but we need
// time to test this to make sure we aren‘t crashes cases
// where it is not a problem.
if (!mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
}
if (mAdded != null) {
mAdded.remove(fragment);
}
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
fragment.mAdded = false;
fragment.mRemoving = true;
moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
transition, transitionStyle, false);
}
}
public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "hide: " + fragment);
if (!fragment.mHidden) {
// If there is a showing or hidding animation, stop it immediately.
if (fragment.mAnimatingShowHide != null) {
fragment.mAnimatingShowHide.end();
}
fragment.mHidden = true;
if (fragment.mView != null) {
Animator anim = loadAnimator(fragment, transition, false,
transitionStyle);
if (anim != null) {
fragment.mAnimatingShowHide = anim;
anim.setTarget(fragment.mView);
// Delay the actual hide operation until the animation finishes, otherwise
// the fragment will just immediately disappear
final Fragment finalFragment = fragment;
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finalFragment.mAnimatingShowHide = null;
if (finalFragment.mView != null) {
finalFragment.mView.setVisibility(View.GONE);
}
}
});
setHWLayerAnimListenerIfAlpha(finalFragment.mView, anim);
anim.start();
} else {
fragment.mView.setVisibility(View.GONE);
}
}
if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
fragment.onHiddenChanged(true);
}
}
public void showFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "show: " + fragment);
if (fragment.mHidden) {
// If there is a showing or hidding animation, stop it immediately.
if (fragment.mAnimatingShowHide != null) {
fragment.mAnimatingShowHide.end();
}
fragment.mHidden = false;
if (fragment.mView != null) {
Animator anim = loadAnimator(fragment, transition, true,
transitionStyle);
if (anim != null) {
fragment.mAnimatingShowHide = anim;
anim.setTarget(fragment.mView);
setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
final Fragment finalFragment = fragment;
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finalFragment.mAnimatingShowHide = null;
}
});
anim.start();
}
fragment.mView.setVisibility(View.VISIBLE);
}
if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
fragment.onHiddenChanged(false);
}
}
public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "detach: " + fragment);
if (!fragment.mDetached) {
fragment.mDetached = true;
if (fragment.mAdded) {
// We are not already in back stack, so need to remove the fragment.
if (mAdded != null) {
if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
mAdded.remove(fragment);
}
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
fragment.mAdded = false;
moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
}
}
}
public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "attach: " + fragment);
if (fragment.mDetached) {
fragment.mDetached = false;
if (!fragment.mAdded) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
mAdded.add(fragment);
fragment.mAdded = true;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
moveToState(fragment, mCurState, transition, transitionStyle, false);
}
}
}
public Fragment findFragmentById(int id) {
if (mAdded != null) {
// First look through added fragments.
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
if (f != null && f.mFragmentId == id) {
return f;
}
}
}
if (mActive != null) {
// Now for any known fragment.
for (int i=mActive.size()-1; i>=0; i--) {
Fragment f = mActive.get(i);
if (f != null && f.mFragmentId == id) {
return f;
}
}
}
return null;
}
public Fragment findFragmentByTag(String tag) {
if (mAdded != null && tag != null) {
// First look through added fragments.
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
if (f != null && tag.equals(f.mTag)) {
return f;
}
}
}
if (mActive != null && tag != null) {
// Now for any known fragment.
for (int i=mActive.size()-1; i>=0; i--) {
Fragment f = mActive.get(i);
if (f != null && tag.equals(f.mTag)) {
return f;
}
}
}
return null;
}
void reportBackStackChanged() {
if (mBackStackChangeListeners != null) {
for (int i=0; i<mBackStackChangeListeners.size(); i++) {
mBackStackChangeListeners.get(i).onBackStackChanged();
}
}
}
void addBackStackState(BackStackRecord state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
reportBackStackChanged();
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (!"fragment".equals(name)) {
return null;
}
String fname = attrs.getAttributeValue(null, "class");
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
if (fname == null) {
fname = a.getString(com.android.internal.R.styleable.Fragment_name);
}
int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);
String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
a.recycle();
int containerId = parent != null ? parent.getId() : 0;
if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
throw new IllegalArgumentException(attrs.getPositionDescription()
+ ": Must specify unique android:id, android:tag, or have a parent with"
+ " an id for " + fname);
}
// If we restored from a previous state, we may already have
// instantiated this fragment from the state and should use
// that instance instead of making a new one.
Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
if (fragment == null && tag != null) {
fragment = findFragmentByTag(tag);
}
if (fragment == null && containerId != View.NO_ID) {
fragment = findFragmentById(containerId);
}
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
+ Integer.toHexString(id) + " fname=" + fname
+ " existing=" + fragment);
if (fragment == null) {
fragment = Fragment.instantiate(context, fname);
fragment.mFromLayout = true;
fragment.mFragmentId = id != 0 ? id : containerId;
fragment.mContainerId = containerId;
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
fragment.mHost = mHost;
fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
addFragment(fragment, true);
} else if (fragment.mInLayout) {
// A fragment already exists and it is not one we restored from
// previous state.
throw new IllegalArgumentException(attrs.getPositionDescription()
+ ": Duplicate id 0x" + Integer.toHexString(id)
+ ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
+ " with another fragment for " + fname);
} else {
// This fragment was retained from a previous instance; get it
// going now.
fragment.mInLayout = true;
// If this fragment is newly instantiated (either right now, or
// from last saved state), then give it the attributes to
// initialize itself.
if (!fragment.mRetaining) {
fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
}
}
// If we haven‘t finished entering the CREATED state ourselves yet,
// push the inflated child fragment along.
if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
moveToState(fragment, Fragment.CREATED, 0, 0, false);
} else {
moveToState(fragment);
}
if (fragment.mView == null) {
throw new IllegalStateException("Fragment " + fname
+ " did not create a view.");
}
if (id != 0) {
fragment.mView.setId(id);
}
if (fragment.mView.getTag() == null) {
fragment.mView.setTag(tag);
}
return fragment.mView;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
LayoutInflater.Factory2 getLayoutInflaterFactory() {
return this;
}
}
2、FragmentTransaction和BackStackRecord
Android对于Fragment的操作管理,不是针对于某一次的操作,而是类似于Git记录的是一次改变或者像数据库的事务一般,记录的是一系列的add、replace、remove操作集合(这些add等操作都最后都会封装到一个Op对象里,Op对象可以看成一个双向链表,记录了前一个操作和后一个操作,比如说我们add了N个MainFragment后,这N个操作会由系统封装成N个Op并存到这个对应的BackStackRecord),FragmentTransaction和BackStackRecord(实际是一个实现了FragmentTransaction的类)则是用于管理Fragment事务的业务类,部分源码结构如下:
final class BackStackState implements Parcelable {
public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
int numRemoved = 0;
BackStackRecord.Op op = bse.mHead;
while (op != null) {
if (op.removed != null) {
numRemoved += op.removed.size();
}
op = op.next;
}
mOps = new int[bse.mNumOp * 7 + numRemoved];
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
}
op = bse.mHead;
int pos = 0;
while (op != null) {
mOps[pos++] = op.cmd;
mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
mOps[pos++] = op.enterAnim;
mOps[pos++] = op.exitAnim;
mOps[pos++] = op.popEnterAnim;
mOps[pos++] = op.popExitAnim;
if (op.removed != null) {
final int N = op.removed.size();
mOps[pos++] = N;
for (int i = 0; i < N; i++) {
mOps[pos++] = op.removed.get(i).mIndex;
}
} else {
mOps[pos++] = 0;
}
op = op.next;
}
mTransition = bse.mTransition;
mTransitionStyle = bse.mTransitionStyle;
mName = bse.mName;
mIndex = bse.mIndex;
mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
mSharedElementSourceNames = bse.mSharedElementSourceNames;
mSharedElementTargetNames = bse.mSharedElementTargetNames;
}
public BackStackState(Parcel in) {
mOps = in.createIntArray();
mTransition = in.readInt();
mTransitionStyle = in.readInt();
mName = in.readString();
mIndex = in.readInt();
mBreadCrumbTitleRes = in.readInt();
mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mBreadCrumbShortTitleRes = in.readInt();
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mSharedElementSourceNames = in.createStringArrayList();
mSharedElementTargetNames = in.createStringArrayList();
}
}
/**
* @hide Entry of an operation on the fragment back stack.
*/
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, Runnable {
static final String TAG = FragmentManagerImpl.TAG;
final FragmentManagerImpl mManager;
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
Op mHead;
Op mTail;
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
void addOp(Op op) {
if (mHead == null) {
mHead = mTail = op;
} else {
op.prev = mTail;
mTail.next = op;
mTail = op;
}
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
mNumOp++;
}
public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can‘t change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can‘t change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
return replace(containerViewId, fragment, null);
}
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}
public FragmentTransaction remove(Fragment fragment) {
Op op = new Op();
op.cmd = OP_REMOVE;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction hide(Fragment fragment) {
Op op = new Op();
op.cmd = OP_HIDE;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction show(Fragment fragment) {
Op op = new Op();
op.cmd = OP_SHOW;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction detach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_DETACH;
op.fragment = fragment;
addOp(op);
return this;
}
public FragmentTransaction attach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_ATTACH;
op.fragment = fragment;
addOp(op);
return this;
}
public int commit() {
return commitInternal(false);
}
private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments, boolean isBack) {
TransitionState state = new TransitionState();
// Adding a non-existent target view makes sure that the transitions don‘t target
// any views by default. They‘ll only target the views we tell add. If we don‘t
// add any, then no views will be targeted.
state.nonExistentView = new View(mManager.mHost.getContext());
// Go over all leaving fragments.
for (int i = 0; i < firstOutFragments.size(); i++) {
int containerId = firstOutFragments.keyAt(i);
configureTransitions(containerId, state, isBack, firstOutFragments,
lastInFragments);
}
// Now go over all entering fragments that didn‘t have a leaving fragment.
for (int i = 0; i < lastInFragments.size(); i++) {
int containerId = lastInFragments.keyAt(i);
if (firstOutFragments.get(containerId) == null) {
configureTransitions(containerId, state, isBack, firstOutFragments,
lastInFragments);
}
}
return state;
}
public class TransitionState {
public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
public View enteringEpicenterView;
public View nonExistentView;
}
}
3、Fragment的add、replace、remove等操作小结
Fragment的add、replace操作这些都是针对栈顶的Fragment,其中每一次add、replace之后,这些操作都会封装成Op对象并保存到BackStackRecord里,也就说这些操作并未真正的起作用,还得把这些操作集合commit(作用类似数据库事务的commit)。在成功commit之后FragmentManager才会将此次所有Op放到主线程中去按顺序执行(主程序去调用BackStackRecord的run方法)。
- FragmentTransaction的add操作的管理类似一个队列的,在此队列中,根据add进去的先后顺序形成了一个”链表“
- 在同一个容器内多次执行add操作,显示的总是最后一次add 的Fragment,但其他 Fragment 依然是存在于容器内的
- remove操作其实相当于把指定的Fragment从“链表”中移除,如果移除的是顶层的Fragment那么显示的自然是次顶层的Fragment;若移除的是中间层的Fragment,那么显示的依然是原来的顶层Fragment。
- attach和detach:使用attach之后Fragment对象的isAdded()返回的值是true,使用detach之后Fragment对象的isAdded()返回的值是false,
- hide和show操作其作用就如字面意思一样,但show操作的不是顶层Fragment是无法显示出来的,同样的如果我们hide顶层Fragment则自动显示次顶层。
- replace操作,原本按照官方的描述是先把容器内的Fragment清除掉,再添加新的Fragment,但是在测试过程中发现并没有清除掉,或许是个bug吧。而且在实际使用replace来切换页面,每次切换的时候,Fragment都需要重新实例化,重新加载一边数据,很明显非常消耗资源和性能的。普遍采取的替代方案之一:切换时hide,add另一个Fragement;当再次返回的时候,在hide当前的,show之前被hide的Fragment。(好处之一就是不用重复实例化Fragment)
private void chooseFragement(Fragment currFragement, Fragment targetFragment) {
if (!to.isAdded()) { /*先判断是否被add过*/
transaction.hide(currFragement).add(R.id.id_content_frame, Fragment).commit(); // 隐藏当前的fragment,add下一个到Activity中
} else {
transaction.hide(currFragement).show(Fragment).commit(); // 隐藏当前的fragment,显示下一个
}
}
4、FragmentTransaction事务相关
与数据库事务类似,Fragement事务也是支持类似回滚的功能的。
4.1、调用FragmentTransaction对象的addToBackStack将事务添加到所谓的Back Stack(此栈由Activity管理),当我们按返回键时可以返回到此FragmentTransaction提交之前对应的Fragment状态。
具体用法就是我们在commit之前,先使用addToBackStack将对应的FragmentTransaction对象添加到回退栈
fragmentManager=getFragmentManager(); fragmentManager.beginTransaction().add(viewGroupId,fragment,tag)
.addToBackStack(tag).commit();
特别地当你remove一个fragment的时候,如果commit()之前没有调用 addToBackStack(),那个fragment将会是destroyed;如果调用了addToBackStack(),这个fragment 会是stopped,可以通过返回键来恢复
4.2、FragmentManager对象的popBackStack将指定的操作弹出Back Stack
popBackStack的默认就是将最上层的操作弹出(模拟用户按下返回键),当栈中有多层时,我们也可以通过id或tag标识来指定弹出到的操作所在层。值得注意的是popBackStack针对的是一次操作集合(或者类似Git里的修改),比如说现在一个容器内add了Fragement1并添加至Back Stack再commit,再接着add Fragment2、Fragment3也添加至Back Stack再commit,最后我们执行popBackStack,那么容器此时的状态就是回退到了刚刚add Fragment1并commit成功之后的状态。(如果add 的顺序是Fragment2–>Fragment3,回退时则是Fragment3–>Fragment2)
/*
*id: 当提交变更时transaction.commit()的返回值。
*name: transaction.addToBackStack(String tag)中的tag值;
*flags:有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
当取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层; 当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈
*/
abstract void popBackStack();
abstract void popBackStack(String name, int flags)
abstract void popBackStack(int id, int flags)
这个函数是异步的:它将弹出栈的请求加入队列,但是这个动作直到应用回到事件循环才会执行。所以使用popBackStack()来弹出栈内容的话,调用该方法后会将事物操作插入到FragmentManager的操作队列,只有当轮询到该事物时才能执行。如果想立即执行事物的话,需要使用下面几个对应的方法:
popBackStackImmediate()
popBackStackImmediate(String tag)
popBackStackImmediate(String tag, int flag)
popBackStackImmediate(int id, int flag)
4.3、关于commit
调用commit()方法并不能立即执行transaction中包含的操作,commit()方法只是把transaction加入Activity的Main线程队列中。但是,如果觉得有必要的话,可以调用executePendingTransactions()方法来立即执行commit()提供的transaction。然而这样做通常是没有必要的,除非这个transaction被其他线程依赖。还有你只能在Activity存储它的状态(当用户要离开Activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常。这是因为当Activity再次被恢复时commit之后的状态将丢失。假如丢失也没关系,我们可以使用commitAllowingStateLoss()方法。
4.4、关于Back Stack状态的监听
通过添加监听器,就可以在Back Stack状态改变时,及时监听到并作相应操作。
abstract void addOnBackStackChangedListener(listener);//添加监听器
abstract void removeOnBackStackChangedListener(listener);//移除监听器
4.4.1添加监听
在Acitivty里为FragmentManager添加一个监听器,一般是在onCreate方法里
private class BackStackChangedListener implements FragmentManager.OnBackStackChangedListener{
@Override
public void onBackStackChanged() {
}
}
BackStackChangedListener listener=new BackStackChangedListener();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.addOnBackStackChangedListener(listener );
4.4.2移除监听
在Acitivty里为FragmentManager添加一个监听器,一般是在onDestory方法里
fragmentManager.removeOnBackStackChangedListener(listener);
一般来说,无论是何种监听,在Activity或者界面销毁时,都要记得remove掉,否则会潜藏着OOM的隐患。