ViewPager+Fragment LazyLoad最优解

作者: 尹star | 来源:发表于2016-03-25 01:31 被阅读8630次

ViewPager+Fragment的模式再常见不过了,以国民应用微信为例,假设微信也是ViewPager+Fragment的实现方式,那表现形式上就是一个ViewPager管理了四个Fragment,左右滑动来回切换。但是ViewPager有一个奇葩的特性叫:预加载,比如打开微信,首先看到的是第一个Tab(微信),但事实上第二个Tab(通讯录)已经加载好了。当选择第二个Tab(通讯录),第三个Tab(发现)已经加载好了,以此类推。
但上诉ViewPager+Fragment的这种组合并不完美,因为我希望用户选择了哪个Tab再去加载哪个Tab的数据,而不要去做预加载,假如当前页面和预加载页面都有大量的网络请求,可能就会比较慢,有很多请求在排队。关于这个问题,也有很偏激的做法,比如弃用ViewPager,自己手动管理Fragment,或者直接禁掉ViewPager预加载。有一种比较合适的方案是保持ViewPager预加载的特性,但是只初始化View,选择当前Tab的时候再进行网络请求。关于这一方案的实现,也是众说纷纭,千奇百怪。最后,还是选择男神Stay的方案。
直接上代码。

public abstract class BasePageFragment extends Fragment {

    protected boolean isViewInitiated;
    protected boolean isVisibleToUser;
    protected boolean isDataInitiated;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        isViewInitiated = true;
        prepareFetchData();
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        this.isVisibleToUser = isVisibleToUser;
        prepareFetchData();
    }

    public abstract void fetchData();

    public boolean prepareFetchData() {
        return prepareFetchData(false);
    }

    public boolean prepareFetchData(boolean forceUpdate) {
        if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
            fetchData();
            isDataInitiated = true;
            return true;
        }
        return false;
    }

}

这是一个父类,看代码这里只有一个setUserVisibleHint需要说下,这是一个相当生僻的方法,我们可以用这个方法来判断当前UI是否可见,所以在prepareFetchData方法里我们做如下判断:就是当前UI可见,并且fragment已经初始化完毕,如果网络数据未加载,那么请求数据,或者需要强制刷新页面,那么也去请求数据,So easy。子Fragment只需要继承父类,实现抽象方法,在fetchData()里做网络请求或者其他耗时操作即可。再在写个子类吧。

public class PageFragment extends BasePageFragment {
    
    public static PageFragment newInstance(String title){
        PageFragment fragment = new PageFragment();
        Bundle args = new Bundle();
        args.putString("key_fragment_title", title);
        fragment.setArguments(args);
        return fragment;
    }

    private String title;
    private TextView tv;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        title = getArguments().getString("key_fragment_title");
        Trace.d(title + ":onCreate");
    }
    
    @Override
    public void onResume() {
        super.onResume();
        Trace.d(title + ":onResume");
    }
    
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Trace.d(title + ":onCreateView");
        tv = new TextView(getActivity());
        return tv;
    }

    @Override
    public void fetchData() {
        tv.setText(title);
        /** * 在这里请求网络。 */
    }
        
}

如果你也有这样的需求或者烦恼,保证药到病除。

有同学说实践过程中遇到了些问题,比如Fragment只刷新一次,这个问题只要手动调用prepareFetchData(),传true即可强制刷新了。还有同学质疑setUserVisibleHint()和onActivityCreated()的执行先后的问题。关于这个请看下图。

1.jpg

相关文章

  • 懒加载BaseFragment

    感谢:ViewPager+Fragment LazyLoad最优解

  • ViewPager+Fragment LazyLoad最优解

    ViewPager+Fragment的模式再常见不过了,以国民应用微信为例,假设微信也是ViewPager+Fra...

  • 改变自己:局部最优与全局最优

    局部最优与全局最优 思维模型 优化问题的局部最优解是指在临近解集合当中的最优(最大或者最小)解。相对应的是全局最优...

  • 贪心算法-Python刷题笔记

    贪心算法 贪心选择:通过一系列的局部最优解达到整体最优解。 前提:必须证明贪心选择可以达到最优解:先证明整体最优解...

  • 动态规划

    与贪心算法求局部最优解相比,动态规划求的是全局最优解(但不是每个问题都有最优解,比如NP完全问题就没有最优解) 例...

  • 从可行解到最优解的思考2021-02-11(未允禁转)

    实际问题一般包含若干组可行解,所有可行解中又存在最优解可行解往往是一些局部最优解,甚至连局部最优都不具备;而最优解...

  • 五大基本算法——动态规划算法

    一、基本要素 1、最优子结构性质 大问题的最优解包含了小问题的最优解,小问题的最优解又可以合并成大问题的最优解。 ...

  • O.M.

    1.最优化问题 无约束:约束: 2.最优解 [严格]局部最优解:[严格]全局最优解: 3.函数 n元单值函数的一阶...

  • 人工智能非信息不对称状态下的动态零和博弈-斗地主心得

    不懂什么是博弈 棋牌总是输股市总是赔钱 博弈中你的最优解 不是我的最优解 也不是对手他的最优解 最优解出现矛盾现象...

  • 动态规划:最长公共子序列

    动态规划 步骤 描述最优解的结构 递归定义最优解的值 按自底向上的方式计算最优解的值 //此3步构成动态规划解...

网友评论

  • 虚心学习的小来子:请问如果有五个fragment,第二个fragment的第一个fragment怎么保证它不会预加载呢?
  • ce6dce22ceb4:有个问题:启动的时候设置默认滑动第N个fragment位置,第一个fragment也会走prepareFetchData加载数据
  • bd8e6ef77b3b:评论我的头像怎么是反的
  • Yx_____________:如果有很多fragment ,怎么控制,貌似滑到选第五个的时候再点第一个就是空白的了。
    曾经的你呀:@尹star 像网易新闻那样的话,一般用FragmenStatePagerAdapter。试了一下,在第一个Fragment 的时候,往下阅读一段,再滑到第5个Fragment后又回到一个Fragment,状态还是我阅读的那个位置。也就是像网易新闻那样的话,用FragmenStatePagerAdapter怎么恢复记住被destroy 的状态啊
    尹star:@Yx_____________ Viewpager有一个 viewPager.setOffscreenPageLimit(n);的方法,可以设置保存fragment的数量,数量为2n+1个。如果设置为2,就回保存5个fragment的状态。如果有很多个fragment,像网易新闻那样的话,一般用FragmenStatePagerAdapter。
  • e26950a6b2f4:既然 setUserVisibleHint 在oncreateView之前,不如在onCreateView里调用 prepareData的时候,调用 getUserVisibleHint()方法,检测当前Fragment是否对用户可见
    凤鸣游子:那这个不行哦,如果懒加载把它拒了,下次你想加载就加不了啊,所以要在setUserVisibleHint里面加上个动态的监听。
  • 997f85c824a0:在BaseFragment的prepareFetchData(boolean forceUpdate)方法中, 调用`fetchData()`后直接设置 `isDataInitiated = true;`是不是欠妥当?
    如果`fetchData()`失败了,`isDataInitiated`应该还是false吧。
    我认为,应该在`fetchData()`中设置`isDataInitiated`的值。
  • abe3f709e800:重写vieppager,不是更好吗?只要改动一个属性
    forevan:@coding_trick 你需要考虑下滑过去再划回来的情况
    武器商人:估计就是去掉viewpage的预加载
    尹star: @coding_trick 重写成什么样
  • 634d129c8a76:void setUserVisibleHint(boolean isVisibleToUser);这个方法是参数代表当前ui是否可见,而不是返回值, :joy: ,写都写错了~
  • 呃哈哈:一会要用了
  • s丶heart:不错
  • davidtps:我大stay棒棒哒 :smile:
  • asturelizhe:你好,我想说 setUserVisibleHint 必然在 onActivityCreated之后执行,所以 isViewInitiated这个条件是多余的,isVisibleToUser && (!isDataInitiated || forceUpdate) 就可以了
    阿飞咯:@stay4it 正解! 我也是这样做的
    stay4it:@asturelizhe
    刚才打印测试了一下。
    setUserVisibleHint在onActivityCreated之前就调用了。所以还是得加上isViewInitialized标记是否已加载View。
  • 2eb56199844d:遇到两个问题:
    1,对第一个fragment不起作用
    2,只会调用一次,一旦滑动一遍之后文fetchData() 就不再被触发
    儿三:@fewwind 首先要感谢作者分享,我有五个页面,使用FragmentPagerAdapter,设置setOffscreenPageLimit(3),我在BasePageFragment 的 onDestroyView 处
    @Override
    public void onDestroyView() {
    super.onDestroyView();
    isDataInitiated = false;
    }
    这样重新回到打开过的Fragment,它若是销毁了会重新调用fetchData()。
    2d8533a83ac8:第一个Fragment可以在onActivityCreate手动调用 setUserVisibleHint(true);那在页面不断切换之间,他们会onDestoryView,onCreateView,view的状态该如何保存呢
    尹star: @fewwind 第一个Fragment是必须要加载的,当然不起作用,这里说的是对预加载的那个。关于只调用一次的问题,可以手动强制刷新,已经预留方法了,仔细看代码。
  • b2f9950dfd97:一直都这样做,原来这就是所谓的最优解,就是当用户可见时才去加载,然后再处理下第一个fragment的特殊情况,避免还没初始化完成就可见去加载数据造成的crash
    尹star: @ixiangfeng 我是标题党
  • 7fb2d224837d:两个疑问,想请教下:
    1.prepareFetchData()返回值是用来做什么的
    2.这样子的话网络请求只能加载一次么?
    恁月:@尹star 有个问题,当这个Fragment由不可见到可见时,setUserVisibleHint这个方法会调用,然后滑到下一个Fragment,由可见到不可见,是不是又继续调用
    7fb2d224837d:@尹star 原来这个传值是用来强制刷新的...
    尹star:@爱睡的猫 这里就是预留了强制刷新~如果需要强制刷新~可以先调用prepareFetchData,传true

本文标题:ViewPager+Fragment LazyLoad最优解

本文链接:https://www.haomeiwen.com/subject/zzgrlttx.html