Fragment懒加载(支持嵌套) 友盟统计Fragment时长最佳实践

来源:互联网 发布:mysql数据库用户名 编辑:程序博客网 时间:2024/06/05 05:44

前言

在项目中我们可能需要获取Fragment可见或者不可见时的回调。(回调这个词在这里用的可能并不准确,这里理解为当Fragment可见或者不可见时能够触发一些方法,下同)。

Fragmeng生命周期中有onResume,onPause,这两个生命周期是跟随Activity的。当调用getSupportFragmentManager().beginTransaction().hide(fragment)时或者滑动ViewPager隐藏Fragment时,Fragment的这两个生命周期都不会回调。

那么,如何得到Fragment可见时的回调呢?如何得到Fragment不可见时的回调呢?

思路

首先先定义一个BetterLifecycleFragment继承自Fragment。我希望当Fragment可见状态改变时,子类可以有对应的生命周期的回调。如 onFragmentResume, onFragmentPause。如下:

public class BetterLifecycleFragment extends Fragment {    /**     * Fragment 可见时回调     * @param isFirst 是否是第一次显示     */    protected void onFragmentResume(boolean isFirst){    }    /**     * Fragment 不可见时回调     */    protected void onFragmentPause(){    }}
  • 注意到这里 onFragmentResume 里有一个 isFirst参数,这个参数是为了方便懒加载。如果是第一次,则去请求数据。

  • 统计Fragment时长也很简单,把原来onResume和onPause中的方法搬到这两个方法里即可。

判断Fragment可见的充分条件

我们先思考Fragment不可见都有几种情况:
1. Activity 进入后台,Fragment#onPause回调。
2. ViewPager中被滑出屏幕之外的Fragment
我们可以通过 getUserVisibleHint() 方法判断Fragment是否在屏幕之中。
3. Activity执行getSupportFragmentManager().beginTransaction().hide(fragment)的Fragment。
这时 Fragment#onHiddenChanged(boolean hidden)方法会执行。

除去上面三种情况,Fragment对于用户就是可见的了。

我们在 BetterLifecycleFragment中添加isFragmentVisible()方法用来判断Fragment是否可见。

public class BetterLifecycleFragment extends Fragment {    private boolean isResuming = false;      private boolean hidden = false;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        hidden = false;    }        @Override    public void onResume() {        super.onResume();        isResuming = true;    }    @Override    public void onPause() {        super.onPause();        isResuming = false;    }    @Override    public void onHiddenChanged(boolean hidden) {        super.onHiddenChanged(hidden);        this.hidden = hidden;    }    /**     * Fragment是否可见     * @return     */    public boolean isFragmentVisible() {        if (isResuming()                && getUserVisibleHint()                && !hidden) {            return true;        }        return false;    }    /**     * Fragment 是否在前台。     * @return     */    private boolean isResuming() {        return isResuming;    }}

注意这里我们并没有使用系统的 isResumed()以及isHiddlen();
为什么呢?不妨自己在onPause以及onHiddenChanged(boolean hidden)里面打印一下这两个方法就知道了。

完整实现

上述已经说过,Fragment不可见有三种情况。同理Fragment可见也和这三种情况有关。

Fragment可见状态改变的三种情况:
1. Fragment进入前台或者进入后台:
对应 onResume 和 onPause
2. ViewPager 滑动:
Fragment 的setUserVisibleHint(boolean isVisibleToUser) 触发。(事实上这个方法根本不会改变Fragment是否可见,只是告诉我们Fragment是否在屏幕之中。)
3. FragmentManager 调用了show或者hide:
Fragment 的onHiddenChanged(boolean hidden) 触发。

我们只需在Fragment中可能改变其可见状态的方法中调用我们的 isFragmentVisible(),然后和上次Fragment的可见状态做个比较,即可得到Fragment可见以及不可见的回调。

完整代码如下:

public class BetterLifecycleFragment extends Fragment {    private boolean isLastVisible = false;    private boolean hidden = false;    private boolean isFirst = true;    private boolean isResuming = false;    private boolean isViewDestroyed = false;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        isLastVisible = false;        hidden = false;        isFirst = true;        isViewDestroyed = false;    }    @Override    public void onResume() {        super.onResume();        isResuming = true;        tryToChangeVisibility(true);    }    @Override    public void onPause() {        super.onPause();        isResuming = false;        tryToChangeVisibility(false);    }    @Override    public void onDestroyView() {        super.onDestroyView();        isViewDestroyed = true;    }    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        setUserVisibleHintClient(isVisibleToUser);    }    private void setUserVisibleHintClient(boolean isVisibleToUser) {        tryToChangeVisibility(isVisibleToUser);        if (isAdded()) {            // 当Fragment不可见时,其子Fragment也是不可见的。因此要通知子Fragment当前可见状态改变了。            List<Fragment> fragments = getChildFragmentManager().getFragments();            if (fragments != null) {                for (Fragment fragment : fragments) {                    if (fragment instanceof BetterLifecycleFragment) {                        ((BetterLifecycleFragment) fragment).setUserVisibleHintClient(isVisibleToUser);                    }                }            }        }    }    @Override    public void onHiddenChanged(boolean hidden) {        super.onHiddenChanged(hidden);        onHiddenChangedClient(hidden);    }    public void onHiddenChangedClient(boolean hidden) {        this.hidden = hidden;        tryToChangeVisibility(!hidden);        if (isAdded()) {            List<Fragment> fragments = getChildFragmentManager().getFragments();            if (fragments != null) {                for (Fragment fragment : fragments) {                    if (fragment instanceof BetterLifecycleFragment) {                        ((BetterLifecycleFragment) fragment).onHiddenChangedClient(hidden);                    }                }            }        }    }    private void tryToChangeVisibility(boolean tryToShow) {        // 上次可见        if (isLastVisible) {            if (tryToShow) {                return;            }            if (!isFragmentVisible()) {                onFragmentPause();                isLastVisible = false;            }            // 上次不可见        } else {            boolean tryToHide = !tryToShow;            if (tryToHide) {                return;            }            if (isFragmentVisible()) {                onFragmentResume(isFirst, isViewDestroyed);                isLastVisible = true;                isFirst = false;            }        }    }    /**     * Fragment是否可见     *     * @return     */    public boolean isFragmentVisible() {        if (isResuming()                && getUserVisibleHint()                && !hidden) {            return true;        }        return false;    }    /**     * Fragment 是否在前台。     *     * @return     */    private boolean isResuming() {        return isResuming;    }    /**     * Fragment 可见时回调     *     * @param isFirst       是否是第一次显示     * @param isViewDestroyed Fragment中的View是否被回收过。     * 存在这种情况:Fragment 的 View 被回收,但是Fragment实例仍在。     */    protected void onFragmentResume(boolean isFirst, boolean isViewDestroyed) {    }    /**     * Fragment 不可见时回调     */    protected void onFragmentPause() {    }}

值得注意:
1. 如果Fragment存在嵌套,那么父Fragment的可见状态将影响到子Fragment的可见状态。所以父Fragment在可见状态改变时要通知子Fragment可见状态发生改变了。
2. 考虑一种情况,Fragment的View被回收,但是Fragment实例仍在,这种在ViewPager中是存在的。所以在onFragmentResume方法中我加入了一个isViewDestroyed表示Fragment的View是否被收回过。

懒加载实现如下:

@Overrideprotected void onFragmentResume(boolean isFirst, boolean isViewDestroy) {    if(isFirst){        loadData();    } else if(isViewDestroy){        // 为View设置数据。当然也可以在onCreateView或者onActivityCreate中设置数据。        setViews();    }}

友盟统计Fragment时长只需把原来onResume和onPuase中的方法copy到onFragmentResume及onFragmentPause方法中即可。

点我下载源码

0 0
原创粉丝点击