Android进阶学习(11)-- ViewPager嵌套Fragment懒加载处理

ViewPager嵌套Fragment懒加载处理

    • 需要懒加载的情况分析
    • 结合生命周期的坑点
    • Fragment嵌套情况处理
    • 完整代码

需要懒加载的情况分析

  1. fragment可见:
    分为两种(1)第一次可见(2)来回切换后可见
    第一次可见:意味着加载的数据更多,网络请求更多。
    来回切换后可见:可能在第一次网络请求后将数据缓存在本地,那么只需要读取缓存即可;
  2. fragment不可见:
    当fragment不可见时,需要中断网络请求(或者轮播图Banner等)

综合以上两个可见和不可见状态,能够感知的生命周期只有onResume和onPasue,外加一个setUserVisibleHint方法,那么懒加载也就是在这三个方法中分发正确的事件,可以将第一次加载,懒加载,中断,这三个操作抽出成为方法,由子类实现,父类负责分发。

结合生命周期的坑点

上面的两种情况分析,看上去非常的简单,实际上懒加载也就是完成了以上的工作,但是,实际代码中,会有坑,onResume和onPause。
先说onResume需要考虑的问题:
假设,现在有三个tab一个viewpager,每个tab对应着一个fragment。当tab1切换到tab2时,由于viewpager中的setOffscreenPageLimit默认值为1,也就意味着tab3此时会走onResume的生命周期;
再比如:Activity1中有多个fragment,此时有Activity1跳转到Activity2,那么Activity1中会缓存多个Fragment,当由Activity2返回Activity1时,会走fragment的onResume生命周期;
那么onResume要做如下处理:
伪代码:

public void onResume() {
        super.onResume();
        //这个if是为了解决tab来回切换的问题,当切换到tab2时,tab3已经执行onResume,但是view还没创建
        //所以需要增加一层判断,当第一次可见时,再去分发事件
        if(如果第一次可见){ 
        	//由于activity切换时,fragment不会走setUserVisibleHint方法,所以也需要增加一层判断
            if (没有hidden && 当前状态不可见 && getUserVisibleHint()){
                //分发可见事件
            }
        }
    }

解决完onResume,继续解决onPause
onPause不需要onResume里的第一层判断,因为只要执行onPause那说明已经执行过了onCreate以及onResume。同样,当Activity1跳转到Activity2时,1里面的所有fragment都会执行onPause,那么只需要处理由可见变为不可见状态的fragment,由可见变为不可见时,需要分发事件,处理中断操作。
伪代码:

    public void onPause() {
        super.onPause();
        //这里也就是说 当前可见的Fragment 需要分发中断事件
        if (当前可见 && getUserVisibleHint()){
            //分发不可见事件
        }
    }

Fragment嵌套情况处理

bug出现情况,假设当前有三个tab,每个tab对应一个fragment,tab2对应的fragment里嵌套了一层viewpager,当tab1加载时,tab2对应的fragment里面嵌套的viewpager里的第一个fragment也会执行onResume。这个情况是非常容易被忽略掉的,处理的逻辑很简单,在分发事件时,首先判断父Fragment是否可见,只有可见情况下才会继续分发。

完整代码

使用时,对应的Fragment继承该父类,子类主动实现firstLoad、lazyLoad、interrupt 三个方法即可。如果有嵌套,嵌套的Fragment也继承该父类即可

public abstract class LazyFragment extends Fragment {

    protected View mRootView = null; //view复用
    protected boolean isCreateView = false; //是否创建
    protected boolean currentVisibleState = false; //当前可见状态
    protected boolean isFirstVisible = true; //是否第一次可见

    protected abstract int setLayoutId();
	
	//初始化view
    protected abstract void initView(View view);

	//第一次加载 留给子类实现
    protected void firstLoad(){
    }
	
	//切换之后的懒加载 留给子类实现
    protected void lazyLoad() {
    }
	
	//切换后中断加载 留给子类实现
    protected void interrupt() {
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    	//父view复用 
        if (mRootView == null) {
            mRootView = inflater.inflate(setLayoutId(), container, false);
        }
        initView(mRootView);
        //已经创建view
        isCreateView = true;
        //第一次加载
        if (!isHidden() && getUserVisibleHint()) {
            dispatchLazyLoadOrInterrupt(true);
        }
        return mRootView;
    }

    @Override
    public void onResume() {
        super.onResume();
        /**
         * 已经加载过的 fragment 会遇到的情况
         *     fragment 所在的 activity 跳转到其他 activity
         *     在 activity 返回时 需要加载
         *     只加载可见 fragment
         */
        if(!isFirstVisible){
            if (!isHidden() && !currentVisibleState && getUserVisibleHint()){
                dispatchLazyLoadOrInterrupt(true);
            }
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        //是否需要分发中断事件
        if (currentVisibleState && getUserVisibleHint()){
            dispatchLazyLoadOrInterrupt(false);
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isCreateView) { // createview 之后才可以加载数据
            if (!currentVisibleState && isVisibleToUser) { //不可见 -> 可见
                dispatchLazyLoadOrInterrupt(true);
            } else if (currentVisibleState && !isVisibleToUser) { // 可见 -> 不可见
                dispatchLazyLoadOrInterrupt(false);
            }
        }
    }

    /**
     * 处理fragment切换时 懒加载 和 中断
     * 可见 -> 不可见 = 中断操作,网络请求等
     * 不可见 -> 可见 = 懒加载
     * @param isVisible
     */
    private void dispatchLazyLoadOrInterrupt(boolean isVisible) {
        // ParentFragment不可见 则不处理
        // 处理fragment里嵌套fragment的情况
        if (isVisible && isParentInVisible()) {
            return;
        }
        if (currentVisibleState == isVisible) {
            return;
        }
        // save当前可见性
        currentVisibleState = isVisible;
        if (isVisible) {
            /**
             * fragment 第一次加载
             * 主要处理这种情况:
             *     fragment第一次加载网络请求的数据缓存到本地,之后加载去读取本地
             *     那么,第一次网络请求通过 firstLoad,之后在 lazyLoad 中读取本都缓存
             */
            if (isFirstVisible){
                isFirstVisible = false;
                firstLoad();
            }
            lazyLoad();
            dispatchChildLazyLoadOrInterrupt(isVisible);
        } else {
            interrupt();
            dispatchChildLazyLoadOrInterrupt(isVisible);
        }
    }

    /**
     * 当 fragment 中嵌套 fragment 时要处理 可见的childfragment
     * @param isVisible
     */
    public void dispatchChildLazyLoadOrInterrupt(boolean isVisible){
        FragmentManager fm = getChildFragmentManager();
        List fragmentList = fm.getFragments();
        for (Fragment item : fragmentList){
            if (item instanceof LazyFragment && !isHidden() && item.getUserVisibleHint()){
                ((LazyFragment) item).dispatchLazyLoadOrInterrupt(isVisible);
            }
        }
    }

    /**
     * 当 fragment 通过 FragmentTransaction 的 show/hide 方法
     * 改变 Fragment 可见性时,在这里处理调度逻辑
     * @param hidden
     */
    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (hidden){
            dispatchLazyLoadOrInterrupt(false);
        }else {
            dispatchLazyLoadOrInterrupt(true);
        }
    }

    /**
     * ParentFragment 是不是不可见
     *
     * @return true -> 不可见 / false -> 可见
     */
    private boolean isParentInVisible() {
        Fragment parent = getParentFragment();
        if (parent instanceof LazyFragment) {
            LazyFragment temp = (LazyFragment) parent;
            return !temp.getCurrentVisibleState();
        }
        return false;
    }

    /**
     * 获取 fragment 当前的可见性
     * @return
     */
    private boolean getCurrentVisibleState() {
        return currentVisibleState;
    }
}

你可能感兴趣的:(Android,进阶学习)