Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android進階——Fragment詳解之操作原理(三)

Android進階——Fragment詳解之操作原理(三)

編輯:關於Android編程

引言

前一篇文章總結了Fragment 的基本概念和基本的用法,相信大家也能夠掌握一些知識了,但是對於一些操作可能還是不知其所以然,說實話曾經很長一段時間為也是暈乎乎的,後來才慢慢重視去學習了解,才略知一二,遂分享之。

一、管理Fragement所涉及到的關鍵類

應用Fragment的過程中涉及到的關鍵類主要有:FragmentManager和、FragmentManagerImplFragmentTransaction和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 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;
    }
}

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 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;
    }
}

3、Fragment的add、replace、remove等操作小結

Fragment的addreplace操作這些都是針對棧頂的Fragment,其中每一次addreplace之後,這些操作都會封裝成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的隱患。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved