ViewPager(五)让ViewPager用起来更顺滑——懒加载及预加载定制

用法概述:

1、换页监听与换页方法
2、懒加载及预加载定制
3、设置间距与添加转场动画
4、轮播、禁止滑动与指示器的配合

我们接着上篇继续来下一个进阶,ViewPager是很好的处理了缓存,但是在一些特殊情况,我们还需要更灵活的控制,举个栗子。。。
当我们的每个页面都是Fragment的情况,这个时候Fragment的内容可能比较多,在用户可能都没有机会打开这个页面的时候,我们尽量将请求网络接口的操作往后放,这个时候需要我们找到这个时机,另外我们在之前的FragmentAdapter的两个实现的区别,也就是ViewPager系列文章第三篇。提到过,在打印的方法回调的log日志中,会发现ViewPager默认会提前加载一边一页,我想加载更多,或更少怎么办呢?这都是这篇文章来解答的问题。

懒加载及预加载定制

懒加载和预加载是两部分,懒加载针对具体页面的加载内容的时机,而预加载是指的ViewPager这个容器在什么时机加载它的子View,和加载多少的问题

懒加载

起始这个更准确的应该是针对每个子View是Fragment控件的情况来谈的。而其他简单的View并不会占用太大的内存,提前加载会提高app显示速度,提升用户性能。
懒加载主要是通过Fragment的机制实现,在Fragment的回调方法中有这么一个方法

 public void setUserVisibleHint(boolean isVisibleToUser) {
    if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
            && mFragmentManager != null && isAdded()) {
        mFragmentManager.performPendingDeferredStart(this);
    }
    mUserVisibleHint = isVisibleToUser;
    mDeferStart = mState < STARTED && !isVisibleToUser;
    if (mSavedFragmentState != null) {
        // Ensure that if the user visible hint is set before the Fragment has
        // restored its state that we don't lose the new value
        mSavedUserVisibleHint = mUserVisibleHint;
    }
}

它是用来判断当前的Fragment界面是否对用户可见,这个完全可以通过这个方法的名字了解到。这个方法内部并不能得到什么有营养的信息。其实这个方法并不会像其他的生命周期方法按顺序执行,所以Fragment的这个方法起始谷歌对外暴露的用于用户自己控制当前View是否可见,而恰恰在我们的Viewpager的两个FragmentPagerAdapter中就有对当前ViewPager子Fragment的可见性,如下边源码是FragmetPagerAdapter的部分源码:

@SuppressWarnings("ReferenceEquality")
@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    //关键在这,上边的都是之前讲过的
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);//这里调用了这个方法,但是设置了false,这里其实是初始化所有的Fragment的可见状态为不可见
    }
    return fragment;
}

下边这个方法就是ViewPager源码中会调用到的方法,而这个方法调用的时机恰巧是要显示的那个页面

@SuppressWarnings("ReferenceEquality")
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment)object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
            //将之前可见的页面重置为不可见
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
            //将调用这个方法的当前页面设置为可见,也就是即将要显示的页面
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

instantiateItem这个方法在之前几篇关于ViewPager的系列中,这个方法就是具体加载的子View,这里边判断了下所有的子View是否是当前应该显示的View。而在这个方法中会把所有不可见的(不是当前View的item)Fragment调用他的setUserVisableHint(false),这个好理解,因为不是当前都是不可见的。
而另外一个方法可能大多数人都还不熟悉。因为这个方法涉及了ViewPager的源码,由于后边会有专门一篇分析源码,所以这里只是先剧透一下,setPrimaryItem()这个方法是ViewPager在计算要显示的Item信息,并保存的时候调用的,之后ViewPager就会执行绘制方法绘制子View。所以将要绘制的马上就要可见了,所以我们看到FragmentPagerAdapter中的这个方法的具体实现就是将上一次可见的置为不可见,然后将传过来的Fragment置为可见。所以就会看到当前的setUserVisableHint(true)方法回调了。

这下是不是清楚了。起始谷歌对PagerAdapter的两个实现帮我们做了很多事情,里边集成了懒加载或者叫缓加载的策略,当然最后需要真正实现懒加载需要在我们的具体的Fragmen中覆写setUserVisableHint这个方法。
这里有个坑:要注意,这个方法回调的时候,可能在onCreateView之前,所以这个时候View还没有初始化,所以会报空指针。这个是一定要注意的,因为有的读者可能会在这个方法里边进行网络请求,在网络回调方法中进行View的数据映射,这个时候报了空指针是有可能的,一定不要慌,要知道原因,在Fragment的setUserVisableHint这个方法的源码中也是用Note的标签来引来开发者注意,着意强调这个方法不是生命周期方法,所以一定要自己控制,如果不用系统的FragmentPagerAdapter,而是实现PagerAdapter自己来控制,那需要在onCreateView里边View初始化完成后我们自行调用setUserVisibleHint(true)方法来,执行具体网络加载。

和此类似的Fragment还有一个方法,就是setMenuVisiblity(Boolean visible)这个方法是为了控制菜单的懒加载,这个也是由我们自己控制,会控制Menu的显示和隐藏。以上的方法只是控制了数据的懒加载,控制ViewPager的子View的实例的懒加载,则设计到ViewPager的加载机制的另一个方法,很重要的一个方法。

预加载

ViewPager的优势就是可以顺滑的处理多个页面的切换,那么这个情况就是充分的运用了ViewPager的预加载功能,正如开篇讲的那样,默认ViewPager会预处理当前页面的前后各一个子View的预加载处理。

ViewPager中对这部分的控制是通过一个变量:mOffscreenPageLimit 翻译过来就是:屏幕外的页面限制数量
在加载子View的时候,通常就是通过计算当前页和这个int类型的变量的关系,来确定前边需要加载几页,后边需要加载几页,总共需要加载几页。(话外音:这个是ViewPager的源码,在源码那篇文章我们会详细的介绍,这里就和读者说个大概,明白了这个变量大致在哪起作用,起什么作用。)

好了,既然我们知道这个变量是控制预加载策略的,那么我们修改了这个变量就会对默认加载策略产生影响。没错,ViewPager给我们暴露了这个方法:

public void setOffscreenPageLimit(int limit) {
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                + DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
        mOffscreenPageLimit = limit;
        populate();
    }
}

如果我们不调用这个方法,ViewPager会执行默认的数量:

private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

是的,默认是1,所以一次性前后至少要预加载一页,如果你目前处在第一页或者最后一页,你将没有前一页或者没有后一页了,那这个时候,将默认最少加载两页咯。

当然如果我们想获取一下当前的预加载数量,可以用下面的方法:

public int getOffscreenPageLimit() {
    return mOffscreenPageLimit;
}

注意!看上边的setOffscreenPageLimit(int limit)源码我们惊奇的发现,第一句条件语句,我们如果设置小于默认的值,这个时候会使用默认值(1)。所以ViewPager的设计最低是缓存前后各一页,不允许不缓存,这个其实可以理解,不缓存不预加载的话,如果每个子View加载数据比较费时间,这个时候你会在一个空页面等待很久,这个体验是非常差的。

但是,但是。。。总有特殊需求,你就是不想要预加载,那可不可以呢?可以,但是,哎,总有但是,比较麻烦,你想想ViewPager为了实现预加载实现 流畅的显示效果,在确定加载子View的时候一定会有很多的判断,来避免重复加载或者漏加载,而这个变量是私有的,你不能通过直接或者继承更改,那么你只有通过自己实现这个类,当然大部分代码还是沿用原生的,对于限制和显示View的控制抓取,可以做些调整,这个涉及较多的ViewPager源码,同样我们先留个悬念,如果迫不及待的读者可以直接去看咯--------ViewPager源码分析,我们可以实现一个NullPreloadViewPager,在特殊的时候使用。

还有童鞋会说,反射可以直接改变呢!!!这里我想说,反射这个是比较暴力的方法,主要用于实现原生方式实现不了的,比如涉及到Android的底层hook来实现我们的目的,比如插件化或者安全机制等。我们在开发的时候应尽量避免,谁知道谷歌在新版的Android里边会对反射这个暴力的方式会加入什么意想不到的限制或者调整,那是很不利于代码健壮性和App的稳定性的。再则,你只改了这个变量的值,那么其他可能涉及这个变量的代码有需要地方怎么办?你全hook了?(并且你即使通过反射修改了这个变量值,但是你还是会发现最终还是按照预加载数量是1执行的,因为涉及源码还是源码篇章我会告诉读者答案)
最后,这也在很大程度违背面向对象语言的设计模式的,关于设计模式我之后也会有相关的文章和读者分享,通俗易懂的和读者聊聊设计模式,各位读者敬请期待。
这里不是不让大家用反射,也不是说他不重要,只是希望大家不要乱用,不要滥用。就像设计模式一样,都是不能滥用的,让他发挥他最大的作用,又不失去它的意义。
最后我们看代码具体事例来验证下我们上边的结论,分别测试一下下面五种情况:
1、什么都不设置,采用默认
2、viewPager.setOffscreenPageLimit(0);
3、viewPager.setOffscreenPageLimit(1);
4、viewPager.setOffscreenPageLimit(2);
5、viewPager.setOffscreenPageLimit(3);

要想让这五个都看出区别来,之前我们代码举的例子中的子View的数量显然不够用,这次我们增加到8个,为了能很好的标记对应的View是否加载了,我们还是用Fragment作为其子View,然后打印它们的生命周期方法。

这次为了方便我把Fragment的名称按照Fragment0,Fragment1 … … Fragment7 这样命名八个Fragment,好,下面,我们看结果:

ViewPager 刚加载的情况(当前页面序号为0):
1、2、3条件下的log日志:

01-05 10:00:05.897 22775-22775/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----false
01-05 10:00:05.897 22775-22775/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----false
01-05 10:00:05.897 22775-22775/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----true
01-05 10:00:05.902 22775-22775/com.hzx.viewpagerdirector E/Fragment0: ---onAttach----
01-05 10:00:05.902 22775-22775/com.hzx.viewpagerdirector E/Fragment0: ---onCreate----
01-05 10:00:05.902 22775-22775/com.hzx.viewpagerdirector E/Fragment1: ---onAttach----
01-05 10:00:05.902 22775-22775/com.hzx.viewpagerdirector E/Fragment1: ---onCreate----
01-05 10:00:05.903 22775-22775/com.hzx.viewpagerdirector E/Fragment0: ---onCreateView---
01-05 10:00:05.905 22775-22775/com.hzx.viewpagerdirector E/Fragment1: ---onCreateView---

条件4的log日志:

01-05 10:09:40.179 23234-23234/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----false
01-05 10:09:40.179 23234-23234/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----false
01-05 10:09:40.179 23234-23234/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----false
01-05 10:09:40.179 23234-23234/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----true
01-05 10:09:40.184 23234-23234/com.hzx.viewpagerdirector E/Fragment0: ---onAttach----
01-05 10:09:40.184 23234-23234/com.hzx.viewpagerdirector E/Fragment0: ---onCreate----
01-05 10:09:40.184 23234-23234/com.hzx.viewpagerdirector E/Fragment1: ---onAttach----
01-05 10:09:40.184 23234-23234/com.hzx.viewpagerdirector E/Fragment1: ---onCreate----
01-05 10:09:40.184 23234-23234/com.hzx.viewpagerdirector E/Fragment2: ---onAttach----
01-05 10:09:40.184 23234-23234/com.hzx.viewpagerdirector E/Fragment2: ---onCreate----
01-05 10:09:40.186 23234-23234/com.hzx.viewpagerdirector E/Fragment0: ---onCreateView---
01-05 10:09:40.187 23234-23234/com.hzx.viewpagerdirector E/Fragment1: ---onCreateView---
01-05 10:09:40.188 23234-23234/com.hzx.viewpagerdirector E/Fragment2: ---onCreateView---

条件5的log日志:

01-05 10:11:25.375 23650-23650/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----false
01-05 10:11:25.375 23650-23650/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----false
01-05 10:11:25.375 23650-23650/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----false
01-05 10:11:25.375 23650-23650/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----false
01-05 10:11:25.375 23650-23650/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----true
01-05 10:11:25.381 23650-23650/com.hzx.viewpagerdirector E/Fragment0: ---onAttach----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment0: ---onCreate----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment1: ---onAttach----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment1: ---onCreate----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment2: ---onAttach----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment2: ---onCreate----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment3: ---onAttach----
01-05 10:11:25.382 23650-23650/com.hzx.viewpagerdirector E/Fragment3: ---onCreate----
01-05 10:11:25.383 23650-23650/com.hzx.viewpagerdirector E/Fragment0: ---onCreateView---
01-05 10:11:25.384 23650-23650/com.hzx.viewpagerdirector E/Fragment1: ---onCreateView---
01-05 10:11:25.385 23650-23650/com.hzx.viewpagerdirector E/Fragment2: ---onCreateView---
01-05 10:11:25.386 23650-23650/com.hzx.viewpagerdirector E/Fragment3: ---onCreateView---

第一种测试情况小结:我们发现1,2,3三种情况的log日志一致,所以也可再次验证默认状态,设置小于0,和设置1最后都是按照设置1来处理的,关于这一点通过下边日志输出也能看出,将不再赘述。分别设置预加载数量1,2,3,对应的日志显示分别多加载了1,2,3页(我们说的这个数量是指当前页的前边后边两个方向,如果任何一个方向剩余的数量不足,则加载剩余数量,如果没有就不加载,这个时候左侧是0,所以当前页左边不预加载)。

向右滑动一页的情况(当前页面序号为1):
条件1、2、3的log日志:

01-05 10:19:58.165 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----false
01-05 10:19:58.165 24436-24436/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----false
01-05 10:19:58.165 24436-24436/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----true
01-05 10:19:58.165 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ---onAttach----
01-05 10:19:58.165 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ---onCreate----
01-05 10:19:58.166 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ---onCreateView---

条件4的log日志:

01-05 10:28:56.068 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----false
01-05 10:28:56.068 25069-25069/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----false
01-05 10:28:56.068 25069-25069/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----true
01-05 10:28:56.068 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ---onAttach----
01-05 10:28:56.068 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ---onCreate----
01-05 10:28:56.069 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ---onCreateView---

条件5的log日志:

01-05 10:32:21.620 25382-25382/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----false
01-05 10:32:21.620 25382-25382/com.hzx.viewpagerdirector E/Fragment0: ---getUserVisibleHint----false
01-05 10:32:21.620 25382-25382/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----true
01-05 10:32:21.620 25382-25382/com.hzx.viewpagerdirector E/Fragment4: ---onAttach----
01-05 10:32:21.620 25382-25382/com.hzx.viewpagerdirector E/Fragment4: ---onCreate----
01-05 10:32:21.621 25382-25382/com.hzx.viewpagerdirector E/Fragment4: ---onCreateView---

第二种测试情况小结:这个时候由于当前页序号为1,所以左边还有1页,右边还有6页,所以条件1、2、3预加载了Fragment0,Fragment1,Fragment2;条件4预加载了Fragment0,Fragment1,Fragment2,Fragment3;而条件5预加载了Fragment0,Fragment1,Fragment2,Fragment3,Fragment4;这时候还没有创建的页面需要销毁

向右滑动两页的情况(当前页面序号为2):
条件1、2、3的log日志:

01-05 10:24:25.643 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----false
01-05 10:24:25.643 24436-24436/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----false
01-05 10:24:25.643 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----true
01-05 10:24:25.643 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ---onAttach----
01-05 10:24:25.644 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ---onCreate----
01-05 10:24:25.644 24436-24436/com.hzx.viewpagerdirector E/Fragment0: ----onDestroyView---
01-05 10:24:25.645 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ---onCreateView---

条件4的log日志:

01-05 10:29:20.199 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----false
01-05 10:29:20.200 25069-25069/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----false
01-05 10:29:20.200 25069-25069/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----true
01-05 10:29:20.200 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ---onAttach----
01-05 10:29:20.200 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ---onCreate----
01-05 10:29:20.201 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ---onCreateView---

条件5的log日志:

01-05 10:32:42.471 25382-25382/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----false
01-05 10:32:42.471 25382-25382/com.hzx.viewpagerdirector E/Fragment1: ---getUserVisibleHint----false
01-05 10:32:42.471 25382-25382/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----true
01-05 10:32:42.471 25382-25382/com.hzx.viewpagerdirector E/Fragment5: ---onAttach----
01-05 10:32:42.471 25382-25382/com.hzx.viewpagerdirector E/Fragment5: ---onCreate----
01-05 10:32:42.472 25382-25382/com.hzx.viewpagerdirector E/Fragment5: ---onCreateView---

第三种测试情况小结:这个时候当前页面序号为2,所以当前页面左边有2页,右边有5页,这个时候条件1、2、3预加载了Fragment1,Fragment2,Fragment3;条件4预加载了Fragment0,Fragment1,Fragment2,Fragment3,Fragment4;条件5预加载了Fragment0,Fragment1,Fragment2,Fragment3,Fragment4,Fragment5;由于条件4、5两种情况都没有超过范围,所以只是创建,不销毁,而条件1、2、3这种情况,由于左边只能预加载一页,现在需要销毁一页了。

向右滑动三页的情况(当前页面序号为3):
条件1、2、3的log日志:

01-05 10:25:19.514 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----false
01-05 10:25:19.516 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----false
01-05 10:25:19.516 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----true
01-05 10:25:19.516 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ---onAttach----
01-05 10:25:19.516 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ---onCreate----
01-05 10:25:19.517 24436-24436/com.hzx.viewpagerdirector E/Fragment1: ----onDestroyView---
01-05 10:25:19.519 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ---onCreateView---

条件4的log日志:

01-05 10:29:53.517 25069-25069/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----false
01-05 10:29:53.518 25069-25069/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----false
01-05 10:29:53.518 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----true
01-05 10:29:53.518 25069-25069/com.hzx.viewpagerdirector E/Fragment5: ---onAttach----
01-05 10:29:53.518 25069-25069/com.hzx.viewpagerdirector E/Fragment5: ---onCreate----
01-05 10:29:53.519 25069-25069/com.hzx.viewpagerdirector E/Fragment0: ----onDestroyView---
01-05 10:29:53.520 25069-25069/com.hzx.viewpagerdirector E/Fragment5: ---onCreateView---

条件5的log日志:

01-05 10:33:00.143 25382-25382/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----false
01-05 10:33:00.144 25382-25382/com.hzx.viewpagerdirector E/Fragment2: ---getUserVisibleHint----false
01-05 10:33:00.144 25382-25382/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----true
01-05 10:33:00.144 25382-25382/com.hzx.viewpagerdirector E/Fragment6: ---onAttach----
01-05 10:33:00.144 25382-25382/com.hzx.viewpagerdirector E/Fragment6: ---onCreate----
01-05 10:33:00.145 25382-25382/com.hzx.viewpagerdirector E/Fragment6: ---onCreateView---

第四种测试情况小结:这个时候当前序号为3,左边有3页,右边有4页,条件1、2、3继续向前滚动,销毁Fragment1,创建Fragment4。这个时候条件4也超出了它的预加载范围,所以它也销毁了左边的Fragment0,创建了Fragment5;这个时候条件5还没超过,只创建了Fragment6,没有销毁;

向右滑动四页的情况(当前页面序号为4):
条件1、2、3的log日志:

01-05 10:25:41.398 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----false
01-05 10:25:41.398 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----false
01-05 10:25:41.398 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----true
01-05 10:25:41.399 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ---onAttach----
01-05 10:25:41.399 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ---onCreate----
01-05 10:25:41.399 24436-24436/com.hzx.viewpagerdirector E/Fragment2: ----onDestroyView---
01-05 10:25:41.403 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ---onCreateView---

条件4的log日志:

01-05 10:30:29.500 25069-25069/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----false
01-05 10:30:29.501 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----false
01-05 10:30:29.501 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----true
01-05 10:30:29.501 25069-25069/com.hzx.viewpagerdirector E/Fragment6: ---onAttach----
01-05 10:30:29.501 25069-25069/com.hzx.viewpagerdirector E/Fragment6: ---onCreate----
01-05 10:30:29.502 25069-25069/com.hzx.viewpagerdirector E/Fragment1: ----onDestroyView---
01-05 10:30:29.503 25069-25069/com.hzx.viewpagerdirector E/Fragment6: ---onCreateView---

条件5的log日志:

01-05 10:33:21.464 25382-25382/com.hzx.viewpagerdirector E/Fragment7: ---getUserVisibleHint----false
01-05 10:33:21.464 25382-25382/com.hzx.viewpagerdirector E/Fragment3: ---getUserVisibleHint----false
01-05 10:33:21.464 25382-25382/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----true
01-05 10:33:21.464 25382-25382/com.hzx.viewpagerdirector E/Fragment7: ---onAttach----
01-05 10:33:21.464 25382-25382/com.hzx.viewpagerdirector E/Fragment7: ---onCreate----
01-05 10:33:21.467 25382-25382/com.hzx.viewpagerdirector E/Fragment0: ----onDestroyView---
01-05 10:33:21.468 25382-25382/com.hzx.viewpagerdirector E/Fragment7: ---onCreateView---

第五种测试情况小结:这个时候当前页面序号为4,左边有4页,右边有3页;这个时候根据log日志发现,五种情况都超过了阈值,条件1、2、3销毁了Fragment2,创建了Fragment5;条件4销毁了Fragment1,创建了Fragment6;条件5创建了Fragment0,创建了Fragment7;

向右滑动五页的情况(当前页面序号为5):
条件1、2、3的log日志:

01-05 10:26:03.501 24436-24436/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----false
01-05 10:26:03.502 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----false
01-05 10:26:03.502 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----true
01-05 10:26:03.502 24436-24436/com.hzx.viewpagerdirector E/Fragment6: ---onAttach----
01-05 10:26:03.502 24436-24436/com.hzx.viewpagerdirector E/Fragment6: ---onCreate----
01-05 10:26:03.502 24436-24436/com.hzx.viewpagerdirector E/Fragment3: ----onDestroyView---
01-05 10:26:03.503 24436-24436/com.hzx.viewpagerdirector E/Fragment6: ---onCreateView---

条件4的log日志:

01-05 10:30:48.534 25069-25069/com.hzx.viewpagerdirector E/Fragment7: ---getUserVisibleHint----false
01-05 10:30:48.534 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----false
01-05 10:30:48.536 25069-25069/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----true
01-05 10:30:48.537 25069-25069/com.hzx.viewpagerdirector E/Fragment7: ---onAttach----
01-05 10:30:48.537 25069-25069/com.hzx.viewpagerdirector E/Fragment7: ---onCreate----
01-05 10:30:48.538 25069-25069/com.hzx.viewpagerdirector E/Fragment2: ----onDestroyView---
01-05 10:30:48.539 25069-25069/com.hzx.viewpagerdirector E/Fragment7: ---onCreateView---

条件5的log日志:

01-05 10:33:39.220 25382-25382/com.hzx.viewpagerdirector E/Fragment4: ---getUserVisibleHint----false
01-05 10:33:39.220 25382-25382/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----true
01-05 10:33:39.221 25382-25382/com.hzx.viewpagerdirector E/Fragment1: ----onDestroyView---

第六种测试情况小结:这个时候当前页面序号为5,左边有5页,右边有2页,同理条件1、2、3销毁了Fragment3,创建了Fragment6;条件4销毁了Fragment2,创建了Fragment7;条件5销毁了Fragment1,右边已经创建完成,不需要创建新的。

向右滑动六页的情况(当前页面序号为6):
条件1、2、3的log日志:

01-05 10:26:54.059 24436-24436/com.hzx.viewpagerdirector E/Fragment7: ---getUserVisibleHint----false
01-05 10:26:54.059 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----false
01-05 10:26:54.059 24436-24436/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----true
01-05 10:26:54.059 24436-24436/com.hzx.viewpagerdirector E/Fragment7: ---onAttach----
01-05 10:26:54.059 24436-24436/com.hzx.viewpagerdirector E/Fragment7: ---onCreate----
01-05 10:26:54.061 24436-24436/com.hzx.viewpagerdirector E/Fragment4: ----onDestroyView---
01-05 10:26:54.062 24436-24436/com.hzx.viewpagerdirector E/Fragment7: ---onCreateView---

条件4的log日志:

01-05 10:31:03.279 25069-25069/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----false
01-05 10:31:03.279 25069-25069/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----true
01-05 10:31:03.279 25069-25069/com.hzx.viewpagerdirector E/Fragment3: ----onDestroyView---

条件5的log日志:

01-05 10:34:03.052 25382-25382/com.hzx.viewpagerdirector E/Fragment5: ---getUserVisibleHint----false
01-05 10:34:03.053 25382-25382/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----true
01-05 10:34:03.053 25382-25382/com.hzx.viewpagerdirector E/Fragment2: ----onDestroyView---

第七种测试情况小结:这个时候当前页面序号6,左边有6页,右边有1页,同理,条件1、2、3销毁了Fragment4,创建了Fragment7;条件4销毁了Fragment3,右边全部创建无需创建,条件5销毁了Fragment2,右边全部创建无需创建

向右滑动七页的情况,也是最后一页(当前页面序号为7):
条件1、2、3的log日志:

01-05 10:27:21.337 24436-24436/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----false
01-05 10:27:21.337 24436-24436/com.hzx.viewpagerdirector E/Fragment7: ---getUserVisibleHint----true
01-05 10:27:21.337 24436-24436/com.hzx.viewpagerdirector E/Fragment5: ----onDestroyView---

条件4的log日志:

01-05 10:31:22.102 25069-25069/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----false
01-05 10:31:22.102 25069-25069/com.hzx.viewpagerdirector E/Fragment7: ---getUserVisibleHint----true
01-05 10:31:22.102 25069-25069/com.hzx.viewpagerdirector E/Fragment4: ----onDestroyView---

条件5的log日志:

01-05 10:34:25.983 25382-25382/com.hzx.viewpagerdirector E/Fragment6: ---getUserVisibleHint----false
01-05 10:34:25.983 25382-25382/com.hzx.viewpagerdirector E/Fragment7: ---getUserVisibleHint----true
01-05 10:34:25.984 25382-25382/com.hzx.viewpagerdirector E/Fragment3: ----onDestroyView---

第八种测试情况小结:这个时候当前页面是最后一页,序号是7,左边有7页,右边没有,所以这个时候只有销毁,没有创建。条件1、2、3销毁了Fragment5;条件4销毁了Fragment4;条件5销毁了Fragment3;

以上测试很好的说明了以下几点:
1、ViewPager创建每一个页面的时候回现将UserVisibleHint置为false,当前显示的页面会再置为true;
2、我们设置的预加载的数量,或者叫缓存数量,和总的缓存数量有以下关系:
当前页左边可预加载的数量 + 当前页右边可预加载的数量 + 1 = 总的缓存数量(这个会在源码里边有代码说明);
3、不设置预加载(缓存)数量,设置小于1的预加载(缓存)数量,最终效果等同于设置预加载(缓存)数量为1;
4、可以看到预加载(缓存)数量设置的越大,页面创建的范围也会越大,就会保证了大范围的流畅,如果发现页面加载比较耗时,创建的代价比较大,并且内存占用不太大的情况,我们可以设置的略大点,用空间换时间的方式来优化,如果ViewPager的子页面比较耗内存,那么久要慎重设置,并且要采用懒加载结合的方式,合理控制加载时机,这个时候用时间换空间。在我们写代码的时候要根据实际业务逻辑来灵活应用,才不辜负google给我们提供的强大V4 support。

本篇就到这里了,欢迎读者评论交流
我们接着下一篇:ViewPager (六) 让ViewPager用起来更顺滑——设置间距与添加转场动画

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值