Android组件之Fragment(三)---实战知识

本文在前面Fragment系列文章的基础上,重在想要从源码和实践运用的角度,去学习、分析、总结Fragment一些新技术函数和组件。

1.setUserVisibleHint与setMaxLifecycle

1.1 废弃原因

我们先看一下最新的setUserVisibleHint方法的注释


    /**
     * Set a hint to the system about whether this fragment's UI is currently visible
     * to the user. This hint defaults to true and is persistent across fragment instance
     * state save and restore.
     *
     * 

An app may set this to false to indicate that the fragment's UI is * scrolled out of visibility or is otherwise not directly visible to the user. * This may be used by the system to prioritize operations such as fragment lifecycle updates * or loader ordering behavior.

* *

Note: This method may be called outside of the fragment lifecycle. * and thus has no ordering guarantees with regard to fragment lifecycle method calls.

* * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default), * false if it is not. * * @deprecated If you are manually calling this method, use * {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)} instead. If * overriding this method, behavior implemented when passing in true should be * moved to {@link Fragment#onResume()}, and behavior implemented when passing in * false should be moved to {@link Fragment#onPause()}. */ @Deprecated public void setUserVisibleHint(boolean isVisibleToUser) { if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && mFragmentManager != null && isAdded() && mIsCreated) { mFragmentManager.performPendingDeferredStart( mFragmentManager.createOrGetFragmentStateManager(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 = isVisibleToUser; } }

通过方法注释,我们知道以下几点信息:
1.setUserVisibleHint已经废弃,不建议使用,废弃原因的话,就是之前Fragment原理分析文章中讲到的,此函数的回调可能发生在生命周期之前,建议使用setMaxLifecycle方法替代,从方法名可知,这个方法是设置fragment最大生命周期的
2.对于之前使用setUserVisibleHint的代码,官方进行如下修改,将fragment不可见(即isVisibleToUser==false)的代码,移到Fragment#onPause()中,将fragment可见(即isVisibleToUser==true)的代码,移到Fragment#onResume()中。

不妨先猜想一下,这个函数既然是设置每个fragment的最大生命周期的,提出也是为了解决setUserVisibleHint与fragment生命周期不同步的问题,那么是否是代表,通过设置这个,就可以控制fragment展示的生命周期?带着这个疑问,继续看。

1.2 源码中理解,如何用?

好了,看到这里,我们知道官方为什么废弃它,以及提供的替代方法了,接下来就是怎么用的问题了。从方法注释看,use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)} instead,这好像有点问题了,像我们自己通过FragmentManager管理fragment的情况,还可以这样调用,但是大家更多的时候,是使用viewpager+fragment的方案实现多页面,而ViewPagerAdapter已经帮我们在源码中自动管理了fragment,这怎么办,难道要修改源码?别着急,官方已经给我们提供了相关方法。
FragmentPagerAdapter的另外一个构造方法


    /**
     * Constructor for {@link FragmentPagerAdapter} that sets the fragment manager for the adapter.
     * This is the equivalent of calling {@link #FragmentPagerAdapter(FragmentManager, int)} and
     * passing in {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}.
     *
     * 

Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the * current Fragment changes.

* * @param fm fragment manager that will interact with this adapter * @deprecated use {@link #FragmentPagerAdapter(FragmentManager, int)} with * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} */ @Deprecated public FragmentPagerAdapter(@NonNull FragmentManager fm) { this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT); } /** * Constructor for {@link FragmentPagerAdapter}. * * If {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} is passed in, then only the current * Fragment is in the {@link Lifecycle.State#RESUMED} state. All other fragments are capped at * {@link Lifecycle.State#STARTED}. If {@link #BEHAVIOR_SET_USER_VISIBLE_HINT} is passed, all * fragments are in the {@link Lifecycle.State#RESUMED} state and there will be callbacks to * {@link Fragment#setUserVisibleHint(boolean)}. * * @param fm fragment manager that will interact with this adapter * @param behavior determines if only current fragments are in a resumed state */ public FragmentPagerAdapter(@NonNull FragmentManager fm, @Behavior int behavior) { mFragmentManager = fm; mBehavior = behavior; }

正常我们使用FragmentPagerAdapter实现自定义的PagerAdapter时,一般只是重写了第一个构造方法(即:只是吧fragmentManager传给了adapter),可以看到实际上,它也是调用了另外一个构造方法,传入了参数BEHAVIOR_SET_USER_VISIBLE_HINT
我们通过方法注释,可以先了解一下这些int值代表的含义

 @Retention(RetentionPolicy.SOURCE)
    @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
    private @interface Behavior { }
    
    /**
     * Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
     * fragment changes.
     *
     * @deprecated This behavior relies on the deprecated
     * {@link Fragment#setUserVisibleHint(boolean)} API. Use
     * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
     * {@link FragmentTransaction#setMaxLifecycle}.
     * @see #FragmentPagerAdapter(FragmentManager, int)
     * 设置为BEHAVIOR_SET_USER_VISIBLE_HINT,setUserVisibleHint方法将被调用
     * 设置为BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,setMaxLifecycle方法将被调用
     */
    @Deprecated
    public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;

    /**
     * Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
     * state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
     *
     * @see #FragmentPagerAdapter(FragmentManager, int)
     */
    public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;

也就是说,ViewPagerAdapter内部是通过这个值来区分调用setMaxLifecycle还是setUserVisibleHint的,接下来我们看一下FragmentPagerAdapter源码,是否是这么一回事儿。
FragmentPagerAdapter#instantiateItem#setPrimaryItem
由上一篇fragment文章可知,fragmentpageradapter中最主要的几个方法就是instantiateItem和setPrimaryItem,分别用作初始化当前fragment、设置当前fragment,所以我们看一下这两个方法,是否做了相关函数的区分调用。

 @NonNull
    @Override
    public Object instantiateItem(@NonNull 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);
            //看这儿,的确区分了
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
            } else {
                fragment.setUserVisibleHint(false);
            }
        }

        return fragment;
    }
     @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment)object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                //看这儿,的确区分了
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                } else {
                    mCurrentPrimaryItem.setUserVisibleHint(false);
                }
            }
            fragment.setMenuVisibility(true);
            //看这儿,的确区分了
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                if (mCurTransaction == null) {
                    mCurTransaction = mFragmentManager.beginTransaction();
                }
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                fragment.setUserVisibleHint(true);
            }

            mCurrentPrimaryItem = fragment;
        }
    }

看到这里,也就是说如果我们调用构造方法,传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,那么FragmentPagerAdapter默认创建fragment的时候,将帮我们提交的时候,提交为mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED)

1.3 Lifecycle.State不同的状态代表什么?

直接看一下Lifecycle.State.RESUMED的源码注释,我们看一下这个代表什么。


    /**
     * Lifecycle states. You can consider the states as the nodes in a graph and
     * {@link Event}s as the edges between these nodes.
     */
    @SuppressWarnings("WeakerAccess")
    public enum State {
        /**
         * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
         * any more events. For instance, for an {@link android.app.Activity}, this state is reached
         * right before Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
         * 销毁状态
         */
        DESTROYED,

        /**
         * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
         * the state when it is constructed but has not received
         * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
         * 初始状态
         */
        INITIALIZED,

        /**
         * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached in two cases:
         * 
    *
  • after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call; *
  • right before {@link android.app.Activity#onStop() onStop} call. *
* 已创建状态 * 从函数注释看,分为两种情况 * 一种是在当前fragment的状态还小于onCreate,则正常走到onCreate * 一种是在当前fragment的状态大于onCreate,则会使fragment的生命周期走到onStop */ CREATED, /** * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state * is reached in two cases: *
    *
  • after {@link android.app.Activity#onStart() onStart} call; *
  • right before {@link android.app.Activity#onPause() onPause} call. *
* 创建并启动,可见不可操作 * 从函数注释看,分为两种情况 * 一种是在当前fragment的状态还小于onStart,则正常走到onStart * 一种是在当前fragment的状态大于onStart,则会使fragment的生命周期走到onPause */ STARTED, /** * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state * is reached after {@link android.app.Activity#onResume() onResume} is called. * fragment的onResume生命周期在Activity#onResume()生命周期之后 * 创建启动并可操作 * 也就是viewPager中,针对于缓存的fragment,如果设置了这个生命周期,则保证fragment的onResume是在fragment可见之后才去执行 */ RESUMED; }

针对于这几种状态,去通过FragmentTransaction去进行fragment生命周期操作,针对于fragment当前状态,结合设置的最大生命周期,执行相应生命周期。
迫不及待的通过Demo验证一下:

package com.itbird.fragment;

import android.view.View;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager2.adapter.FragmentStateAdapter;

/**
 * Created by itbird on 2022/3/18
 */
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    private final int PAGER_COUNT = 4;
    Fragment fragment1;
    Fragment fragment2;
    Fragment fragment3;
    Fragment fragment4;

    public MyFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
        super(fm, behavior);
        fragment1 = BlankFragment1.newInstance("name1", "age1");
        fragment2 = BlankFragment2.newInstance("name2", "age2");
        fragment3 = BlankFragment3.newInstance("name3", "age3");
        fragment4 = BlankFragment4.newInstance("name4", "age4");
    }

    public MyFragmentPagerAdapter(@NonNull FragmentManager fm) {
        super(fm);
        fragment1 = BlankFragment1.newInstance("name1", "age1");
        fragment2 = BlankFragment2.newInstance("name2", "age2");
        fragment3 = BlankFragment3.newInstance("name3", "age3");
        fragment4 = BlankFragment4.newInstance("name4", "age4");
    }

    @Override
    public int getCount() {
        return PAGER_COUNT;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        switch (position) {
            case 0:
                fragment = fragment1;
                break;
            case 1:
                fragment = fragment2;
                break;
            case 2:
                fragment = fragment3;
                break;
            case 3:
                fragment = fragment4;
                break;
        }
        return fragment;
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return super.isViewFromObject(view, object);
    }
}

运行一下:



日志看一下生命周期

2022-03-25 16:07:51.452 21151-21151/? I/zygote: Late-enabling -Xcheck:jni
2022-03-25 16:07:51.486 21151-21151/? W/zygote: Unexpected CPU variant for X86 using defaults: x86
2022-03-25 16:07:51.831 21151-21151/com.itbird E/FragmenViewPagerTestActivity: FragmenViewPagerTestActivity onCreate
2022-03-25 16:07:51.846 21151-21151/com.itbird D/FragmenViewPagerTestActivity: onStart
2022-03-25 16:07:51.853 21151-21151/com.itbird D/FragmenViewPagerTestActivity: onResume
2022-03-25 16:07:51.873 21151-21172/com.itbird D/OpenGLRenderer: HWUI GL Pipeline
2022-03-25 16:07:51.928 21151-21151/com.itbird D/BlankFragment1: onAttach
2022-03-25 16:07:51.930 21151-21151/com.itbird D/BlankFragment1: onCreate
2022-03-25 16:07:51.930 21151-21151/com.itbird D/BlankFragment1: onCreateView
2022-03-25 16:07:51.937 21151-21151/com.itbird D/BlankFragment1: onViewCreated
2022-03-25 16:07:51.937 21151-21151/com.itbird D/BlankFragment1: onActivityCreated
2022-03-25 16:07:51.938 21151-21151/com.itbird D/BlankFragment1: onStart
//如我们所料,BlankFragment2在不可见状态下,也就是缓存状态下,只执行到onStart
2022-03-25 16:07:51.939 21151-21151/com.itbird D/BlankFragment2: onAttach
2022-03-25 16:07:51.940 21151-21151/com.itbird D/BlankFragment2: onCreate
2022-03-25 16:07:51.940 21151-21151/com.itbird D/BlankFragment2: onCreateView
2022-03-25 16:07:51.946 21151-21151/com.itbird D/BlankFragment2: onViewCreated
2022-03-25 16:07:51.947 21151-21151/com.itbird D/BlankFragment2: onActivityCreated
2022-03-25 16:07:51.947 21151-21151/com.itbird D/BlankFragment2: onStart
//fragment1正常执行到onResume
2022-03-25 16:07:51.949 21151-21151/com.itbird D/BlankFragment1: onResume

我们这时去滑动到fragment2,应该BlankFragment2的onResume会被调用,BlankFragment1的生命周期不变,BlankFragment3应该会执行到onStart


也许会有人问,我没看到ViewPager哪里设置最大生命周期呀,为什么执行到onStart?大家可以看一下上面1.2
我们说到两个构造方法状态判断的时候,其实已经贴出了。这里再看一下
FragmentPagerAdapter#instantiateItem

 @Override
    public Object instantiateItem(@NonNull 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);
            //这里,初始化item fragment时,已经设置了为STARTED
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
            } else {
                fragment.setUserVisibleHint(false);
            }
        }

        return fragment;
    }

针对于add、remove、hide、show时,不同状态下的fragment操作设置最大生命周期,会带来的不同, 这里就不多赘述了。

小结
对于ViewPager+Fragment实现懒加载,在最新版的FragmentPagerAdapter,可以切换到BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT模式,在Fragment#onResume里判断,更符合显示逻辑

2.onHiddenChanged与setUserVisibleHint的区别

onHiddenChanged():对于通过FragmentManager自己管理fragment时,调用show、hide方法,此方法才会回调 。

setUserVisibleHint():对于调用系统ViewPager+Fragment方案,才会回调,但是这时候Fragment不会去调用上面说的onhiddenchanged方法,只会调用setUserVisibleHint这个方法。

3.ViewPager2与ViewPager


/**
 * ViewPager2 replaces {@link androidx.viewpager.widget.ViewPager}, addressing most of its
 * predecessor’s pain-points, including right-to-left layout support, vertical orientation,
 * modifiable Fragment collections, etc.
 *
 * @see androidx.viewpager.widget.ViewPager
 */
public final class ViewPager2 extends ViewGroup {
}

从文档注释来看ViewPager2确实是用来替代ViewPager 的,顺带解决之前ViewPager的一些问题,并且加入了 RTL,竖向滚动支持。

3.1 新功能&API的变动

我们先来看看有哪些功能和使用上的变化:

新功能:
支持RTL布局
支持竖向滚动
完整支持notifyDataSetChanged

API的变动:
FragmentStateAdapter替换了原来的 FragmentStatePagerAdapter
RecyclerView.Adapter替换了原来的 PagerAdapter
registerOnPageChangeCallback替换了原来的 addPageChangeListener

看了上面这些介绍,有一点比较吸引人的就是支持竖向滚动了,这是怎么实现的呢?ViewPager2的源码不长,我们来简单分析一下。

3.2 源码解析

通过查看源码得知,ViewPager2是直接继承ViewGroup的,意味着和ViewPager不兼容,类注释上也写了它的作用是取代ViewPager,不过短时间内ViewPager应该还不会被废弃掉。
继续查看源码,发现了两个比较重要的成员变量:

public final class ViewPager2 extends ViewGroup {
    int mCurrentItem;
    boolean mCurrentItemDirty = false;
    private RecyclerView.AdapterDataObserver mCurrentItemDataSetChangeObserver =
            new DataSetChangeObserver() {
                @Override
                public void onChanged() {
                    mCurrentItemDirty = true;
                    mScrollEventAdapter.notifyDataSetChangeHappened();
                }
            };

    private LinearLayoutManager mLayoutManager;
    RecyclerView mRecyclerView;
    private PagerSnapHelper mPagerSnapHelper;
    ScrollEventAdapter mScrollEventAdapter;
    private PageTransformerAdapter mPageTransformerAdapter;
    private RecyclerView.ItemAnimator mSavedItemAnimator = null;

    public ViewPager2(@NonNull Context context) {
        super(context);
        initialize(context, null);
    }
}

很清楚得知,ViewPager2的核心实现就是RecyclerView+LinearLayoutManager了,因为LinearLayoutManager本身就支持竖向和横向两种布局方式,所以ViewPager2也能很容易地支持这两种滚动方向了,而几乎不需要添加任何多余的代码。
为了让RecyclerView变得像原来的ViewPager,需要设置下SnapHelper:

        mPagerSnapHelper = new PagerSnapHelperImpl();
        mPagerSnapHelper.attachToRecyclerView(mRecyclerView);

熟悉RecyclerView的同学都知道,SnapHelper用于辅助RecyclerView在滚动结束时将Item对齐到某个位置。PagerSnapHelper的作用让滑动结束时使当前Item居中显示,并且 限制一次只能滑动一页,不能快速滑动,这样就和viewpager的交互很像了。

另外和viewpager一样,viewpager2可以承载fragment,我们需要继承实现它提供的FragmentStateAdapter

public abstract class FragmentStateAdapter extends
        RecyclerView.Adapter implements StatefulAdapter {
    // State saving config
    private static final String KEY_PREFIX_FRAGMENT = "f#";
    private static final String KEY_PREFIX_STATE = "s#";

    // Fragment GC config
    private static final long GRACE_WINDOW_TIME_MS = 10_000; // 10 seconds

    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
    final Lifecycle mLifecycle;
    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
    final FragmentManager mFragmentManager;

    // Fragment bookkeeping
    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
    final LongSparseArray mFragments = new LongSparseArray<>();
    private final LongSparseArray mSavedStates = new LongSparseArray<>();
    private final LongSparseArray mItemIdToViewHolder = new LongSparseArray<>();

    private FragmentMaxLifecycleEnforcer mFragmentMaxLifecycleEnforcer;

    // Fragment GC
    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
    boolean mIsInGracePeriod = false;
    private boolean mHasStaleFragments = false;

    /**
     * @param fragmentActivity if the {@link ViewPager2} lives directly in a
     * {@link FragmentActivity} subclass.
     *
     * @see FragmentStateAdapter#FragmentStateAdapter(Fragment)
     * @see FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
     */
    public FragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
        this(fragmentActivity.getSupportFragmentManager(), fragmentActivity.getLifecycle());
    }
}

3.3 懒加载

我们在看ViewPager代码的时候,有一个缓冲默认最小为1机制,所以需要实现懒加载,需要借助fragment相应生命周期去自己实现,尽管后来又setMaxLifecycle等函数,但是我们依然会觉得ViewPager本身的缺陷,一直在通过外界来规避。
我们看一下ViewPager2是否已解决这个问题
ViewPager2#setOffscreenPageLimit

    public void setOffscreenPageLimit(@OffscreenPageLimit int limit) {
        if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) {
            throw new IllegalArgumentException(
                    "Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0");
        }
        mOffscreenPageLimit = limit;
        // Trigger layout so prefetch happens through getExtraLayoutSize()
        mRecyclerView.requestLayout();
    }

从代码中看到,ViewPager2实际上允许设置limit 为0,只不过会抛出一个异常,但是通过上层来捕获就可以了,但是实际上生效的,也就说缓存为0的效果实现了。不像ViewPager源码中,检测到limit小于1,会强制修改为1。
ViewPager#setOffscreenPageLimit

public static final int DEFAULT_OFFSCREEN_PAGES = 1;
   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();
       }
   }

3.4 局部刷新

从上面的源码分析我们知道,Viewpager2利用recyclerview来实现viewpager的功能,那么自然而然,recyclerview拥有局部刷新的功能,Viewpager2肯定也是有的。

public abstract class FragmentStateAdapter extends
        RecyclerView.Adapter implements StatefulAdapter {}

可以看到FragmentStateAdapter 继承与RecyclerView.Adapter,所以notifyDataSetChanged & notifyItemChanged(int position)两个功能,肯定在FragmentStateAdapter 也拥有了。

总结

1.Fragment中废弃了setUserVisibleHint方法,而是用setMaxLifecycle替代,是因为可以解决Fragment生命周期与可见之前不同步的问题。另外一方面,Google官方也通过此方法,给我们公开了可以控制Fragment生命周期的方法。
2.onHiddenChanged是自己通过FragmentManager管理Fragment时才会调用,ViewPager+Fragment方案管理Fragment时,回调的是setUserVisibleHint方法。
3.Viewpager2利用Recyclerview来实现Viewpager的功能,无疑使使其可扩展性大大提升,代码也变得更优雅简洁,使用起来也更灵活。同时内部也支持实现了懒加载、局部刷新功能。

你可能感兴趣的:(Android组件之Fragment(三)---实战知识)