前言:使用ViewPager和fragment情况很常见,然而今天却遇到了问题,通过查阅ViewPager相关资料发现是ViewPager缓存机制造成的,在此记录一下,避免再次入坑。
问题:在一个包含ViewPager页面里包含两个fragment(OneFragment、TwoFragment),在activity的onResume()中调用加载数据方法,然而当前页面切至后台并发生被系统回收后,再次切回该页面时只需onResume发现没有加载数据。主要代码如下:
1、页面代码:
class TestActivity : BaseActivity() {
private val mFragmetList = mutableListOf<BaseFragment>()
private var mCurrentFragment: BaseFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_me_order)
initView()
}
private fun initView() {
mCurrentFragment = OneFragment()
mFragmetList.add(mCurrentFragment)
mFragmetList.add(TwoFragment())
viewPager.adapter = ViewPagerAdapter(supportFragmentManager, mFragmetList)
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(p0: Int) {}
override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {}
override fun onPageSelected(p0: Int) {
setVisiable(false)//设置当前fragment不可见
mCurrentFragment = mFragmetList[p0] as BaseFragment
setVisiable(true)//设置新fragment可见
}
})
Timber.d("**onCreate()----mCurrentFragment=$mCurrentFragment")
}
override fun onResume() {
super.onResume()
setVisiable(true)
Timber.d("**onResume")
}
override fun onPause() {
super.onPause()
setVisiable(false)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
Timber.d("**onRestoreInstanceState")
}
//对当前fragment可见性操作
private fun setVisiable(visible: Boolean) {
mCurrentFragment?.setVisiable(visible)
Timber.d("**setVisiable(visible)----mCurrentFragment=$mCurrentFragment")
}
}
2、BaseFragment代码如下:(OneFragment、TwoFragment继承该Fragment)
class BaseFragment : Fragment(){
private var mAdapter: OrderAdapter? = null
private var mIsVisiable = false//页面是否可见
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
context?.let { it ->
mAdapter = OrderAdapter(it, mDatas, R.layout.adapter_order_item, mPresenter)
getListView().adapter = mAdapter
//解决setVisiable为可见方法执行在mAdapterc初始化前导致不刷新问题
if (mIsVisiable) {
loadData()
}
}
Timber.d("**onViewCreated()----Fragment=$this----mIsVisiable=$mIsVisiable")
}
fun setVisiable(isVisiable: Boolean) {
mIsVisiable = isVisiable
mAdapter?.let {//onViewCreated未执行时mAdapter为空
if (isVisiable ) {
loadData()
}
}
Timber.d("**setVisiable($isVisiable)----OneFragment=$this")
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
Timber.d("**onViewStateRestored()----Fragment==$this")
}
fun loadData() {
//省略加载数据代码
}
fun showOrderListResult(orderListRes: OrderListRes?) {
//省略获取数据后显示代码
}
}
3、问题重现时日志信息如下:
分析:发现在TestActivity中当前的fragment为“fb806d66-21ce-44d0-805f-cb770f6b5230”,而ViewPager恢复的当前fragment是“330ee00e-519c-469f-a6b5-544ae97a0ba6”,当前展示的正是ViewPager缓存的这个Fragment,而TestActivity只是实例化并没有添加到ViewPager中,所以在对TestActivity中的fragmentsetVisiable()操作都不会显示
原理:当页面恢复时虽然在onCreate中创建了新的Fragment实例,然而ViewPager缓存机制会判断是否缓存当前Item的fragment如果存在则不再add,从而新创建的fragment不会显示,另外如果页面回收前展示的是TwoFragment则恢复时展示的也是TwoFragment,并且回调OnPageChangeListener,如果在oncreate中注册该监听则会执行该回调。
解决方案:在oncreate方法中判断如果是恢复状态则不再创建新fragment实例,而是用supportFragmentManager中已经缓存的,这样后续操作都是对当前ViewPager里的fragment了,代码如下:
class TestActivity : BaseActivity() {
private val mFragmetList = mutableListOf<BaseFragment>()
private var mCurrentIndex = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_me_order)
initView(savedInstanceState)
}
private fun initView(savedInstanceState: Bundle?) {
if(savedInstanceState != null && supportFragmentManager.fragments.size > 0){
supportFragmentManager.fragments.forEach {
mFragmetList.add(it)
}
mCurrentIndex = savedInstanceState.getInt("current_index")
}else{
mFragmetList.add(OneFragment())
mFragmetList.add(TwoFragment())
setListener()
}
viewPager.adapter = ViewPagerAdapter(supportFragmentManager, mFragmetList)
Timber.d("**onCreate()----mCurrentFragment=$mFragmetList[mCurrentIndex]")
}
override fun onResume() {
super.onResume()
setVisiable(true)
Timber.d("**onResume")
}
override fun onPause() {
super.onPause()
setVisiable(false)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("current_index", mCurrentIndex)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
setListener()
Timber.d("**onRestoreInstanceState")
}
//对当前fragment可见性操作
private fun setVisiable(visible: Boolean) {
(mFragmetList[mCurrentIndex] as? BaseFragment)?.setVisiable(visible)
Timber.d("**setVisiable(visible)----mCurrentFragment=$mFragmetList[mCurrentIndex]")
}
private fun setListener(){
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(p0: Int) {}
override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {}
override fun onPageSelected(p0: Int) {
setVisiable(false)//设置当前fragment不可见
mCurrentIndex = p0
setVisiable(true)//设置新fragment可见
}
})
}
}