Android-技术AndroidAndroid应用开发那些事

Fragment的setUserVisibleHint方法实现懒

2017-01-03  本文已影响1793人  Android绝世小菜鸟

最近有个需求,需要在Fragment中实现懒加载,当Fragment中可见的时候才去加载数据和做一些页面的操作,当ViewPager+Fragment一起的时候,ViewPager为了让滑动的时候可以有很好的用户的体验,也就是防止出现卡顿现象,因此它有一个缓存机制。默认情况下,ViewPager会提前创建好当前Fragment旁的两个Fragment,举个例子说也就是如果你当前显示的是编号3的Fragment,那么其实编号2和4的Fragment也已经创建好了,也就是说这3个Fragment都已经执行完 onAttach() -> onResume() 这之间的生命周期函数了。

Paste_Image.png

本来Fragment的 onResume()表示的是当前Fragment处于可见且可交互状态,但由于ViewPager的缓存机制,它已经失去了意义,也就是说我们只是打开了“福利”这个Fragment,但其实“休息视频”和“拓展资源”这两个Fragment的数据也都已经加载好了。
如果加载数据的操作都比较耗时或者都是类似图片的占用大量内存,这时就应该考虑想想是否该实现懒加载。也就是,当我打开哪个Fragment的时候,它才会去加载数据。

这里就用到了Fragment里setUserVisibleHint方法,下面有两种解决方案

1.第一种实现方法: 父类封装

该方法用于告诉系统,这个Fragment的UI是否是可见的。所以我们只需要继承Fragment并重写该方法,即可实现在fragment可见时才进行数据加载操作,即Fragment的懒加载。

public abstract class LazyFragment extends Fragment {
    protected boolean isVisible;
    /**
     * 在这里实现Fragment数据的缓加载.
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {//当可见的时候执行操作
            isVisible = true;
            onVisible();
        } else {//不可见时执行相应的操作
            isVisible = false;
            onInvisible();
        }
    }
    protected void onVisible(){
        lazyLoad();
    }
    protected abstract void lazyLoad();//子类实现
    protected void onInvisible(){}
}

在LazyFragment,我增加了三个方法,一个是onVisiable,即fragment被设置为可见时调用,一个是onInvisible,即fragment被设置为不可见时调用。另外再写了一个lazyLoad的抽象方法,该方法在onVisible里面调用。你可能会想,为什么不在getUserVisibleHint里面就直接调用呢?

我这么写是为了代码的复用。因为在fragment中,我们还需要创建视图(onCreateView()方法),可能还需要在它不可见时就进行其他小量的初始化操作(比如初始化需要通过AIDL调用的远程服务)等。而setUserVisibleHint是在onCreateView之前调用的,那么在视图未初始化的时候,在lazyLoad当中就使用的话,就会有空指针的异常。而把lazyLoad抽离成一个方法,那么它的子类就可以这样做:

public class OpenResultFragment extends LazyFragment{
    // 标志位,标志已经初始化完成。
    private boolean isPrepared;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(LOG_TAG, "onCreateView");
        View view = inflater.inflate(R.layout.fragment_open_result, container, false);
        //XXX初始化view的各控件
    isPrepared = true;
        lazyLoad();
        return view;
    }
    @Override
    protected void lazyLoad() {
        if(!isPrepared || !isVisible) {
            return;
        }
        //填充各控件的数据
    }
}

在上面的类当中,我们增加了一个标志位isPrepared,用于标志是否初始化完成。然后在我们所需要的初始化操作完成之后调用,如上面的例子当中,在初始化view之后,设置 isPrepared为true,同时调用lazyLoad()方法。而在lazyLoad()当中,判断isPrepared和isVisible只要有一个不为true就不往下执行。也就是仅当初始化完成,并且可见的时候才继续加载,这样的避免了未初始化完成就使用而带来的问题。

2.第二种实现方法:单个Fragment需要懒加载,onResume中实现

 @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            setClazyShowUi();
        } else{ //这个是设置Fragment不可见时调用,这里没有用到,所以不需要
        }
    }
    //设置懒加载数据
    private void setClazyShowUi(){
        if (MemberManager.isLogin()) {
            loadUserInfo();
            fragmentMineBinding.rlMineLogin.setVisibility(View.GONE);
            fragmentMineBinding.rlMineUserInfo.setVisibility(View.VISIBLE);
        } else {
            onUpdateUI(MemberManager.getMember());
            fragmentMineBinding.rlMineLogin.setVisibility(View.VISIBLE);
            fragmentMineBinding.rlMineUserInfo.setVisibility(View.GONE);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if(getUserVisibleHint()) {
            setClazyShowUi();
        }
    }

上面的方法避免了在onCreateView中因为View还没有初始化的时候,出现空指针的情况,因为onResume执行的时候,onCreate已经执行完了,其实第一种也可以使用onResume的形式,原理都是一样的.

上一篇 下一篇

猜你喜欢

热点阅读