Android Fragment在用户视角可见性监听 众所周知,Anroid 的 Fragment 的生命周期十分复杂。并且 Fragment 在不同的组建中甚至有不同的可见性实现,比如早期的 ViewPager 通过调用 Fragment 的 setUserVisibleHint 方法来控制其可见性,而其他一些组建也会通过 FragmentTransaction 的 hide 或者 show 方法实现对 Fragment 的隐藏和显示。 除此之外,其本身的生命周期 onPause() 和 onResume() 也会影响其可见性。在 onResume 之前 Fragment 都是不可见的。没有了吗?不是的,还有一种情况会影响 Fragment 的生命周期,那就是其父 Fragment 的可见性。当其父 Fragment 不可见时,其子 Fragment 不一定会收到任何回调,但是其依然会不可见。 由此可见,想要实时监听 Fragment 在用户视角的可见性是一件挺复杂的事情。需要考虑很多种情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 open class VisibleChangeListenerFragment : Fragment (), IPareVisibilityObserver { private var parentFragmentVisible = false private val listeners: MutableList<OnVisibleChangeListener> = mutableListOf() var visibleForUser = false private set (value) { if (field == value) { return } Log.d("tangrui" , "${this.javaClass.simpleName} visible = $value " ) field = value listeners.forEach { it.onVisibleChange(this , value) } notifyChildVisibleChange(value) } override fun onResume () { super .onResume() parentFragmentVisible = queryParentFragmentVisible() checkAndSetVisible() } override fun onPause () { visibleForUser = false super .onPause() } override fun onDestroy () { listeners.clear() super .onDestroy() } override fun setUserVisibleHint (isVisibleToUser: Boolean ) { super .setUserVisibleHint(isVisibleToUser) checkAndSetVisible() } override fun onHiddenChanged (hidden: Boolean ) { super .onHiddenChanged(hidden) checkAndSetVisible() } private fun checkAndSetVisible () { visibleForUser = isResumed && !isHidden && userVisibleHint && parentFragmentVisible } fun addOnVisibleChangeListener (listener: OnVisibleChangeListener ) { listeners.add(listener) } fun removeOnVisibleChangeListener (listener: OnVisibleChangeListener ) { listeners.remove(listener) } override fun onParentFragmentVisibleChanged (visible: Boolean ) { if (!visible) { parentFragmentVisible = false checkAndSetVisible() } else { parentFragmentVisible = queryParentFragmentVisible() checkAndSetVisible() } } private fun queryParentFragmentVisible () : Boolean { var parent = parentFragment var parentVisibleForUser = true while (parent != null ) { if (parent is VisibleChangeListenerFragment) { if (!parent.visibleForUser) { parentVisibleForUser = false break } } else { if (parent.isHidden || !parent.isResumed || !parent.userVisibleHint) { parentVisibleForUser = false break } } parent = parent.parentFragment } return parentVisibleForUser } private fun notifyChildVisibleChange (visible: Boolean ) { if (isDetached || !isAdded) { return } val fragmentManager = childFragmentManager val fragments = fragmentManager.fragments for (fragment in fragments) { if (fragment !is IPareVisibilityObserver) { continue } (fragment as IPareVisibilityObserver).onParentFragmentVisibleChanged(visible) } } interface OnVisibleChangeListener { fun onVisibleChange (fragment: VisibleChangeListenerFragment , visible: Boolean ) } } interface IPareVisibilityObserver { fun onParentFragmentVisibleChanged (visible: Boolean ) }