前言
本篇文章主要提供一种监听 Fragment 可见性监听的方案,完美多种 case,有兴趣的可以看看。废话不多说,开始进入正文。
在开发当中, fragment 经常使用到。在很多应用场景中,我们需要监听到 fragment 的显示与隐藏,来进行一些操作。比如,统计页面的停留时长,页面隐藏的时候停止播放视频。
有些同学可能会说了,这还不容易,直接监听 Fragment 的 onResume,onPause。我只能说,兄弟,too young,too simple。
下面,让我们一起来实现 fragment 的监听。主要分为几种 case
一个页面只有一个 fragment 的,使用 replace
Hide 和 Show 操作
ViewPager 嵌套 Fragment
宿主 Fragment 再嵌套 Fragment,比如 ViewPager 嵌套 ViewPager,再嵌套 Fragment
Replace 操作
replace 操作这种比较简单,因为他会正常调用 onResume 和 onPause 方法,我们只需要在 onResume 和 onPause 做 check 操作即可
1 override fun onResume() {
2 info("onResume")
3 super.onResume()
4 onActivityVisibilityChanged(true)
5 }
6
7
8 override fun onPause() {
9 info("onPause")
10 super.onPause()
11 onActivityVisibilityChanged(false)
12 }
Hide 和 Show 操作
Hide 和 show 操作,会促发生命周期的回调,但是 hide 和 show 操作并不会,那么我们可以通过什么方法来监听呢?其实很简单,可以通过 onHiddenChanged 方法
1 /**2 * 调用 fragment show hide 的时候回调用这个方法3 */
4 override fun onHiddenChanged(hidden: Boolean) {
5 super.onHiddenChanged(hidden)
6 checkVisibility(hidden)
7 }
ViewPager 嵌套 Fragment
ViewPager 嵌套 Fragment,这种也是很常见的一种结构。因为 ViewPager 的预加载机制,在 onResume 监听是不准确的。
这时候,我们可以通过 setUserVisibleHint 方法来监听,当方法传入值为true的时候,说明Fragment可见,为false的时候说明Fragment被切走了
1public void setUserVisibleHint(boolean isVisibleToUser)2
有一点需要注意的是,个方法可能先于Fragment的生命周期被调用(在FragmentPagerAdapter中,在Fragment被add之前这个方法就被调用了),所以在这个方法中进行操作之前,可能需要先判断一下生命周期是否执行了。
1 /** 2 * Tab切换时会回调此方法。对于没有Tab的页面,[Fragment.getUserVisibleHint]默认为true。 3 */
4 @Suppress("DEPRECATION")
5 override fun setUserVisibleHint(isVisibleToUser: Boolean) {
6 info("setUserVisibleHint = $isVisibleToUser")
7 super.setUserVisibleHint(isVisibleToUser)
8 checkVisibility(isVisibleToUser)
9 }
10
11 /**12 * 检查可见性是否变化13 *14 * @param expected 可见性期望的值。只有当前值和expected不同,才需要做判断15 */
16 private fun checkVisibility(expected: Boolean) {
17 if (expected == visible) return
18 val parentVisible = if (localParentFragment == null) {
19 parentActivityVisible
20 } else {
21 localParentFragment?.isFragmentVisible() ?: false
22 }
23 val superVisible = super.isVisible()
24 val hintVisible = userVisibleHint
25 val visible = parentVisible && superVisible && hintVisible
26 info(
27 String.format(
28 "==> checkVisibility = %s ( parent = %s, super = %s, hint = %s )",
29 visible, parentVisible, superVisible, hintVisible
30 )
31 )
32 if (visible != this.visible) {
33 this.visible = visible
34 onVisibilityChanged(this.visible)
35 }
36 }