本文在前面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的功能,无疑使使其可扩展性大大提升,代码也变得更优雅简洁,使用起来也更灵活。同时内部也支持实现了懒加载、局部刷新功能。