FragmentPagerAdapter 的那些坑
来源:互联网 发布:陕西大数据集团官网 编辑:程序博客网 时间:2024/06/05 16:03
引子
开始前,首先来看一下这种常用的布局,每个底部导航栏项中呢都有一个ViewPager。
笔者先来说一下自己遇到的问题,第一次进入的时候ViewPager(第一页)显示正常,但是切换到别的底部tab再切换回来的时候,ViewPager的第一项和第二项的视图就没有了,试过FragmentPagerAdapter 和 FragmentStatePagerAdapter 但都没有解决问题,所以笔者决定从源码的角度出发,去探索问题所在。
起始
首先
底部Tab1是HomeFragement
底部Tab2是OrdersFragment
由HomeFragment切换到OrdersFragment之后的周期调用情况如下:
— 后台打印如下 —
07-29 11:21:56.560 18830-18830/com.finobusiness.forhomelife D/HomeFragment: onDestroyView:
07-29 11:21:56.565 18830-18830/com.finobusiness.forhomelife D/OrdersFragment: onCreateView:
HomeFragment只是调用了onDestroyView方法,没有调用onDestroy方法。
问题源头
FragmentPagerAdapter
的instantiateItem()
方法如下:
@Override public Object instantiateItem(ViewGroup container, int position) { final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { mCurTransaction.attach(fragment); } else { fragment = getItem(position); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } return fragment; }
需要注意的是这段代码:
Fragment fragment = mFragmentManager.findFragmentByTag(name);
可以看到在去实例化一个Fragment之前会先去FragmentManager当中通过Tag查找是否已经有了这个Fragment,如果有,则不再调用public abstract Fragment getItem(int position)
这个方法,通常这是咱们实现的用来提供Fragment的方法。
Fragment 的 onDestroyView()
因为ViewPager当中的Fragment的视图已经被销毁了
onDestroyView的源码
public void onDestroyView() { mCalled = true; }
没错,只有这么一行,但是里面的mCalled威力可是很惊人的,一会儿就会涉及到。
在代码中查询,发现onDestroyView()方法在全局中只有一个地方被调用到了,那就是
void performDestroyView() { if (mChildFragmentManager != null) { mChildFragmentManager.dispatchDestroyView(); } mState = CREATED; mCalled = false; onDestroyView(); if (!mCalled) { throw new SuperNotCalledException("Fragment " + this + " did not call through to super.onDestroyView()"); } if (mLoaderManager != null) { mLoaderManager.doReportNextStart(); } }
首先呢可以看到如果onDestroyView()方法在在子类中没有被调用,则会抛出SuperNotCalledException
异常
其次
mChildFragmentManager.dispatchDestroyView
最终会调用到FragmentManager
的void moveToState(int newState, int transit, int transitStyle, boolean always)
方法。
moveToState方法的源码如下
void moveToState(int newState, int transit, int transitStyle, boolean always) { mCurState = newState; if (mActive != null) { boolean loadersRunning = false; for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); if (f != null) moveToState(f, newState, transit, transitStyle, false); } } }
case Fragment.ACTIVITY_CREATED: if (newState < Fragment.ACTIVITY_CREATED) { f.performDestroyView(); if (f.mView != null && f.mContainer != null) { f.mContainer.removeView(f.mView); } f.mContainer = null; f.mView = null; f.mInnerView = null; }
最终可以看到fragment.view被移除并且被置为null。
下一次进来的时候Fragment被通过getFragmentByTag方法找到,但是他的View却是空,所以我们的ViewPager的前两页(默认的缓存页)就没有视图。
解决之道
解决的办法就是ViewPager所在的那个Fragment中手动调用PagerAdapter 的destroyItem
方法
- FragmentPagerAdapter 的那些坑
- FragmentPagerAdapter与FragmentStatePagerAdapter那些秘密的事
- FragmentPagerAdapter 遇到的问题
- FragmentPagerAdapter的工作流程
- ViewPager + FragmentPagerAdapter的使用
- FragmentPagerAdapter的理解
- 通用的FragmentPagerAdapter
- PagerAdapter、FragmentPagerAdapter的使用
- FragmentPagerAdapter的用法
- FragmentPagerAdapter的用法
- ViewPager+FragmentPagerAdapter的使用
- FragmentPagerAdapter的用法详解
- FragmentPagerAdapter
- FragmentPagerAdapter
- FragmentPagerAdapter
- FragmentPagerAdapter
- FragmentPagerAdapter
- FragmentPagerAdapter
- ROS学习(-)基本概念+发布&订阅消息
- 【代码笔记】HTML+CSS+JAVAScript+jQuery滑过图标下滑列表
- (HDU 5753)2016 Multi-University Training Contest 3 Permutation Bo (水)
- H5引导页制作流程分享及脱坑记录
- php发送email最终版 (案例)
- FragmentPagerAdapter 的那些坑
- hdoj2612Find a way(两次bfs)
- 论assert(0)的作用
- ACM中常见错误对应表
- Activity生命周期+四种模式——枯燥重要(五)
- POJ 3126 Prime Path(首相的无厘头要求,这个是不是华盛顿,BFS)
- ubuntu14.04安装wine
- 从数组中获取最值
- CreateProcess创建新的进程