Fragment对用户可见的判断实践,亲测有效

概述

相信很多使用过 Fragment 的朋友都对判断 Fragment 是否对用户可见有此疑问,网上有很多文章也介绍得比较片面,只覆盖到了其中一种情况。我在项目中也有遇到这样的问题,经过试验和积累,已经总结出判断的方法并进行了封装,在这里给大家简单介绍下。

先说说几个重要的函数

1. setUserVisibleHint

网上很多对这个方法的说明,这个方法只会在 ViewPager 和 FragmentPagerAdapter一起使用时才会触发。我们可以通过 getUserVisibleHint 来得到这个状态。

看下源码的说明:

  /**
     * Set a hint to the system about whether this fragment's UI is currently visible
     * to the user. This hint defaults to true and is persistent across fragment instance
     * state save and restore.
     *
     * <p>An app may set this to false to indicate that the fragment's UI is
     * scrolled out of visibility or is otherwise not directly visible to the user.
     * This may be used by the system to prioritize operations such as fragment lifecycle updates
     * or loader ordering behavior.</p>
     *
     * <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle.
     * and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p>
     *
     * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
     *                        false if it is not.
     */
    public void setUserVisibleHint(boolean isVisibleToUser) {
        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
                && mFragmentManager != null && isAdded()) {
            mFragmentManager.performPendingDeferredStart(this);
        }
        mUserVisibleHint = isVisibleToUser;
        mDeferStart = mState < STARTED && !isVisibleToUser;
    }
    /**
     * @return The current value of the user-visible hint on this fragment.
     * @see #setUserVisibleHint(boolean)
     */
    public boolean getUserVisibleHint() {
        return mUserVisibleHint;
    }

上面说了,setUserVisibleHint 是在一定场景下才会使用的,单纯用 getUserVisibleHint 来判断可见是不对的。 这仅仅适用于 ViewPager 的情况。

2. onHiddenChanged

onHiddenChanged 方法是在使用 show/hide 方法时会触发。来看下源码的说明:

/**
     * Called when the hidden state (as returned by {@link #isHidden()} of
     * the fragment has changed.  Fragments start out not hidden; this will
     * be called whenever the fragment changes state from that.
     * @param hidden True if the fragment is now hidden, false otherwise.
     */
    public void onHiddenChanged(boolean hidden) {
    }

在我们使用show(fragment)和 hide(fragment)改变了 fragment 的显示状态时,会触发此函数,并且可以通过 isHidden() 来获取当前显示隐藏的状态。

说说我项目中的封装

我在 BaseFragment 中封装了onVisible();和onInvisible();两个回调,业务只需要覆写这两个方法就能根据 Fragment 可见状态的改变来写逻辑。

PS:这里说明一下,我封装的可见不可见回调,是在状态改变的时候回调的。如果已经是 hide 不可见了,再执行 onPause 方法时我就不会触发 onInvisible 的回调了,所以业务端可以根据回调进行逻辑处理。

直接看代码吧,代码中有详细的注释说明,这里就不多说了。



    /**
     * 当fragment与viewpager、FragmentPagerAdapter一起使用时,切换页面时会调用此方法
     *
     * @param isVisibleToUser 是否对用户可见
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        boolean change = isVisibleToUser != getUserVisibleHint();
        super.setUserVisibleHint(isVisibleToUser);
        // 在viewpager中,创建fragment时就会调用这个方法,但这时还没有resume,为了避免重复调用visible和invisible,
        // 只有当fragment状态是resumed并且初始化完毕后才进行visible和invisible的回调
        if (isResumed() && change) {
            if (getUserVisibleHint()) {
                onVisible();
            } else {
                onInvisible();
            }
        }
    }

    /**
     * 当使用show/hide方法时,会触发此回调
     *
     * @param hidden fragment是否被隐藏
     */
    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (hidden) {
            onInvisible();
        } else {
            onVisible();
        }
    }


    @Override
    public void onResume() {
        super.onResume();
        // onResume并不代表fragment可见
        // 如果是在viewpager里,就需要判断getUserVisibleHint,不在viewpager时,getUserVisibleHint默认为true
        // 如果是其它情况,就通过isHidden判断,因为show/hide时会改变isHidden的状态
        // 所以,只有当fragment原来是可见状态时,进入onResume就回调onVisible
        if (getUserVisibleHint() && !isHidden()) {
            onVisible();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        // onPause时也需要判断,如果当前fragment在viewpager中不可见,就已经回调过了,onPause时也就不需要再次回调onInvisible了
        // 所以,只有当fragment是可见状态时进入onPause才加调onInvisible
        if (getUserVisibleHint() && !isHidden()) {
            onInvisible();
        }
    }

还是用一些场景来说明一下吧。

  1. 如果一个Fragment跳转到另一个 Activity,回来时会调用 Fragment 的 onResume 方法,这时,由于不在 ViewPager 中,getUserVisibleHint 默认是返回 true 的,那 Fragment 是否可见就依赖于 isHidden 方法了,如果跳转时是可见,那 isHidden 就是 false,执行 onVisible 回调,如果 跳转时不可见,那 isHidden 就是 true,那么就不会回调 onVisible了。
  2. 如果是在 ViewPager 中,因为 ViewPager 会自动调用 setUserVisibleHint 方法来改变可见状态,如果不在 onResume 中增加判断,会导致从别的 Activity 回来后,ViewPager 里所有的 Fragment 都执行 onVisible 回调了,实际上 ViewPager 只有一个Fragment 是当前可见的。

关于 setUserVisibleHint 还是多说几句,代码中有判断 isResumed ,是因为在ViewPager中,一创建 Fragment 时就调用了 setUserVisibleHint 方法,此时回调可见不可见是不合适的,因为还没有把 View 创建好,所以增加了 isResumed 判断,因为在 onResume 时也会进行判断并且回调的,也避免了重复调用 onVisible 和 onInvisible。

总结

判断 Fragment 对用户是否可见还是依赖于 getUserVisibleHint 和 isHidden 这两个重要方法的。这里也需要去理解 ViewPager 里 setUserVisibleHint 的作用,它只是把在屏幕外的 Fragment 加了一个标识,因为它也是被加到 window 中了,也是 onResume 状态了,所以用了一个新标识去表明不在屏幕内,标识为不可见。所以要结合判断,不能只判断其中一个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容