編輯:關於Android編程
前一篇文章總結了Fragment 的基本概念和基本的用法,相信大家也能夠掌握一些知識了,但是對於一些操作可能還是不知其所以然,說實話曾經很長一段時間為也是暈乎乎的,後來才慢慢重視去學習了解,才略知一二,遂分享之。
應用Fragment的過程中涉及到的關鍵類主要有:FragmentManager和、FragmentManagerImpl、FragmentTransaction和BackStackRecord等。
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 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();
}
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();
}
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();
}
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();
}
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;
}
}
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 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 firstOutFragments,
SparseArray 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 nameOverrides = new ArrayMap();
public View enteringEpicenterView;
public View nonExistentView;
}
}
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,顯示下一個
}
}
與數據庫事務類似,Fragement事務也是支持類似回滾的功能的。
具體用法就是我們在commit之前,先使用addToBackStack將對應的FragmentTransaction對象添加到回退棧
fragmentManager=getFragmentManager(); fragmentManager.beginTransaction().add(viewGroupId,fragment,tag)
.addToBackStack(tag).commit();
特別地當你remove一個fragment的時候,如果commit()之前沒有調用 addToBackStack(),那個fragment將會是destroyed;如果調用了addToBackStack(),這個fragment 會是stopped,可以通過返回鍵來恢復
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)
調用commit()方法並不能立即執行transaction中包含的操作,commit()方法只是把transaction加入Activity的Main線程隊列中。但是,如果覺得有必要的話,可以調用executePendingTransactions()方法來立即執行commit()提供的transaction。然而這樣做通常是沒有必要的,除非這個transaction被其他線程依賴。還有你只能在Activity存儲它的狀態(當用戶要離開Activity時)之前調用commit(),如果在存儲狀態之後調用commit(),將會拋出一個異常。這是因為當Activity再次被恢復時commit之後的狀態將丟失。假如丟失也沒關系,我們可以使用commitAllowingStateLoss()方法。
通過添加監聽器,就可以在Back Stack狀態改變時,及時監聽到並作相應操作。
abstract void addOnBackStackChangedListener(listener);//添加監聽器
abstract void removeOnBackStackChangedListener(listener);//移除監聽器
在Acitivty裡為FragmentManager添加一個監聽器,一般是在onCreate方法裡
private class BackStackChangedListener implements FragmentManager.OnBackStackChangedListener{
@Override
public void onBackStackChanged() {
}
}
BackStackChangedListener listener=new BackStackChangedListener();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.addOnBackStackChangedListener(listener );
在Acitivty裡為FragmentManager添加一個監聽器,一般是在onDestory方法裡
fragmentManager.removeOnBackStackChangedListener(listener);
一般來說,無論是何種監聽,在Activity或者界面銷毀時,都要記得remove掉,否則會潛藏著OOM的隱患。
1. 產品概述 友盟社會化組件,可以讓移動應用快速具備社會化分享、登錄、評論、喜歡等功能,並提供實時、全面的社會化數據統計分析服務。 指南將會手把手教你使用社
本文是針對AndBase框架學習整理的第一篇筆記,想要了解AndBase框架的朋友可以閱讀本文,大家共同學習。1.使用AndBase實現多功能標題欄AndBase框架內部
本文實例為大家分享了Android分類側滑菜單的制作方法,供大家參考,具體內容如下classificmenuActivity.java代碼:package com.sis
public class TvControlActivity extends Activity { private TvControlActivity tvCont