Fragment FragmentManager FragmentTransaction 详解

Fragment FragmentManager FragmentTransaction 详解

     Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计. 平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互.Fragment允许这样的一种设计,而不需要你亲自来管理view hierarchy的复杂变化. 通过将activity的布局分散到fragment中, 你可以在运行时修改activity的外观, 并在由activity管理的back stack中保存那些变化. 
     例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表, 然后在屏幕右侧使用另一个fragment来展示一篇文章 – 2个fragment并排显示在相同的一个activity中, 并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输入事件. 因此, 取代使用一个activity来选择一篇文章,而另一个activity来阅读文章 的方式, 用户可以在相同的activity中选择一篇文章并且阅读, 如图所示: 


    fragment在你的应用中应当是一个模块化和可重用的组件. 即,fragment定义了它自己的布局, 以及通过使用它自己的生命周期回调方法定义了它自己的行为, 你可以将fragment包含到多个activity中. Fragment 表现 Activity 中用UI的一个行为或者一部分. 可以组合多个fragment放在一个单独的activity中来创建一个多界面区域的UI,并可以在多个activity里重用某一个fragment.把fragment 想象成一个activity的模块化区域, 有它自己的生命周期, 接收属于它的输入事件, 并且可以在activity运行期间添加和删除.
   Fragment 必须总是被嵌入到一个activity中, 它们的生命周期直接被其所属的宿主activity的生命周期影响. 例如, 当activity被暂停,那么在其中的所有fragment也被暂停; 当activity被销毁, 所有隶属于它的fragment也被销毁. 然而,当一个activity正在运行时(处于resumed状态), 我们可以独立地操作每一个fragment, 比如添加或删除它们. 当处理这样一个fragment事务时, 也可以将它添加到activity所管理的back stack -- 每一个activity中的back stack实体都是一个发生过的fragment事务的记录. back stack允许用户通过按下 BACK 按键从一个fragment事务后退(往后导航).

Fragment 与Activity的关系

       如果我们将自己的Activity  setContentView 为以下的布局
[html]  view plain copy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <fragment  
  7.         android:id="@+id/id_fragment_title"  
  8.         android:name="com.android.example.TitleFragment"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="45dp" />  
  11.   
  12.     <fragment  
  13.         android:layout_below="@id/id_fragment_title"  
  14.         android:id="@+id/id_fragment_content"  
  15.         android:name="com.android.example.ContentFragment"  
  16.         android:layout_width="fill_parent"  
  17.         android:layout_height="fill_parent" />  
  18.   
  19. </RelativeLayout>  

    很明显,我们的Activity将会由上下两个fragment组成,也就是简单的将我们的屏幕划分成2块区域,2块区域可以独立处理自己的逻辑。Fragment总是存在于Activity中。


一个简单的对比图可以很轻松说明fragment和Activity生命周期。
我们都知道Activity的生命周期始于OnCreate,fragment既然依托于Activity那么很自然fragment的生命周期必须在Activity Created 之后。
我们跟踪Activity的OnCreate函数(Android4.4)
[java]  view plain copy
  1.     protected void onCreate(Bundle savedInstanceState) {  
  2. ........  
  3.         mFragments.dispatchCreate();  
  4. ........  
  5. }  

其他的,我们先不关心,在Activity的Created之后(中),唯一能和fragment关联上的只有mFragments,通过源码我们知道
[java]  view plain copy
  1. final FragmentManagerImpl mFragments = new FragmentManagerImpl();  
mFragments 其实是FragmentManager的实例,那么我们继续跟踪dispatchCreate
[java]  view plain copy
  1. public void dispatchCreate() {  
  2.     mStateSaved = false;  
  3.     moveToState(Fragment.CREATED, false);  
  4. }  

似乎什么都没有做,其实不然,每一个Activity都有一个FragmentManager,作为Fragment的管理类,FragmentManager自然管理者Activity对应所有的fragment,那么我们的核心自然就在moveToState

[java]  view plain copy
  1. void moveToState(int newState, int transit, int transitStyle, boolean always) {  
  2.         if (mActivity == null && newState != Fragment.INITIALIZING) {  
  3.             throw new IllegalStateException("No activity");  
  4.         }  
  5.   
  6.         if (!always && mCurState == newState) {  
  7.             return;  
  8.         }  
  9.   
  10.         mCurState = newState;  
  11.         if (mActive != null) {  
  12.             boolean loadersRunning = false;  
  13.             for (int i=0; i<mActive.size(); i++) {  
  14.                 Fragment f = mActive.get(i);  
  15.                 if (f != null) {  
  16.                     moveToState(f, newState, transit, transitStyle, false);  
  17.                     if (f.mLoaderManager != null) {  
  18.                         loadersRunning |= f.mLoaderManager.hasRunningLoaders();  
  19.                     }  
  20.                 }  
  21.             }  
  22.   
  23.             if (!loadersRunning) {  
  24.                 startPendingDeferredFragments();  
  25.             }  
  26.   
  27.             if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {  
  28.                 mActivity.invalidateOptionsMenu();  
  29.                 mNeedMenuInvalidate = false;  
  30.             }  
  31.         }  
  32.     }  


[java]  view plain copy
  1. void moveToState(Fragment f, int newState, int transit, int transitionStyle,  
  2.             boolean keepActive) {  
  3.         if (DEBUG && false) Log.v(TAG, "moveToState: " + f  
  4.             + " oldState=" + f.mState + " newState=" + newState  
  5.             + " mRemoving=" + f.mRemoving + " Callers=" + Debug.getCallers(5));  
  6.   
  7.         // Fragments that are not currently added will sit in the onCreate() state.  
  8.         if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {  
  9.             newState = Fragment.CREATED;  
  10.         }  
  11.         if (f.mRemoving && newState > f.mState) {  
  12.             // While removing a fragment, we can't change it to a higher state.  
  13.             newState = f.mState;  
  14.         }  
  15.         // Defer start if requested; don't allow it to move to STARTED or higher  
  16.         // if it's not already started.  
  17.         if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {  
  18.             newState = Fragment.STOPPED;  
  19.         }  
  20.         if (f.mState < newState) {  
  21.             // For fragments that are created from a layout, when restoring from  
  22.             // state we don't want to allow them to be created until they are  
  23.             // being reloaded from the layout.  
  24.             if (f.mFromLayout && !f.mInLayout) {  
  25.                 return;  
  26.             }  
  27.             if (f.mAnimatingAway != null) {  
  28.                 // The fragment is currently being animated...  but!  Now we  
  29.                 // want to move our state back up.  Give up on waiting for the  
  30.                 // animation, move to whatever the final state should be once  
  31.                 // the animation is done, and then we can proceed from there.  
  32.                 f.mAnimatingAway = null;  
  33.                 moveToState(f, f.mStateAfterAnimating, 00true);  
  34.             }  
  35.             switch (f.mState) {  
  36.                 case Fragment.INITIALIZING:  
  37.                     if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);  
  38.                     if (f.mSavedFragmentState != null) {  
  39.                         f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(  
  40.                                 FragmentManagerImpl.VIEW_STATE_TAG);  
  41.                         f.mTarget = getFragment(f.mSavedFragmentState,  
  42.                                 FragmentManagerImpl.TARGET_STATE_TAG);  
  43.                         if (f.mTarget != null) {  
  44.                             f.mTargetRequestCode = f.mSavedFragmentState.getInt(  
  45.                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);  
  46.                         }  
  47.                         f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(  
  48.                                 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);  
  49.                         if (!f.mUserVisibleHint) {  
  50.                             f.mDeferStart = true;  
  51.                             if (newState > Fragment.STOPPED) {  
  52.                                 newState = Fragment.STOPPED;  
  53.                             }  
  54.                         }  
  55.                     }  
  56.                     f.mActivity = mActivity;  
  57.                     f.mParentFragment = mParent;  
  58.                     f.mFragmentManager = mParent != null  
  59.                             ? mParent.mChildFragmentManager : mActivity.mFragments;  
  60.                     f.mCalled = false;  
  61.                     f.onAttach(mActivity);  
  62.                     if (!f.mCalled) {  
  63.                         throw new SuperNotCalledException("Fragment " + f  
  64.                                 + " did not call through to super.onAttach()");  
  65.                     }  
  66.                     if (f.mParentFragment == null) {  
  67.                         mActivity.onAttachFragment(f);  
  68.                     }  
  69.   
  70.                     if (!f.mRetaining) {  
  71.                         f.performCreate(f.mSavedFragmentState);  
  72.                     }  
  73.                     f.mRetaining = false;  
  74.                     if (f.mFromLayout) {  
  75.                         // For fragments that are part of the content view  
  76.                         // layout, we need to instantiate the view immediately  
  77.                         // and the inflater will take care of adding it.  
  78.                         f.mView = f.performCreateView(f.getLayoutInflater(  
  79.                                 f.mSavedFragmentState), null, f.mSavedFragmentState);  
  80.                         if (f.mView != null) {  
  81.                             f.mView.setSaveFromParentEnabled(false);  
  82.                             if (f.mHidden) f.mView.setVisibility(View.GONE);  
  83.                             f.onViewCreated(f.mView, f.mSavedFragmentState);  
  84.                         }  
  85.                     }  
  86.                 case Fragment.CREATED:  
  87.                     if (newState > Fragment.CREATED) {  
  88.                         if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);  
  89.                         if (!f.mFromLayout) {  
  90.                             ViewGroup container = null;  
  91.                             if (f.mContainerId != 0) {  
  92.                                 container = (ViewGroup)mContainer.findViewById(f.mContainerId);  
  93.                                 if (container == null && !f.mRestored) {  
  94.                                     throwException(new IllegalArgumentException(  
  95.                                             "No view found for id 0x"  
  96.                                             + Integer.toHexString(f.mContainerId) + " ("  
  97.                                             + f.getResources().getResourceName(f.mContainerId)  
  98.                                             + ") for fragment " + f));  
  99.                                 }  
  100.                             }  
  101.                             f.mContainer = container;  
  102.                             f.mView = f.performCreateView(f.getLayoutInflater(  
  103.                                     f.mSavedFragmentState), container, f.mSavedFragmentState);  
  104.                             if (f.mView != null) {  
  105.                                 f.mView.setSaveFromParentEnabled(false);  
  106.                                 if (container != null) {  
  107.                                     Animator anim = loadAnimator(f, transit, true,  
  108.                                             transitionStyle);  
  109.                                     if (anim != null) {  
  110.                                         anim.setTarget(f.mView);  
  111.                                         anim.start();  
  112.                                     }  
  113.                                     container.addView(f.mView);  
  114.                                 }  
  115.                                 if (f.mHidden) f.mView.setVisibility(View.GONE);  
  116.                                 f.onViewCreated(f.mView, f.mSavedFragmentState);  
  117.                             }  
  118.                         }  
  119.   
  120.                         f.performActivityCreated(f.mSavedFragmentState);  
  121.                         if (f.mView != null) {  
  122.                             f.restoreViewState(f.mSavedFragmentState);  
  123.                         }  
  124.                         f.mSavedFragmentState = null;  
  125.                     }  
  126.                 case Fragment.ACTIVITY_CREATED:  
  127.                 case Fragment.STOPPED:  
  128.                     if (newState > Fragment.STOPPED) {  
  129.                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);  
  130.                         f.performStart();  
  131.                     }  
  132.                 case Fragment.STARTED:  
  133.                     if (newState > Fragment.STARTED) {  
  134.                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);  
  135.                         f.mResumed = true;  
  136.                         f.performResume();  
  137.                         // Get rid of this in case we saved it and never needed it.  
  138.                         f.mSavedFragmentState = null;  
  139.                         f.mSavedViewState = null;  
  140.                     }  
  141.             }  
  142.         } else if (f.mState > newState) {  
  143.             switch (f.mState) {  
  144.                 case Fragment.RESUMED:  
  145.                     if (newState < Fragment.RESUMED) {  
  146.                         if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);  
  147.                         f.performPause();  
  148.                         f.mResumed = false;  
  149.                     }  
  150.                 case Fragment.STARTED:  
  151.                     if (newState < Fragment.STARTED) {  
  152.                         if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);  
  153.                         f.performStop();  
  154.                     }  
  155.                 case Fragment.STOPPED:  
  156.                 case Fragment.ACTIVITY_CREATED:  
  157.                     if (newState < Fragment.ACTIVITY_CREATED) {  
  158.                         if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);  
  159.                         if (f.mView != null) {  
  160.                             // Need to save the current view state if not  
  161.                             // done already.  
  162.                             if (!mActivity.isFinishing() && f.mSavedViewState == null) {  
  163.                                 saveFragmentViewState(f);  
  164.                             }  
  165.                         }  
  166.                         f.performDestroyView();  
  167.                         if (f.mView != null && f.mContainer != null) {  
  168.                             Animator anim = null;  
  169.                             if (mCurState > Fragment.INITIALIZING && !mDestroyed) {  
  170.                                 anim = loadAnimator(f, transit, false,  
  171.                                         transitionStyle);  
  172.                             }  
  173.                             if (anim != null) {  
  174.                                 final ViewGroup container = f.mContainer;  
  175.                                 final View view = f.mView;  
  176.                                 final Fragment fragment = f;  
  177.                                 container.startViewTransition(view);  
  178.                                 f.mAnimatingAway = anim;  
  179.                                 f.mStateAfterAnimating = newState;  
  180.                                 anim.addListener(new AnimatorListenerAdapter() {  
  181.                                     @Override  
  182.                                     public void onAnimationEnd(Animator anim) {  
  183.                                         container.endViewTransition(view);  
  184.                                         if (fragment.mAnimatingAway != null) {  
  185.                                             fragment.mAnimatingAway = null;  
  186.                                             moveToState(fragment, fragment.mStateAfterAnimating,  
  187.                                                     00false);  
  188.                                         }  
  189.                                     }  
  190.                                 });  
  191.                                 anim.setTarget(f.mView);  
  192.                                 anim.start();  
  193.   
  194.                             }  
  195.                             f.mContainer.removeView(f.mView);  
  196.                         }  
  197.                         f.mContainer = null;  
  198.                         f.mView = null;  
  199.                     }  
  200.                 case Fragment.CREATED:  
  201.                     if (newState < Fragment.CREATED) {  
  202.                         if (mDestroyed) {  
  203.                             if (f.mAnimatingAway != null) {  
  204.                                 // The fragment's containing activity is  
  205.                                 // being destroyed, but this fragment is  
  206.                                 // currently animating away.  Stop the  
  207.                                 // animation right now - it is not needed,  
  208.                                 // and we can't wait any more on destroying  
  209.                                 // the fragment.  
  210.                                 Animator anim = f.mAnimatingAway;  
  211.                                 f.mAnimatingAway = null;  
  212.                                 anim.cancel();  
  213.                             }  
  214.                         }  
  215.                         if (f.mAnimatingAway != null) {  
  216.                             // We are waiting for the fragment's view to finish  
  217.                             // animating away.  Just make a note of the state  
  218.                             // the fragment now should move to once the animation  
  219.                             // is done.  
  220.                             f.mStateAfterAnimating = newState;  
  221.                             newState = Fragment.CREATED;  
  222.                         } else {  
  223.                             if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);  
  224.                             if (!f.mRetaining) {  
  225.                                 f.performDestroy();  
  226.                             }  
  227.   
  228.                             f.mCalled = false;  
  229.                             f.onDetach();  
  230.                             if (!f.mCalled) {  
  231.                                 throw new SuperNotCalledException("Fragment " + f  
  232.                                         + " did not call through to super.onDetach()");  
  233.                             }  
  234.                             if (!keepActive) {  
  235.                                 if (!f.mRetaining) {  
  236.                                     makeInactive(f);  
  237.                                 } else {  
  238.                                     f.mActivity = null;  
  239.                                     f.mParentFragment = null;  
  240.                                     f.mFragmentManager = null;  
  241.                                 }  
  242.                             }  
  243.                         }  
  244.                     }  
  245.             }  
  246.         }  
  247.           
  248.         f.mState = newState;  
  249.     }  


通过这一系列moveToState(不止2个)函数,我们可以知道 FragmentManager管理者所有的fragment,并且同时切换着所有fragment的状态,这里我们先关注到INITIALIZING这个状态,那么很明显我们的fragment的生命周期为
f.onAttach(mActivity);
-》
f.performCreate(f.mSavedFragmentState);
-》
 f.onViewCreated(f.mView, f.mSavedFragmentState);
细心的你可能会发现,这中间有一个问题我们在Activity的OnCreate函数中切换的状态是Fragment.CREATED
似乎是直接跳过了INITIALIZING这个状态,并且我们甚至没有将我们的fragment交给我们的FragmentManager来管理其实不然,我们的Activity在分析加载每一个View时完成了这个过程
[java]  view plain copy
  1. public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {  
  2.         if (!"fragment".equals(name)) {  
  3.             return onCreateView(name, context, attrs);  
  4.         }  
  5.           
  6.         String fname = attrs.getAttributeValue(null"class");  
  7.         TypedArray a =   
  8.             context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);  
  9.         if (fname == null) {  
  10.             fname = a.getString(com.android.internal.R.styleable.Fragment_name);  
  11.         }  
  12.         int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);  
  13.         String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);  
  14.         a.recycle();  
  15.           
  16.         int containerId = parent != null ? parent.getId() : 0;  
  17.         if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {  
  18.             throw new IllegalArgumentException(attrs.getPositionDescription()  
  19.                     + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);  
  20.         }  
  21.   
  22.         // If we restored from a previous state, we may already have  
  23.         // instantiated this fragment from the state and should use  
  24.         // that instance instead of making a new one.  
  25.         Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null;  
  26.         if (fragment == null && tag != null) {  
  27.             fragment = mFragments.findFragmentByTag(tag);  
  28.         }  
  29.         if (fragment == null && containerId != View.NO_ID) {  
  30.             fragment = mFragments.findFragmentById(containerId);  
  31.         }  
  32.   
  33.         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"  
  34.                 + Integer.toHexString(id) + " fname=" + fname  
  35.                 + " existing=" + fragment);  
  36.         if (fragment == null) {  
  37.             fragment = Fragment.instantiate(this, fname);  
  38.             fragment.mFromLayout = true;  
  39.             fragment.mFragmentId = id != 0 ? id : containerId;  
  40.             fragment.mContainerId = containerId;  
  41.             fragment.mTag = tag;  
  42.             fragment.mInLayout = true;  
  43.             fragment.mFragmentManager = mFragments;  
  44.             fragment.onInflate(this, attrs, fragment.mSavedFragmentState);  
  45.             mFragments.addFragment(fragment, true);  
  46.   
  47.         } else if (fragment.mInLayout) {  
  48.             // A fragment already exists and it is not one we restored from  
  49.             // previous state.  
  50.             throw new IllegalArgumentException(attrs.getPositionDescription()  
  51.                     + ": Duplicate id 0x" + Integer.toHexString(id)  
  52.                     + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)  
  53.                     + " with another fragment for " + fname);  
  54.         } else {  
  55.             // This fragment was retained from a previous instance; get it  
  56.             // going now.  
  57.             fragment.mInLayout = true;  
  58.             // If this fragment is newly instantiated (either right now, or  
  59.             // from last saved state), then give it the attributes to  
  60.             // initialize itself.  
  61.             if (!fragment.mRetaining) {  
  62.                 fragment.onInflate(this, attrs, fragment.mSavedFragmentState);  
  63.             }  
  64.             mFragments.moveToState(fragment);  
  65.         }  
  66.   
  67.         if (fragment.mView == null) {  
  68.             throw new IllegalStateException("Fragment " + fname  
  69.                     + " did not create a view.");  
  70.         }  
  71.         if (id != 0) {  
  72.             fragment.mView.setId(id);  
  73.         }  
  74.         if (fragment.mView.getTag() == null) {  
  75.             fragment.mView.setTag(tag);  
  76.         }  
  77.         return fragment.mView;  
  78.     }  
当我们的Activity在创建视图View时会判断View的类型,当我们的视图是由一个fragment时会把它交给FragmentManager来管理。
那么到这里我们Fragment和Activity的生命周期就分析完成了。
至此,我们需要知道
一、FragmentManager是Fragment的管理类,每一个Activity都对应着一个FragmentManager(可以通过getFragmentManager来得到)。
二、在Activity的Created状态和Onstart状态之间,我们的fragment完成了  

onAttach()

fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。

OnCreate()

创建我们的fragment

onCreateView()

activity要得到fragmentlayout时,调用此方法,fragment在其中创建自己的layout(界面)


FragmentManager  与 FragmentTransaction

一般来讲,我们都是通过
[java]  view plain copy
  1. FragmentManager fm = getFragmentManager();    
  2. FragmentTransaction transaction = fm.beginTransaction();   
来操作多个fragment之间的切换(hide、show、replace、add、remove 等)
[java]  view plain copy
  1. @Override  
  2. public FragmentTransaction beginTransaction() {  
  3.     return new BackStackRecord(this);  
  4. }  

很明显我们的FragmentTransaction实际上是一个回退栈。
类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。
对于  回退栈的分析我就直接借鉴别人的成果了
1、管理Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

看这样一个效果图:


点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学Android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

下面讲解代码:很明显一共是3个Fragment和一个Activity.

先看Activity的布局文件:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <FrameLayout  
  7.         android:id="@+id/id_content"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent" >  
  10.     </FrameLayout>  
  11.   
  12. </RelativeLayout>  
不同的Fragment就在这个FrameLayout中显示。

MainActivity.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.zhy_fragments;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.FragmentManager;  
  5. import android.app.FragmentTransaction;  
  6. import android.os.Bundle;  
  7. import android.view.Window;  
  8.   
  9. public class MainActivity extends Activity  
  10. {  
  11.   
  12.   
  13.     @Override  
  14.     protected void onCreate(Bundle savedInstanceState)  
  15.     {  
  16.         super.onCreate(savedInstanceState);  
  17.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  18.         setContentView(R.layout.activity_main);  
  19.   
  20.         FragmentManager fm = getFragmentManager();  
  21.         FragmentTransaction tx = fm.beginTransaction();  
  22.         tx.add(R.id.id_content, new FragmentOne(),"ONE");  
  23.         tx.commit();  
  24.     }  
  25.   
  26. }  
很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

下面是FragmentOne

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.zhy_fragments;  
  2.   
  3. import android.app.Fragment;  
  4. import android.app.FragmentManager;  
  5. import android.app.FragmentTransaction;  
  6. import android.os.Bundle;  
  7. import android.view.LayoutInflater;  
  8. import android.view.View;  
  9. import android.view.View.OnClickListener;  
  10. import android.view.ViewGroup;  
  11. import android.widget.Button;  
  12.   
  13. public class FragmentOne extends Fragment implements OnClickListener  
  14. {  
  15.   
  16.     private Button mBtn;  
  17.   
  18.     @Override  
  19.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  20.             Bundle savedInstanceState)  
  21.     {  
  22.         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  23.         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
  24.         mBtn.setOnClickListener(this);  
  25.         return view;  
  26.     }  
  27.   
  28.     @Override  
  29.     public void onClick(View v)  
  30.     {  
  31.         FragmentTwo fTwo = new FragmentTwo();  
  32.         FragmentManager fm = getFragmentManager();  
  33.         FragmentTransaction tx = fm.beginTransaction();  
  34.         tx.replace(R.id.id_content, fTwo, "TWO");  
  35.         tx.addToBackStack(null);  
  36.         tx.commit();  
  37.   
  38.     }  
  39.   
  40. }  

我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

接下来FragmentTwo

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.zhy_fragments;  
  2.   
  3. import android.app.Fragment;  
  4. import android.app.FragmentManager;  
  5. import android.app.FragmentTransaction;  
  6. import android.os.Bundle;  
  7. import android.view.LayoutInflater;  
  8. import android.view.View;  
  9. import android.view.View.OnClickListener;  
  10. import android.view.ViewGroup;  
  11. import android.widget.Button;  
  12.   
  13. public class FragmentTwo extends Fragment implements OnClickListener  
  14. {  
  15.   
  16.     private Button mBtn ;  
  17.     @Override  
  18.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  19.             Bundle savedInstanceState)  
  20.     {  
  21.         View view = inflater.inflate(R.layout.fragment_two, container, false);  
  22.         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
  23.         mBtn.setOnClickListener(this);  
  24.         return view ;   
  25.     }  
  26.     @Override  
  27.     public void onClick(View v)  
  28.     {  
  29.         FragmentThree fThree = new FragmentThree();  
  30.         FragmentManager fm = getFragmentManager();  
  31.         FragmentTransaction tx = fm.beginTransaction();  
  32.         tx.hide(this);  
  33.         tx.add(R.id.id_content , fThree, "THREE");  
  34. //      tx.replace(R.id.id_content, fThree, "THREE");  
  35.         tx.addToBackStack(null);  
  36.         tx.commit();  
  37.     }  
  38.   
  39.   
  40. }  

这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

最后FragmentThree就是简单的Toast了:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.zhy_fragments;  
  2.   
  3. import android.app.Fragment;  
  4. import android.os.Bundle;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.view.ViewGroup;  
  9. import android.widget.Button;  
  10. import android.widget.Toast;  
  11.   
  12. public class FragmentThree extends Fragment implements OnClickListener  
  13. {  
  14.   
  15.     private Button mBtn;  
  16.   
  17.     @Override  
  18.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  19.             Bundle savedInstanceState)  
  20.     {  
  21.         View view = inflater.inflate(R.layout.fragment_three, container, false);  
  22.         mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);  
  23.         mBtn.setOnClickListener(this);  
  24.         return view;  
  25.     }  
  26.   
  27.     @Override  
  28.     public void onClick(View v)  
  29.     {  
  30.         Toast.makeText(getActivity(), " i am a btn in Fragment three",  
  31.                 Toast.LENGTH_SHORT).show();  
  32.     }  
  33.   
  34. }  

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!


接下来我们来重点分析replace、hide、show、add、remove
[html]  view plain copy
  1. public void run() {  
  2.         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);  
  3.   
  4.         if (mAddToBackStack) {  
  5.             if (mIndex < 0) {  
  6.                 throw new IllegalStateException("addToBackStack() called after commit()");  
  7.             }  
  8.         }  
  9.   
  10.         bumpBackStackNesting(1);  
  11.   
  12.         Op op = mHead;  
  13.         while (op != null) {  
  14.             switch (op.cmd) {  
  15.                 case OP_ADD: {  
  16.                     Fragment f = op.fragment;  
  17.                     f.mNextAnim = op.enterAnim;  
  18.                     mManager.addFragment(f, false);  
  19.                 } break;  
  20.                 case OP_REPLACE: {  
  21.                     Fragment f = op.fragment;  
  22.                     if (mManager.mAdded != null) {  
  23.                         /// M: it may remove mManager.mAdded items in mManager.removeFragment()  
  24.                         final ArrayList<Fragment> added = new ArrayList<Fragment>(mManager.mAdded);  
  25.                         for (int i=0; i<added.size(); i++) {  
  26.                             Fragment old = added.get(i);  
  27.                             if (FragmentManagerImpl.DEBUG) Log.v(TAG,  
  28.                                     "OP_REPLACE: adding=" + f + " old=" + old);  
  29.                             if (f == null || old.mContainerId == f.mContainerId) {  
  30.                                 if (old == f) {  
  31.                                     op.fragment = f = null;  
  32.                                 } else {  
  33.                                     if (op.removed == null) {  
  34.                                         op.removed = new ArrayList<Fragment>();  
  35.                                     }  
  36.                                     op.removed.add(old);  
  37.                                     old.mNextAnim = op.exitAnim;  
  38.                                     if (mAddToBackStack) {  
  39.                                         old.mBackStackNesting += 1;  
  40.                                         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "  
  41.                                                 + old + " to " + old.mBackStackNesting);  
  42.                                     }  
  43.                                     mManager.removeFragment(old, mTransition, mTransitionStyle);  
  44.                                 }  
  45.                             }  
  46.                         }  
  47.                     }  
  48.                     if (f != null) {  
  49.                         f.mNextAnim = op.enterAnim;  
  50.                         mManager.addFragment(f, false);  
  51.                     }  
  52.                 } break;  
  53.                 case OP_REMOVE: {  
  54.                     Fragment f = op.fragment;  
  55.                     f.mNextAnim = op.exitAnim;  
  56.                     mManager.removeFragment(f, mTransition, mTransitionStyle);  
  57.                 } break;  
  58.                 case OP_HIDE: {  
  59.                     Fragment f = op.fragment;  
  60.                     f.mNextAnim = op.exitAnim;  
  61.                     mManager.hideFragment(f, mTransition, mTransitionStyle);  
  62.                 } break;  
  63.                 case OP_SHOW: {  
  64.                     Fragment f = op.fragment;  
  65.                     f.mNextAnim = op.enterAnim;  
  66.                     mManager.showFragment(f, mTransition, mTransitionStyle);  
  67.                 } break;  
  68.                 case OP_DETACH: {  
  69.                     Fragment f = op.fragment;  
  70.                     f.mNextAnim = op.exitAnim;  
  71.                     mManager.detachFragment(f, mTransition, mTransitionStyle);  
  72.                 } break;  
  73.                 case OP_ATTACH: {  
  74.                     Fragment f = op.fragment;  
  75.                     f.mNextAnim = op.enterAnim;  
  76.                     mManager.attachFragment(f, mTransition, mTransitionStyle);  
  77.                 } break;  
  78.                 default: {  
  79.                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);  
  80.                 }  
  81.             }  
  82.   
  83.             op = op.next;  
  84.         }  
  85.   
  86.         mManager.moveToState(mManager.mCurState, mTransition,  
  87.                 mTransitionStyle, true);  
  88.   
  89.         if (mAddToBackStack) {  
  90.             mManager.addBackStackState(this);  
  91.         }  
  92.     }  

通过上面的switch,最终我们的fragment状态切换交给了我们FragmentManager
[java]  view plain copy
  1.    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {  
  2. ............  
  3.        mAdded.remove(fragment);  
  4. ..........  
  5. }  

[java]  view plain copy
  1.     public void hideFragment(Fragment fragment, int transition, int transitionStyle) {  
  2. ..............  
  3.         finalFragment.mView.setVisibility(View.GONE);  
  4. .............  
  5. }  

[java]  view plain copy
  1.     public void showFragment(Fragment fragment, int transition, int transitionStyle) {  
  2. ...........  
  3.             fragment.mView.setVisibility(View.VISIBLE);  
  4. .........  
  5. }  

[java]  view plain copy
  1.     public void addFragment(Fragment fragment, boolean moveToStateNow) {  
  2. .............  
  3.          mAdded.add(fragment);  
  4. .......  
  5. }  
通过上面四个函数我们发现
hide只是简单的隐藏我们Fragment的视图;
[java]  view plain copy
  1. transaction.hide(mFragment);  
show 显示我们的Fragment
[java]  view plain copy
  1. transaction.show(mFragment);  
remove 从我们的fragment List列表中移除了我们的fragment的实例,移除就意味着它不会存在我们的回退栈中
[java]  view plain copy
  1. transaction.remove(mFragment);  
add 把fragment放入到我们的队列中
[java]  view plain copy
  1. transaction.add(mFragment);  
那么,我们的replace呢,细心的话我们会发现
[java]  view plain copy
  1. transaction.replace(<span style="color: rgb(68, 68, 68); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 17.98611068725586px;">R.id.id_content,</span>mFragment);  

[html]  view plain copy
  1. <span style="color: rgb(51, 51, 51);"> case OP_REPLACE: {  
  2.                     Fragment f = op.fragment;  
  3.                     if (mManager.mAdded != null) {  
  4.                         /// M: it may remove mManager.mAdded items in mManager.removeFragment()  
  5.                         final ArrayList<Fragment> added = new ArrayList<Fragment>(mManager.mAdded);  
  6.                         for (int i=0; i<added.size(); i++) {  
  7.                             Fragment old = added.get(i);  
  8.                             if (FragmentManagerImpl.DEBUG) Log.v(TAG,  
  9.                                     "OP_REPLACE: adding=" + f + " old=" + old);  
  10.                             if (f == null || old.mContainerId == f.mContainerId) {  
  11.                                 if (old == f) {  
  12.                                     op.fragment = f = null;  
  13.                                 } else {  
  14.                                     if (op.removed == null) {  
  15.                                         op.removed = new ArrayList<Fragment>();  
  16.                                     }  
  17.                                     op.removed.add(old);  
  18.                                     old.mNextAnim = op.exitAnim;  
  19.                                     if (mAddToBackStack) {  
  20.                                         old.mBackStackNesting += 1;  
  21.                                         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "  
  22.                                                 + old + " to " + old.mBackStackNesting);  
  23.                                     }  
  24.                                     </span><span style="color:#ff0000;">mManager.removeFragment(old, mTransition, mTransitionStyle);</span><span style="color:#333333;">  
  25.                                 }  
  26.                             }  
  27.                         }  
  28.                     }  
  29.                     if (f != null) {  
  30.                         f.mNextAnim = op.enterAnim;  
  31.                         </span><span style="color:#ff0000;">mManager.addFragment(f, false);</span><span style="color:#333333;">  
  32.                     }  
  33.                 } break;</span>  
这是什么意思呢,很明显  replace  == remove + add
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值