• 解决Fragment中使用ViewPager时,ViewPager里的Fragment错位和空白问题。


    这两天开始在改OSChina的开源android客户端,打算用Fragment来分离Main这个Activity里的功能。用Fragment嵌套ViewPager+Fragment的时候发现问题。

    红色框的是主Fragment,蓝色框是主Fragment内嵌的ViewPager+Fragment。

    例如当”资讯“切换到”问答“的时候,”问答“内的ViewPager+Fragment显示不符合预期,因为里面的Fragment错位了,前面几个显示的是”资讯“里面的Fragment。

    而且有些显示Fragment显示空白。检查了下没问题,查看源代码发现是创建FragmentPagerAdapter时用getFragmentManager()传入的FragmentManager都是获取自Activity的同一个FragmentManager。

    FragmentManager里用ArrayList自动缓存了Fragment,如果两个主Fragment用同样的布局ID会使得缓存的tag相同,结果会导致子Fragment互相替换。

    FragmentPagerAdapter里的源代码:

     1 @Override
     2     public Object instantiateItem(ViewGroup container, int position) {
     3         if (mCurTransaction == null) {
     4             mCurTransaction = mFragmentManager.beginTransaction();
     5         }
     6 
     7         final long itemId = getItemId(position);
     8 
     9         // Do we already have this fragment?
    10         String name = makeFragmentName(container.getId(), itemId);
    11         Fragment fragment = mFragmentManager.findFragmentByTag(name);
    12         if (fragment != null) {
    13             if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
    14             mCurTransaction.attach(fragment);
    15         } else {
    16             fragment = getItem(position);
    17             if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
    18             mCurTransaction.add(container.getId(), fragment,
    19                     makeFragmentName(container.getId(), itemId));
    20         }
    21         if (fragment != mCurrentPrimaryItem) {
    22             fragment.setMenuVisibility(false);
    23             fragment.setUserVisibleHint(false);
    24         }
    25 
    26         return fragment;
    27     }

    还有Fragment显示空白的问题,打印所有Fragment的生命周期发现,当”资讯“切换到”问答“的时候主Fragment会执行onCreate到onResume,但是ViewPager里的Fragment却没动静。

    调用notifyDataSetChanged()也无法刷新子Fragment。查看源代码后发现使用getChildFragmentManager()来替代getFragmentManager()获取FragmentManager能解决问题,

    因为getChildFragmentManager()会为本Fragment创建一个私有的FragmentManager。

     1 /**
     2      * Return the FragmentManager for interacting with fragments associated
     3      * with this fragment's activity.  Note that this will be non-null slightly
     4      * before {@link #getActivity()}, during the time from when the fragment is
     5      * placed in a {@link FragmentTransaction} until it is committed and
     6      * attached to its activity.
     7      *
     8      * <p>If this Fragment is a child of another Fragment, the FragmentManager
     9      * returned here will be the parent's {@link #getChildFragmentManager()}.
    10      */
    11     final public FragmentManager getFragmentManager() {
    12         return mFragmentManager;
    13     }
    14 
    15     /**
    16      * Return a private FragmentManager for placing and managing Fragments
    17      * inside of this Fragment.
    18      */
    19     final public FragmentManager getChildFragmentManager() {
    20         if (mChildFragmentManager == null) {
    21             instantiateChildFragmentManager();
    22             if (mState >= RESUMED) {
    23                 mChildFragmentManager.dispatchResume();
    24             } else if (mState >= STARTED) {
    25                 mChildFragmentManager.dispatchStart();
    26             } else if (mState >= ACTIVITY_CREATED) {
    27                 mChildFragmentManager.dispatchActivityCreated();
    28             } else if (mState >= CREATED) {
    29                 mChildFragmentManager.dispatchCreate();
    30             }
    31         }
    32         return mChildFragmentManager;
    33     }
     1 void instantiateChildFragmentManager() {
     2         mChildFragmentManager = new FragmentManagerImpl();
     3         mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
     4             @Override
     5             public View findViewById(int id) {
     6                 if (mView == null) {
     7                     throw new IllegalStateException("Fragment does not have a view");
     8                 }
     9                 return mView.findViewById(id);
    10             }
    11         }, this);
    12     }

    同时还会根据本Fragment现在所处的状态来更新私有FragmentManager里所缓存的Fragment。

     1     ArrayList<Fragment> mActive;
     2     ArrayList<Fragment> mAdded;
     3     ArrayList<Integer> mAvailIndices;
     4     ArrayList<BackStackRecord> mBackStack;
     5     ArrayList<Fragment> mCreatedMenus;
     6 
     7     public void dispatchStart() {
     8         mStateSaved = false;
     9         moveToState(Fragment.STARTED, false);
    10     }
    11     
    12     public void dispatchResume() {
    13         mStateSaved = false;
    14         moveToState(Fragment.RESUMED, false);
    15     }
    16     
    17     public void dispatchPause() {
    18         moveToState(Fragment.STARTED, false);
    19     }
    20     
    21     public void dispatchStop() {
    22         // See saveAllState() for the explanation of this.  We do this for
    23         // all platform versions, to keep our behavior more consistent between
    24         // them.
    25         mStateSaved = true;
    26 
    27         moveToState(Fragment.STOPPED, false);
    28     }
    29     
    30     public void dispatchReallyStop() {
    31         moveToState(Fragment.ACTIVITY_CREATED, false);
    32     }
    33 
    34     public void dispatchDestroyView() {
    35         moveToState(Fragment.CREATED, false);
    36     }
    37 
    38     public void dispatchDestroy() {
    39         mDestroyed = true;
    40         execPendingActions();
    41         moveToState(Fragment.INITIALIZING, false);
    42         mActivity = null;
    43         mContainer = null;
    44         mParent = null;
    45     }
    46 
    47     void moveToState(int newState, int transit, int transitStyle, boolean always) {
    48         if (mActivity == null && newState != Fragment.INITIALIZING) {
    49             throw new IllegalStateException("No activity");
    50         }
    51 
    52         if (!always && mCurState == newState) {
    53             return;
    54         }
    55 
    56         mCurState = newState;
    57         if (mActive != null) {
    58             boolean loadersRunning = false;
    59             for (int i=0; i<mActive.size(); i++) {
    60                 Fragment f = mActive.get(i);
    61                 if (f != null) {
    62                     moveToState(f, newState, transit, transitStyle, false); //更新Fragment
    63                     if (f.mLoaderManager != null) {
    64                         loadersRunning |= f.mLoaderManager.hasRunningLoaders(); //是否在装载中
    65                     }
    66                 }
    67             }
    68 
    69             if (!loadersRunning) {
    70                 startPendingDeferredFragments();  //不是的话启动更新
    71             }
    72 
    73             if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
    74                 mActivity.supportInvalidateOptionsMenu(); // 刷新选项菜单
    75                 mNeedMenuInvalidate = false;
    76             }
    77         }
    78     }
    79 
    80     void startPendingDeferredFragments() {
    81         if (mActive == null) return;
    82 
    83         for (int i=0; i<mActive.size(); i++) {
    84             Fragment f = mActive.get(i);
    85             if (f != null) {
    86                 performPendingDeferredStart(f);
    87             }
    88         }
    89     }
    90     
    91     

    根据Fragment所处的状态,启动和恢复Fragment的视图。

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

    我这里有support V4、V7、V13包的源代码。共享在百度云盘,需要的可以去下载。http://pan.baidu.com/s/1ntkbxpB

  • 相关阅读:
    第一阶段冲刺8
    第一阶段冲刺7
    第一阶段冲刺6
    第一阶段冲刺5
    第一阶段冲刺4
    第一阶段冲刺3
    冲刺阶段二
    典型用户和用户场景
    团队题目需求分析-NABCD
    第二阶段第七天
  • 原文地址:https://www.cnblogs.com/zhengxt/p/3781139.html
Copyright © 2020-2023  润新知