解决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
解决Fragment中使用ViewPager时,ViewPager里的Fragment错位和空白问题的更多相关文章
- 解决Yii2中刷新网页时验证码不刷新的问题
解决Yii2中刷新网页时验证码不刷新的问题 [ 2.0 版本 ] ljfrocky 2015-05-30 19:39:00 1304次浏览 5条评论 10110 在Yii2框架中,如果在表单中使用 ...
- Fragment-如何监听fragment中的回退事件与怎样保存fragment状态
一.如何监听Fragment中的回退事件 1.问题阐述 在Activity中监听回退事件是件非常容易的事,因为直接重写onBackPressed()函数就好了,但当大家想要监听Fragment中的回退 ...
- 解决nodejs中json序列化时Date类型默认为UTC格式
在nodejs中,json序列化时Date类型时,默认转为UTC格式. 如下图 上面只是一个例子,下面我用一个更具体化的例子来展示一个这个情况,我们在开发WEB项目中,经常用到Express组件, 我 ...
- 解决nodejs中json序列化时Date类型为UTC格式
在nodejs中,json序列化时Date类型时,默认转为UTC格式. 如下图 zhupengfei@DESKTOP-HJASOE3 MINGW64 /d/MyProject/exp2 $ node ...
- 如何解决python中使用flask时遇到的markupsafe._compat包缺失的问题
在使用python进行GUI的程序编写时,使用flask时出现错误: 在使用pip freeze进行查看已下载的包时显示MarkupSafe与Jinjia2都已安装: 在网上查阅一些资料后发现,在py ...
- 解决IIS中部署WCF时,访问.svc文件的404错误问题
如果你直接在IIS 7中配置WCF,访问.svc文件时会出现404错误.解决方法,以管理员身份进入命令行模式,运行:"%windir%\Microsoft.NET\Framework\v3. ...
- 彻底解决Yii2中网页刷新时验证码不刷新的问题
修改vendor/yiisoft/yii2/captcha/CaptchaValidator.php这个文件就可以了,修改的地方见下图: 总结 归根到底,是因为yii2在渲染网页的时候,会先输出js验 ...
- Android瀑布流优化,解决Recyclerview展示大批量图片时Item自动切换、闪烁、空白等问题
本文涉及的代码案例可以在下方的链接中找到,如果对你有帮助,请给个Star(#^.^#) https://github.com/CodeTillDoom/StaggeredRcl 问题分析 这段时间业务 ...
- angularjs中使用swiper时不起作用,最后出现空白位
controller.js中定义swipers指令: var moduleCtrl = angular.module('newscontroller',['infinite-scroll','ngTo ...
随机推荐
- sp<> 强指针类的用法
在android 中可以广泛看到的template<typename T>, class Sp 句柄类实际上是android 为实现垃圾回收机制的智能指针.智能指针是c++ 中的一个概念 ...
- MapReduce ---- TD-IDF
1.TF-IDF TF-IDF(term frequency/inverse document frequency) 的概念被公认为信息检索中最重要的发明.描述单个term与特定document的相关 ...
- HTML 5 新标签
HTML 5 是一个新的网络标准,目标在于取代现有的 HTML 4.01, XHTML 1.0 and DOM Level 2 HTML 标准.它希望能够减少浏览器对于需要插件的丰富性网络应用服务( ...
- PhotoShop-CS4使用-----如何对psd进行简单切图
一.如何快速截图 1.如果图片为psd样式 2.要用放大器放大该图,选择放大器,放大后如图 3.开始切图 以其中一个为例,选中你所要选择切的图片 4. 选中后 5.选择文件---新建 6.此 ...
- Confluent
Confluent介绍(一) 最开始接触confluent是通过这篇博客,How to Build a Scalable ETL Pipeline with Kafka Connect,对于做大数 ...
- ETL中的数据增量抽取机制
ETL中的数据增量抽取机制 ( 增量抽取是数据仓库ETL(extraction,transformation,loading,数据的抽取.转换和装载)实施过程中需要重点考虑的问 题.在ETL过 ...
- [问题解决] initAndListen: 10309 Unable to create/open lock file: /data/db/mongod.lock
错误: 在linux下开启mongoDB的 $ >bin: ./mongod 时报错:initAndListen: 10309 Unable to create/open lock file: ...
- 如何在 静态编译的QT 5.5.1 中 使用数据库插件连接 ODBC(调用静态插件)
前段时间由于工作的关系,需要编写一个将数据插入到 Sql server 2012 的桌面软件. 由于之前使用的是MFC,偶然间接触到了Qt后,被它的简洁惊艳到了,于是便毅然而然的转投到了Qt的怀抱,哈 ...
- iOS定位功能
1.实现定位功能需要导入系统库MapKit.framework 2.在iPhone手机上默认是禁止手机定位的,所以,要询问系统是否开启手机定位功能. 为了开启手机定位功能,还需在info.plist中 ...
- js apply 和call的区别
function Person(name, profession) { this.name = name; this.profession = profession; this.speak = fun ...