一、前言
对于Fragment的使用和生命周期网上有很多说明(传送门),此篇文章只是记录一些特殊情况下的Fragment的生命周期执行情况。
二、发现问题
在开发中,经常使用底部导航功能,一般采用RadioGroup+Fragment实现界面切换,例如
class MainActivity : AppCompatActivity(){
private var mFragments: Array<Fragment?> = arrayOf((HomeFragment()), null, null, null)
...
/**
* Fragment切换
*/
private fun selectedFragment(itemId: Int) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
Log.i("itemId", "" + itemId)
val fragment = mFragments[itemId]
//先检查Fragment是否创建
if (fragment == null) {
when (itemId) {
0-> mFragments[0] = HomeFragment()
1-> mFragments[1] = DeviceFragment()
2-> mFragments[2] = TaskFragment()
3-> mFragments[3] = MangerFragment()
}
//添加到管理类
fragmentTransaction.add(R.id.linearLayout, mFragments[itemId]!!)
}
for (i in mFragments.indices) {
val fragment = mFragments[i]
fragment?.let {
if (i != itemId) {
fragmentTransaction.hide(fragment)
} else {
fragmentTransaction.show(fragment)
}
}
}
fragmentTransaction.commit()
}
...
}
但是我发现使用show/hide 切换Fragment时,onResume
与onPause
并没有执行。fragment第一次创建的时候执行了onResume方法,但是后续的切换中只是调用show/hide,并不走onResume
与onPause
。如果我们需要处理Fragment的可见和不可见时该怎么办呢?
三、解决问题
3.1 未使用Viewpager
其实在调用show/hide方法会执行onHiddenChanged
方法,true为当前界面hide
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
}
但是如果是在fragment启动新页面和返回来并没有执行onHiddenChanged
方法,而是所有fragment都执行了onPause
和onResume
,所以我们需要结合起来进行判断
class HomeFragment: Fragment(){
//是否运行onHiddenChanged方法
private var isHiddenChanged = false
//是否可见
private var isUserVisible = false
override fun onResume() {
super.onResume()
//如果没有执行onHiddenChanged方法,则是第一个fragment
if (!isHiddenChanged) {
isHiddenChanged = true
//记录页面可见状态
isUserVisible = true
onUserVisible()
} else {
//如果当前页面可见的
if (isUserVisible) {
onUserVisible()
}
}
}
override fun onPause() {
super.onPause()
//如果当前页面可见的
if (isUserVisible) {
onUserInvisible()
}
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
//如果是隐藏
if (hidden) {
//如果当前fragment已经页面可见了,那么就要执行不可见方法
if (isUserVisible) {
isUserVisible = false
onUserInvisible()
}
} else {
//如果是显示
onUserVisible()
//记录页面可见状态
isUserVisible = true
}
isHiddenChanged = true
}
/**
* 当前Fragment不可见执行方法
*/
private fun onUserInvisible() {}
/**
* 当前Fragment可见执行方法
*/
private fun onUserVisible() {}
}
3.2 使用Viewpager
如果切换时使用的是viewpager,例如
class MainActivity : AppCompatActivity(){
private var mFragments: Array<Fragment?> = arrayOf((HomeFragment()), DeviceFragment(), TaskFragment(), MangerFragment())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewPager.setAdapter(MyFragmentPagerAdapter(getSupportFragmentManager(), mFragments));
}
...
/**
* Fragment切换
*/
private fun selectedFragment(itemId: Int) {
mViewPager.setCurrentItem(itemId)
}
...
}
class MyFragmentPagerAdapter(fm: FragmentManager, private val array: Array<Fragment>) : FragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return array[position]
}
override fun getCount(): Int {
return array.size
}
}
经过实验发现根本就不走onHiddenChanged
方法,fragment在viewpager中会执行setUserVisibleHint(boolean isVisibleToUser)
方法
private var isFirstResume = true
private var isFirstVisible = true
private var isUserVisible = false
private var isPrepared: Boolean = false
override fun onResume() {
super.onResume()
if (isFirstResume) {
isFirstResume = false
return
}
if (userVisibleHint) {
isUserVisible = true
onUserVisible()
}
}
override fun onPause() {
super.onPause()
if (isFirstResume) {
isFirstResume = false
return
}
if (userVisibleHint) {
if(isUserVisible){
isUserVisible = false
onUserInvisible()
}
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initPrepare()
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
if (isFirstVisible) {
isFirstVisible = false
initPrepare()
} else {
isUserVisible = true
onUserVisible()
}
} else {
if(isUserVisible){
isUserVisible = false
onUserInvisible()
}
}
}
private fun initPrepare() {
if (isPrepared) {
isUserVisible = true
onUserVisible()
} else {
isPrepared = true
}
}
open fun onUserVisible() {
Log.e(ViewPagerFragment.TAG, "$mTag === onUserVisible")
}
open fun onUserInvisible() {
Log.e(ViewPagerFragment.TAG, "$mTag === onUserInvisible")
}
override fun onDestroyView() {
super.onDestroyView()
isFirstResume = true
isFirstVisible = true
isUserVisible = false
isPrepared = false
}