Fragment 有很多种使用方法,官方并没有提供一个统一的 api 来处理 Fragment 的可见性判断和回调,导致在不同的使用场景下需要使用不同的方法来判断 Fragment 的可见性。网上已经有很多讲 Fragment 可见性的文章,但是大部分文章覆盖的使用场景不够全面,有些文章的用法也过时了,因此本人梳理了当前 Fragment 的各种使用场景,提供了一个统一的 api 来处理 Fragment 的可见性。
一般使用场景
在Activity中直接使用
在 xml 文件中声明 Fragment,或者在代码中通过 FragmentTransaction 的 add 或 replace 动态载入 Fragment。这两种情况下都只要监听 Fragment 的 onResume 和 onPause 方法就能判断 Fragment 的可见性。
override fun onResume() {
super.onResume()
determineFragmentVisible()
override fun onPause() {
super.onPause()
determineFragmentInvisible()
使用show和hide控制显示和隐藏
Google 在 androidx.fragment 1.2.0
中新增了一个 FragmentContainerView,用来替代 FlameLayout 做为 Fragment 的容器,在下文中将使用 FragmentContainerView 作为 Fragment 的容器。
通过 FragmentTransaction 的 add 将 Fragment 添加到 FragmentManager 后,Fragment 的生命周期会跟随绑定的 Activity 或父 Fragment 走到 onResume,这个时候,只要所依附的 Activity 或父 Fragment 的生命周期不发生变化,通过 FragmentTransaction 的 show 和 hide 方法控制 Fragment 的显示和隐藏并不会改变 Fragment 的生命周期,这个时候需要监听 onHiddenChanged 判断 Fragment 的可见性。
一般情况下,将 Fragment add 到 FragmentManager 的过程是在 Activity 中的 onCreate 回调中进行的,第一次回调 onHiddenChanged 是在 Fragment 回调 onCreateView 之前。如果需要在 Fragment 第一次可见的时候进行 UI 操作,就会出错,为了避免出错,需要结合 Fragment 的 onResume 和 onPause 判断 Fragment 的可见性。
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
determineFragmentInvisible()
} else {
determineFragmentVisible()
override fun onResume() {
super.onResume()
determineFragmentVisible()
override fun onPause() {
super.onPause()
determineFragmentInvisible()
AndroidX用法
调用了 hide 后,接着调用 setMaxLifecycle(fragment, Lifecycle.State.STARTED)
,Fragment 生命周期会走到 onPause。调用 show 方法后,接着调用 setMaxLifecycle(fragment, Lifecycle.State.RESUMED)
,Fragment 生命周期会走到 onPause。这样只要监听 Fragment 的 onResume 和 onPause 方法就能判断 Fragment 的可见性。
override fun onResume() {
super.onResume()
determineFragmentVisible()
override fun onPause() {
super.onPause()
determineFragmentInvisible()
在ViewPager中使用
在 support 和 androidx.fragment 1.0.0
,通过监听 setUserVisibleHint 判断Fragment 的可见性。如果将 Fragment add 到 FragmentManager 的过程是在 Activity 中的 onCreate 回调中进行的,第一次回调 setUserVisibleHint 也是在 Fragment 回调 onCreateView 之前,也需要结合 Fragment 的 onResume 和 onPause 判断 Fragment 的可见性。
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
determineFragmentVisible()
} else {
determineFragmentInvisible()
override fun onResume() {
super.onResume()
determineFragmentVisible()
override fun onPause() {
super.onPause()
determineFragmentInvisible()
AndroidX用法
谷歌从 androidx.fragment 1.1.0
中开始,对 FragmentPagerAdapter 和 FragmentStatePagerAdapter 进行了调整,支持使用 setMaxLifecycle 控制 Fragment 的生命周期,只需要创建 Adpter 的时候, Behavior 选择 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
。
public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
} else {
mCurrentPrimaryItem.setUserVisibleHint(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
fragment.setUserVisibleHint(true);
这样只要监听 Fragment 的 onResume 和 onPause 方法就能判断 Fragment 的可见性。
override fun onResume() {
super.onResume()
determineFragmentVisible()
override fun onPause() {
super.onPause()
determineFragmentInvisible()
在ViewPager2中使用
在 ViewPager2 中使用 Fragment 时,使用的适配器是 FragmentStateAdapter,FragmentStateAdapter 内部使用 FragmentMaxLifecycleEnforcer ,FragmentMaxLifecycleEnforcer 也是通过 setMaxLifecycle 控制 Fragment 的生命周期
class FragmentStateAdapter {
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
mFragmentMaxLifecycleEnforcer = new FragmentMaxLifecycleEnforcer();
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
mFragmentMaxLifecycleEnforcer = null;
class FragmentMaxLifecycleEnforcer {
void updateFragmentMaxLifecycle(boolean dataSetChanged) {
for (int ix = 0; ix < mFragments.size(); ix++) {
if (itemId != mPrimaryItemId) {
transaction.setMaxLifecycle(fragment, STARTED);
} else {
toResume = fragment; // itemId map key, so only one can match the predicate
if (toResume != null) { // in case the Fragment wasn't added yet
transaction.setMaxLifecycle(toResume, RESUMED);
这样只要监听 Fragment 的 onResume 和 onPause 方法就能判断 Fragment 的可见性。
override fun onResume() {
super.onResume()
determineFragmentVisible()
override fun onPause() {
super.onPause()
determineFragmentInvisible()
在 IFragmentVisibility
中定义 Fragment 可见性相关方法:
interface IFragmentVisibility {
* Fragment可见时调用。
fun onVisible() {}
* Fragment不可见时调用。
fun onInvisible() {}
* Fragment第一次可见时调用。
fun onVisibleFirst() {}
* Fragment可见时(第一次除外)调用。
fun onVisibleExceptFirst() {}
* Fragment当前是否对用户可见
fun isVisibleToUser(): Boolean
Fragment可见
Fragment 可见受到几个因素影响:Fragment 是否处于 RESUMED 状态、Fragment 是否显示、Fragment Hint 是否对用户可见,判断Fragment可见性可能会被连续调用多次,如果当前已经对用户可见,则不进行判断可见性。
// Fragment当前是否对用户可见。
private var mIsFragmentVisible = false
// Fragment当前是否是第一次对用户可见。
private var mIsFragmentVisibleFirst = true
private fun determineFragmentVisible() {
if (isResumed && !isHidden && userVisibleHint && !mIsFragmentVisible) {
mIsFragmentVisible = true
onVisible()
if (mIsFragmentVisibleFirst) {
mIsFragmentVisibleFirst = false
onVisibleFirst()
} else {
onVisibleExceptFirst()
Fragment不可见
当 Fragment 处于可见状态,调用一次 determineFragmentInvisible 方法,Fragment 就变成不可见了。
private fun determineFragmentInvisible() {
if (mIsFragmentVisible) {
mIsFragmentVisible = false
onInvisible()
Fragment嵌套
分析日志可知,所有的 Fragment 生命周期都走到了onResume,但是 Fragment-2、Fragment-1-2、Fragment-2-2 因为 isHidden = true
,判断出是不可见状态。Fragment-2-1 是 isHidden = false
,但是 Fragment-2 是 isHidden = true
,从逻辑上父 Fragment 不可见,子 Fragment 也应该不可见。所以在判断 Fragment 是否可见的时候,还要考虑父 Fragment 是否可见(如果存在父 Fragment)。
当从 Fragment-1 切换到 Fragment-2 后,可以看到,Fragment-1 不可见,Fragment-2 可见,但是本应该不可见的 Fragment-1-1 还是可见,本应该可见的 Fragment-2-1 还是不可见,说明判断可见性逻辑还有待优化的地方。
从 Fragment-1 切换到 Fragment-2,这两者的 onHiddenChanged 被调用了,所以它们的可见性发生了变化。Fragment-1-1 和 Fragment-2-1 没有任何操作,但是它们的可见性也应该随着父Fragment 可见性发生变化而变化,所以应该在父 Fragment 可见性变化的时候重新判断一次子 Fragment 的可见性。
AndroidX用法
全部使用 setMaxLifecycle 控制 Fragment 生命周期,可以看到 Fragment 的可见性判断是正确的。
子 Fragment 的生命周期会根据所绑定的 Activity 或父 Fragment 的生命周期变化而变化,setMaxLifecycle 改变了父 Fragment 的生命周期,子 Fragment 的生命周期自然就跟着变化了。所以,仅监听 Fragment 的 onResume 和 onPause 就能判断 Fragment 的可见性,不需要调整判断逻辑。
在 determineFragmentVisible 中增加判断父 Fragment 是否可见的代码:
private fun determineFragmentVisible() {
val parent = parentFragment
if (parent != null && parent is VisibilityFragment) {
if (!parent.isVisibleToUser()) {
// 父Fragment不可见,子Fragment也一定不可见
return
在 determineFragmentVisible 和 determineFragmentInvisible 增加判断子 Fragment 的可见性代码:
private fun determineFragmentVisible() {
if (isResumed && !isHidden && userVisibleHint && !mIsFragmentVisible) {
determineChildFragmentVisible()
private fun determineFragmentInvisible() {
if (mIsFragmentVisible) {
determineChildFragmentInvisible()
private fun determineChildFragmentVisible() {
childFragmentManager.fragments.forEach {
if (it is VisibilityFragment) {
it.determineFragmentVisible()
private fun determineChildFragmentInvisible() {
childFragmentManager.fragments.forEach {
if (it is VisibilityFragment) {
it.determineFragmentInvisible()
在实现了上述功能后,对于需要懒加载功能的 Fragment,只需要重写 onVisibleFirst,在里面加载数据就可以了。
对于全部使用 setMaxLifecycle 控制 Fragment 生命周期的代码,Fragment 的可见性判断相对比较简单,只要监听 Fragment 的 onResume 和 onPause 方法就能判断 Fragment 的可见性。
对于老的用法或者老的用法和 setMaxLifecycle 混用的代码,Fragment 可见性判断不仅要考虑使用方式,也要考虑父 Fragment 的可见性,同时自身可见性改变的时候,也要主动调用子 Fragment 判断可见性的代码。