重新认识viewPaper

在学习viewpaper前,需要先了解预加载和懒加载。

1.什么是预加载?

Fragment切换的时候,会预先加载未可见的Fragment,就是预加载。

2.预加载的弊端:

a.预加载的越多就会越卡

b.一个Fragment占用 1M,5个就(5*1M),累计到后面就会OOM

c.如果预加载的Fragment在请求网络,不仅浪费流量,还会很卡顿

3.如何解决预加载的弊端?

懒加载

4.什么是懒加载?

防止预加载,用到才加载,可见才加载,不可见就不加载。

懒加载,其实也就是延迟加载,就是等到该页面的UI展示给用户时,再加载该页面的数据(从网络、数据库等),而不是依靠ViewPager预加载机制提前加载两三个,甚至更多页面的数据。

5.为什么要用懒加载?

这样可以提高所属Activity的初始化速度,也可以为用户节省流量.而这种懒加载的方式也已经/正在被诸多APP所采用。

接下来学习开始学习viewpaper源码:

6.阅读Viewpager源码

viewpaper需搭配adapter使用,而viewpaper控件中使用onMeasure()测量,onLayout()排版,onDraw()绘制。在onMeasure中有一个重要的方法populate(),这个方法,setOffscreenPageLimit里也会调用populate()。
图片

6.1. 阅读populate()方法可知道重要的适配器原理:【FragmentPagerAdapter extends PagerAdapter 】

a.startUpdate()准备适配

b.instantiateItem创建适配的item数据

c.destroyItem销毁适配的item数据

d.setPrimaryItem设置当前显示item数据

e.finishUpdate完成适配

可以知道==>

a是在准备缓存初始化工作

bcd是在处理缓存数据,增加和删除缓存节点item信息

e是触发缓存,启动真正的数据Item更新业务

6.2. 在FragmentPagerAdapter中分析setUserVisibleHint()的执行时机:【【extends PagerAdapter】】

可以发现,有两个方法里有调用setUserVisibleHint(),分别是instantiateItem()和setPrimaryItem()

举例:有fragment1,fragment2,fragment3,fragment4,当fragment1跳转到fragment3时,可以发现:

会优先缓存fragment4,设置setUserVisibleHint(false),

再处理当前fragment1,设置setUserVisibleHint(false),

最后处理目标fragment3,设置setUserVisibleHint(true),

可以发现:

  1. 由于先调用setPrimaryItem(),再调用finishUpdate(),所以setUserVisibleHint()会优先调用,再调生命周期内的方法。
  2. instantiateItem()中会调用1次setUserVisibleHint,一次false;在setPrimaryItem()中会调用2次setUserVisibleHint,一次是true,一次是false。

Fragment 生命周期按先后顺序:

onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume ->onPause -> onStop -> onDestroyView -> onDestroy -> onDetach

ViewPager + Fragment 的实现我们需要关注的几个生命周期有:
onCreatedView + onResume + onPause + onDestroyView

非生命周期函数:
setUserVisibleHint + onHiddenChanged

7.实际应用

举例:有tab1,tab2,tab3,tab4,tab5,可以来回切换页面,tab2有一个嵌套的viewpaper,tab5有一个按钮,点击进入新的activity。下面将对他们进行优化

优化步骤

1.由tab1切换到tab2,运行奔溃,提示子fragment类中断更新onFragmentLoadStop对ui操作。

原因:由于setUserVisibleHint在生命周期函数之前执行,缓存的tab3会执行setUserVisibleHint(false),而onCreateView还来不及执行,因为就没有执行其中的initView()方法。

解决:新增isViewCreated判断,isViewCreated为true则能够执行分发可见和不可见的动作。
在这里插入图片描述
在这里插入图片描述

2.第一次打开页面时,页面一直处于loading的状态。

原因:执行动作的方法在setUserVisibleHint中,而执行这个方法时isViewCreateed为false。当生命周期方法走到onCreateView()时,isViewCreateed=true,可是并不会主动再调用一次setUserVisibleHint(true)的方法

解决:手动调用setUserVisibleHint(true),使得相关动作得以执行。
在这里插入图片描述

3.打开和关闭页面时,发现,有重新更新加载和重复暂停加载的日志

原因:需要从“不可见->可见变化过程”才是可见(加载);“可见”->"不可见"才是可见(停止)

解决:新增isVisibleStateUP用于记录上一次可见的状态,

在分发 可见 不可见 的动作的方法dispatchUserVisibleHint里:

4.当点击tab5进入一个新activity,再返回tab5时候,都没有执行setUserVisibleHint方法,只执行了onResume和onPause。

原因:从源码中可知,在ViewPager的populate函数中,当mAdapter去执行setPrimarvItem时,才会执行setUserVisibleHint。它是非生命周期函数。

解决:在onResume和onPause中,根据getUserVisibleHint() 和isVisibleStateUP,判断是否需要执行分发的方法。
在这里插入图片描述
5.

1).当执行tab1时,tab2内嵌套的fragment也会执行

原因:目标tab1时,会默认预加载tab2,执行生命周期方法onCreateView,会初始化嵌套的fragment中的第一个。

解决:判断t2可见并且嵌套的父控件也可见时,才执行。
在这里插入图片描述

// TODO 判断 父控件 是否可见, 什么意思? 例如: Fragment2_vp1子Fragment 的 父亲/父控件==Fragment2
在这里插入图片描述

2).从tab1切换到tab2时,tab2里面嵌套的viewPager的fragment默认不会分发执行

解决:在双重ViewPager嵌套的情况下,第一次滑到Frgment 嵌套ViewPager(fragment)的场景的时候此时只会加载外层Fragment的数据,而不会加载内嵌viewPager中的fragment的数据,因此,我们需要在此增加一个当外层Fragment可见的时候,分发可见事件给自己内嵌的所有Fragment显示。即需手动分发执行嵌套fragment里的。
在这里插入图片描述
重要代码:

public abstract class LazyFragment extends Fragment {
    FragmentDelegater mFragmentDelegater;
    private View rootView = null;
    private boolean isViewCreated = false;//view是否加载
    private boolean isVisibleStateUP = false;// 记录上一次可见的状态

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        E("onCreateView");
        if (rootView == null) {
            rootView = inflater.inflate(getLayoutRes(), container, false);
        }
        isViewCreated = true;
        initView(rootView);
        if (getUserVisibleHint()) {//tip2:手动调用使其执行
            setUserVisibleHint(true);
        }
        return rootView;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        E("setUserVisibleHint");
        if (isViewCreated) {//tip1:isViewCreated为true则能够执行分发可见和不可见的动作
            if (isVisibleToUser && !isVisibleStateUP) {
                dispatchUserVisibleHint(true);
            } else if (!isVisibleToUser && isVisibleStateUP) {
                dispatchUserVisibleHint(false);
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        E("onResume");
        //tip4.从不可见到可见,说明可见
        if (getUserVisibleHint() && !isVisibleStateUP) {
            dispatchUserVisibleHint(true);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        E("onPause");
        //tip4.从可见到不可见,说明不可见
        if (getUserVisibleHint() && isVisibleStateUP) {
            dispatchUserVisibleHint(false);
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        E("onDestroyView");
    }

    // 让子类完成,初始化布局,初始化控件
    protected abstract void initView(View rootView);

    protected abstract int getLayoutRes();

    // -->>>停止网络数据请求
    public void onFragmentLoadStop() {
        E("onFragmentLoadStop");
    }

    // -->>>加载网络数据请求
    public void onFragmentLoad() {
        E("onFragmentLoad");
    }

    // 分发 可见 不可见 的动作 【第一版 1.2】
    private void dispatchUserVisibleHint(boolean visibleState) {
        this.isVisibleStateUP = visibleState;

        if (visibleState && isParentInvisible()) {
            return;
        }

        if (visibleState) {
            // 加载数据
            onFragmentLoad();  // 都是对第一层有效,嵌套无效
            dispatchChildVisibleState(true);
        } else {
            // 停止一切操作
            onFragmentLoadStop();  // 都是对第一层有效,嵌套无效
            dispatchChildVisibleState(false);
        }
    }

    //  tip6:为了解决第二个问题,T1 到 T2 T2里面嵌套的ViewPager的Fragment默认不会分发执行
    //  解决:需要手动的分发执行嵌套Fragment里面的
    private void dispatchChildVisibleState(boolean b) {
        FragmentManager fragmentManager = getChildFragmentManager();
        List<Fragment> fragmentList = fragmentManager.getFragments();
        if (fragmentList != null) {
            for (Fragment fragment : fragmentList) {
                if (fragment instanceof LazyFragment && !fragment.isHidden() && fragment.getUserVisibleHint()) {
                    ((LazyFragment) fragment).dispatchUserVisibleHint(b);
                }
            }
        }
    }

    private boolean isParentInvisible() {
        Fragment parentFragment = getParentFragment();
        if (parentFragment instanceof LazyFragment) {
            LazyFragment fragment = (LazyFragment) parentFragment;
            return !fragment.isVisibleStateUP;
        }
        return false;
    }

    public void setFragmentDelegater(FragmentDelegater mFragmentDelegater) {
        this.mFragmentDelegater = mFragmentDelegater;
    }

    private void E(String string) {
        if (mFragmentDelegater != null) {
            mFragmentDelegater.dumpLifeCycle(string);
        }
    }
}

8. viewPager1和viewPager2 区别#

懒加载对比:

viewPager1是由setUsetVisibleHint和生命周期方法(onCreateView、onResume)等等配合实现的懒加载;

viewPager2中setUsetVisibleHint已过时,是由setMaxLifecycle代替

缓存页面对比:

viewPager1是由populate中的重要适配器原理等配合实现的缓存页面

viewPager2是由RecycleView中的缓存机制来实现的

使用方面对比:

a.基于RecyclerView实现。这意味着RecyclerView的优点将会被ViewPager2所继承。

b.支持竖直滑动。只需要一个参数就可以改变滑动方向。

c.支持关闭用户输入。通过setUserInputEnabled来设置是否禁止用户滑动页面

d.FragmentStatePagerAdapter 被 FragmentStateAdapter 替代

e.addPageChangeListener 被 registerOnPageChangeCallback替代

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值