ViewPager实现懒加载

日常开发中很多场景会用到ViewPager+Fragment,一般情况下,我们是通过setOffscreenPageLimit(int limit)方法控制页面的预加载数量。但是有时候,我们又会有这样的需求,当用户滑动到相关页面的时候,才加载相关页面的数据,从而节省用户流量。那么,设置setOffscreenPageLimit(int limit)是否可以达到这样的目的呢,答案是肯定的:不能!
为什么不能呢,先上源码:

private static final int DEFAULT_OFFSCREEN_PAGES = 1;
/**
* Set the number of pages that should be retained to either side of the
* current page in the view hierarchy in an idle state. Pages beyond this
* limit will be recreated from the adapter when needed.
*
* <p>This is offered as an optimization. If you know in advance the number
* of pages you will need to support or have lazy-loading mechanisms in place
* on your pages, tweaking this setting can have benefits in perceived smoothness
* of paging animations and interaction. If you have a small number of pages (3-4)
* that you can keep active all at once, less time will be spent in layout for
* newly created view subtrees as the user pages back and forth.</p>
*
* <p>You should keep this limit low, especially if your pages have complex layouts.
* This setting defaults to 1.</p>
*
* @param limit How many pages will be kept offscreen in an idle state.
*/
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();
    }
}

可以看到如果设置的limit小于默认的offscreen_pages,就采用默认值,而默认值是1,也就是说ViewPager会加载当前页面的前后两个页面!
网上也有不少关于如何达到懒加载的文章,比如:实现ViewPager懒加载的三种方法,这篇博文介绍了三种方法。同时第三种方法是博主推荐的,而这种方法也是我今天想要介绍的。第三种方法实现懒加载demo的github地址为:懒加载Demo
这个Demo基本能够实现懒加载,但是我在使用过程中,我发现相关的Fragment的setUserVisibleHint(boolean isVisibleToUser)居然失效了!。
我们知道当ViewPager+Fragment,当adapter继承FragmentPagerAdater后,要知道相应的Frgement可见还是不可见,不能通过onStop()和onResume()来实现,需通过setUserVisibleHint(boolean isVisibleToUser)
来判断,那么证明,setUserVisibleHint(boolean isVisibleToUser)应当在必要的地方中进行调用了。
先看一下LazyFragmentPagerAdapter中关于setUserVisibleHint(boolean isVisibleToUser)的代码:

public abstract class LazyFragmentPagerAdapter extends LazyPagerAdapter<Fragment> {
   ......
    @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) {
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(container, position);
            if (fragment instanceof Laziable) {
                mLazyItems.put(position, fragment);
            } else {
                mCurTransaction.add(container.getId(), fragment, name);
            }
        }
        if (fragment != getCurrentItem()) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }
	......
}

在LazyFragmentPagerAdapter 全文上下中,只用到一个地方的setUserVisibleHint(boolean isVisibleToUser),而且是设置为false了。
我们再来看看FragmentPagerAdapter中是如何调用setUserVisibleHint(boolean isVisibleToUser)的吧:

public abstract class FragmentPagerAdapter extends PagerAdapter {
    ......

    @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);
        }

        return fragment;
    }

    @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;
        }
    }
	......
}

在FragmentPagerAdapter中的setPrimaryItem()方法中,可知如果当前的mCurrentPrimaryItem不是将要展示的Fragment,就将mCurrentPrimaryItem通过调用setUserVisibleHint告诉我们该Fragment将不可见,同时将要展示的Fragment的设置为可见,并将将要展示的Fragment赋值给mCurrentPrimaryItem。
对比,LazyFragmentPagerAdapter缺少如此部分设置,故增加如下:

/**
     * 通过viewpager中的代码进行调用:mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
     * 重写此方法,仿照FragmentPagerAdapter设置当前fragment visible or invisible
     * @param container
     * @param position
     * @param object
     */
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        super.setPrimaryItem(container, position, object);
        Fragment fragment = (Fragment)object;
        if (fragment != getCurrentItem()) {
            if (getCurrentItem() != null) {
                getCurrentItem().setMenuVisibility(false);
                getCurrentItem().setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            setCurrentItem(fragment);
        }
    }

好了,通过增加以上方法,即能够实现懒加载且可通过setUserVisibleHint()判断Fragment是否可见了。
使用该懒加载方法应注意以下几点:
1、所有的Fragmeng实现LazyFragmentPagerAdapter.Laziable接口。
2、自定义Adapter继承LazyFragmentPagerAdapter。
3、ViewPager替换为LazyViewPager。
这是我目前接触到的对原有的ViewPager+Fragment改造比较方便的方法,给大神如果有更好的方法,望请推荐!

猜你喜欢

转载自blog.csdn.net/Heijinbaitu/article/details/85487966