博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解决Fragment中使用ViewPager时,ViewPager里的Fragment错位和空白问题
阅读量:4364 次
发布时间:2019-06-07

本文共 9710 字,大约阅读时间需要 32 分钟。

这两天开始在改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 *

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
mActive; 2 ArrayList
mAdded; 3 ArrayList
mAvailIndices; 4 ArrayList
mBackStack; 5 ArrayList
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

根据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

转载于:https://www.cnblogs.com/Free-Thinker/p/4458849.html

你可能感兴趣的文章
微服务:Java EE的拯救者还是掘墓人?
查看>>
如何在Centos里面,把.net core程序设为开机自启动
查看>>
1920*1080pc端适配
查看>>
Nutch系列1:简介
查看>>
前端UI框架选择区别对比推荐
查看>>
栈 队列 和 双向队列
查看>>
从垃圾回收看闭包
查看>>
Intel Core Microarchitecture Pipeline
查看>>
如何去除交叉表的子行(列)的小计?
查看>>
Web字体(链接)嵌入
查看>>
switch… case 语句的用法
查看>>
day07补充-数据类型总结及拷贝
查看>>
语言、数据和运算符
查看>>
正则表达式30分钟入门教程
查看>>
sqlserver try catch·
查看>>
1028: 可乐(2018年中南大学研究生复试机试题 )
查看>>
珍藏的最全的windows操作系统快捷键
查看>>
【DBAplus】SQL优化:一篇文章说清楚Oracle Hint的正确使用姿势
查看>>
二叉树结点删除操作
查看>>
图论-单源最短路-SPFA算法
查看>>