RecyclerView扩展(一)ViewPager2使用与原理
ViewPager2的使用
ViewPager2实际是内部的RecyclerView
在起作用,所以ViewPager2一定要设置RecyclerView.Adapter
赋值,并且Adapter的ItemView的布局必须为match_parent
。才能正常使用。ViewPager2+Fragment
提供了一个新的适配器FragmentStateAdapter
。
ViewPager2+Fragment代码实例
FragmentStateAdapter实例
class ViewPagerAdapter : FragmentStateAdapter {
private var fragmentList: MutableList<Fragment> = ArrayList()
constructor(fragmentActivity: FragmentActivity) : super(fragmentActivity)
constructor(fragment: Fragment) : super(fragment)
constructor(fragmentManager: FragmentManager, lifecycle: Lifecycle) : super(
fragmentManager,
lifecycle
)
fun addFragment(fragment: Fragment?) {
if (fragment == null) {
return
}
fragmentList.add(fragment)
}
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
Activity中为Viewpager2初始化设置Adapter
val pageAdapter = ViewPagerAdapter(this)
pageAdapter.addFragment(FirstFragment())
pageAdapter.addFragment(TwoFragment())
pageAdapter.addFragment(ThreeFragment())
viewPage2.adapter = pageAdapter
ViewPager2的常见的API:
// 可滑动的方向
viewPage?.orientation = ViewPager2.ORIENTATION_HORIZONTAL
// 用户是否可滑动
viewPage?.isUserInputEnabled = true
// 页面滑动监听
viewPage?.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { //监听事件
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
}
})
// 设置转场动画
val compositePageTransformer = CompositePageTransformer()
compositePageTransformer.addTransformer(MarginPageTransformer(40))
compositePageTransformer.addTransformer(ScaleTransformer())
viewPage?.setPageTransformer(compositePageTransformer)
viewPage?.beginFakeDrag() // 开始模拟滑动
if (viewPage?.fakeDragBy(-500f) == true) { //模拟滑动
viewPage?.endFakeDrag() //结束模拟滑动
}
以上就是ViewPager2的使用和属性设置,功能还是比较强大的,接下来看看Viewpager2的源码进行原理分析。
ViewPager2原理浅析
从ViewPager2的构造函数开始分析:
public ViewPager2(@NonNull Context context) {
super(context);
initialize(context, null);
}
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
//...
// 初始化RecyclerView
mRecyclerView = new RecyclerViewImpl(context);
mRecyclerView.setId(ViewCompat.generateViewId());
mRecyclerView.setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
// 初始化LayoutManager,所以我们不用设置LayoutManager
mLayoutManager = new LinearLayoutManagerImpl(context);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
setOrientation(context, attrs);
mRecyclerView.setLayoutParams(
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mRecyclerView.addOnChildAttachStateChangeListener(enforceChildFillListener());
//创建滑动事件转换器对象,通过监听RecyclerView的监听把状态传给ViewPager2
mScrollEventAdapter = new ScrollEventAdapter(this);
// 创建模拟拖动器对象,用于提供代码模拟滑动
mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);
// 创建PagerSnapHelper对象,实现页面切换居中且只滑动一页的效果
mPagerSnapHelper = new PagerSnapHelperImpl();
mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
// 设置RecyclerView的滑动监听
mRecyclerView.addOnScrollListener(mScrollEventAdapter);
//....
}
// 重新封装了RecyclerView,处理拦截事件
private class RecyclerViewImpl extends RecyclerView {
// 设置用户是否可以滑动控件
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
return isUserInputEnabled() && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isUserInputEnabled() && super.onInterceptTouchEvent(ev);
}
}
// 封装LinearLayoutManager,处理缓存页面
private class LinearLayoutManagerImpl extends LinearLayoutManager {
// 设置缓存页面
@Override
protected void calculateExtraLayoutSpace(@NonNull RecyclerView.State state,
@NonNull int[] extraLayoutSpace) {
int pageLimit = getOffscreenPageLimit();
if (pageLimit == OFFSCREEN_PAGE_LIMIT_DEFAULT) {
// Only do custom prefetching of offscreen pages if requested
super.calculateExtraLayoutSpace(state, extraLayoutSpace);
return;
}
final int offscreenSpace = getPageSize() * pageLimit;
extraLayoutSpace[0] = offscreenSpace;
extraLayoutSpace[1] = offscreenSpace;
}
}
// 封装PagerSnapHelper,处理模拟拖动时的目标View
private class PagerSnapHelperImpl extends PagerSnapHelper {
// 如果正在模拟拖动则不设置目标View
@Nullable
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
return isFakeDragging() ? null : super.findSnapView(layoutManager);
}
}
在ViewPager2的构造方法中都会调用initialize
方法进行初始化,其中实例化了一个封装的RecyclerView,并为这个RecyclerView设置了layoutManager、OnScrollListener监听、SnapHelper
。
- 封装了RecyclerView,主要是
onTouchEvent、onInterceptTouchEvent
多加了isUserInputEnabled
变量来判断是否拦截事件,从而实现控制用户是否可以滑动功能。 - 封装了LinearLayoutManager,主要是在
calculateExtraLayoutSpace
方法中根据OffscreenPageLimit
扩展可用的空间,从而实现页面的缓存功能。 - 封装了PagerSnapHelper,主要是在
findSnapView
中判断是否正在模拟滑动中,如果是则不设置目标View。它的作用是使RecyclerView每次只能滑动一页,而且ItemView会自动居中显示。PagerSnapHelper在这篇不在分析,不熟悉可以看看之前写的文章:RecycerView扩展SnapHepler源码分析 -
mScrollEventAdapter
是RecyclerView滑动监听的对象,把RecyclerView的滑动监听状态转换成ViewPager2的OnPageChangeCallback
的三种状态。 -
mFakeDragger
是实现模拟拖动器的对象。
ScrollEventAdapter
ScrollEventAdapter
直译意思是:滑动事件适配器。类如其名,它的作用就是将RecyclerView的滑动事件适配成ViewPager2的OnPageChangeCallback的事件
public abstract static class OnPageChangeCallback {
// 页面正在滑动时调用
public void onPageScrolled(int position, float positionOffset,
@Px int positionOffsetPixels) {
}
// 页面切换后调用
public void onPageSelected(int position) {
}
// 页面状态发生变化后调用:开始滑动前、手势离开后、滑动停止
public void onPageScrollStateChanged(@ScrollState int state) {
}
}
final class ScrollEventAdapter extends RecyclerView.OnScrollListener {
private OnPageChangeCallback mCallback;
ScrollEventAdapter(@NonNull ViewPager2 viewPager) {
// 根据ViewPager2拿到一些相关信息
mViewPager = viewPager;
mRecyclerView = mViewPager.mRecyclerView;
//noinspection ConstantConditions
mLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mScrollValues = new ScrollEventValues();
resetState(); // 变量初始化
}
// 给ViewPager2设置
void setOnPageChangeCallback(OnPageChangeCallback callback) {
mCallback = callback;
}
// 将RecyclerView的滑动适配成 页面滑动状态变化
private void dispatchStateChanged(@ScrollState int state) {
if (mAdapterState == STATE_IN_PROGRESS_IMMEDIATE_SCROLL
&& mScrollState == SCROLL_STATE_IDLE) {
return;
}
if (mScrollState == state) {
return;
}
mScrollState = state;
if (mCallback != null) {
mCallback.onPageScrollStateChanged(state);
}
}
// 将RecyclerView的滑动适配成 页面切换完成状态
private void dispatchSelected(int target) {
if (mCallback != null) {
mCallback.onPageSelected(target);
}
}
// 将RecyclerView的滑动适配成 页面滑动
private void dispatchScrolled(int position, float offset, int offsetPx) {
if (mCallback != null) {
mCallback.onPageScrolled(position, offset, offsetPx);
}
}
// 这个方法只有在RecyclerView滑动之前、手势离开屏幕后、滑动结束后会调用。
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
// 滑动之前
if ((mAdapterState != STATE_IN_PROGRESS_MANUAL_DRAG
|| mScrollState != SCROLL_STATE_DRAGGING)
&& newState == RecyclerView.SCROLL_STATE_DRAGGING) {
// 此方法随后调用 dispatchStateChanged(SCROLL_STATE_DRAGGING)适配成Pager的事件
startDrag(false);
return;
}
// 手指离开后
if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_SETTLING) {
if (mScrollHappened) {
// 调用dispatchStateChanged(SCROLL_STATE_SETTLING) 适配成Page事件
dispatchStateChanged(SCROLL_STATE_SETTLING);
mDispatchSelected = true;
}
return;
}
// 滑动结束后
if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_IDLE) {
boolean dispatchIdle = false;
updateScrollEventValues();
if (!mScrollHappened) {
// 在拖动过程中页面没有移动,因此我们位于列表的开头或结尾。或根本没有页面。
// 在第一种情况下,ViewPager的合同要求至少一个滚动事件。在第二种情况下,不要发送该滚动事件
if (mScrollValues.mPosition != RecyclerView.NO_POSITION) {
// 如果在头尾则给一个滑动事件
dispatchScrolled(mScrollValues.mPosition, 0f, 0);
}
dispatchIdle = true;
} else if (mScrollValues.mOffsetPx == 0) {
dispatchIdle = true;
if (mDragStartPosition != mScrollValues.mPosition) {
// 滑动完成切换事件
dispatchSelected(mScrollValues.mPosition);
}
}
if (dispatchIdle) {
// 滑动状态改变事件
dispatchStateChanged(SCROLL_STATE_IDLE);
resetState();
}
}
if (mAdapterState == STATE_IN_PROGRESS_SMOOTH_SCROLL
&& newState == RecyclerView.SCROLL_STATE_IDLE && mDataSetChangeHappened) {
updateScrollEventValues();
if (mScrollValues.mOffsetPx == 0) {
if (mTarget != mScrollValues.mPosition) {
// 滑动切换完成事件
dispatchSelected(
mScrollValues.mPosition == NO_POSITION ? 0 : mScrollValues.mPosition);
}
// 滑动状态发生变化事件
dispatchStateChanged(SCROLL_STATE_IDLE);
resetState();
}
}
}
// recyclerView滑动时会调用
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
mScrollHappened = true;
updateScrollEventValues();
// 在手指滑动时离开屏幕时mDispatchSelected设置为true
if (mDispatchSelected) {
// 检车是否滑动完成,调用dispatchSelected()方法适配调用
// Drag started settling, need to calculate target page and dispatch onPageSelected now
mDispatchSelected = false;
boolean scrollingForward = dy > 0 || (dy == 0 && dx < 0 == mViewPager.isRtl());
// "&& values.mOffsetPx != 0": filters special case where we're scrolling forward and
// the first scroll event after settling already got us at the target
mTarget = scrollingForward && mScrollValues.mOffsetPx != 0
? mScrollValues.mPosition + 1 : mScrollValues.mPosition;
if (mDragStartPosition != mTarget) {
dispatchSelected(mTarget);
}
} else if (mAdapterState == STATE_IDLE) {
// onScrolled while IDLE means RV has just been populated after an adapter has been set.
// Contract requires us to fire onPageSelected as well.
int position = mScrollValues.mPosition;
// Contract forbids us to send position = -1 though
dispatchSelected(position == NO_POSITION ? 0 : position);
}
// 调用dispatchScrolled适配页面滑动。如果位置是-1,则用0代替。
dispatchScrolled(mScrollValues.mPosition == NO_POSITION ? 0 : mScrollValues.mPosition,
mScrollValues.mOffset, mScrollValues.mOffsetPx);
// Dispatch idle in onScrolled instead of in onScrollStateChanged because RecyclerView
// doesn't send IDLE event when using setCurrentItem(x, false)
if ((mScrollValues.mPosition == mTarget || mTarget == NO_POSITION)
&& mScrollValues.mOffsetPx == 0 && !(mScrollState == SCROLL_STATE_DRAGGING)) {
// When the target page is reached and the user is not dragging anymore, we're settled,
// so go to idle.
// Special case and a bit of a hack when mTarget == NO_POSITION: RecyclerView is being
// initialized and fires a single scroll event. This flags mScrollHappened, so we need
// to reset our state. However, we don't want to dispatch idle. But that won't happen;
// because we were already idle.
dispatchStateChanged(SCROLL_STATE_IDLE);
resetState();
}
}
}
ScrollEventAdapter作为一个RecyclerView.OnScrollListener
的实例类。在监听RecyclerView的滑动状态的两个方法onScrollStateChanged、onScrolled
中,根据不同的状态调用dispatchStateChanged、dispatchSelected、dispatchScrolled
设配成ViewPager2的页面滑动接口OnPageChangeCallback
。
PageTransformerAdapter
PageTransformerAdapter
是ViewPager2.OnPageChangeCallback
的实例类,作用是把OnPageChangeCallback.onPageScrolled
的事件适配成PageTransformer.transformPage()
事件。
先看看PageTransformerAdapter
在ViewPager2被调用的地方:
private void initialize(Context context, AttributeSet attrs) {
// ....
// 初始化PageTransformerAdapter,并把实例添加进CompositeOnPageChangeCallback中
mPageTransformerAdapter = new PageTransformerAdapter(mLayoutManager);
mPageChangeEventDispatcher.addOnPageChangeCallback(mPageTransformerAdapter);
}
public void setPageTransformer(@Nullable PageTransformer transformer) {
if (transformer != null) {
if (!mSavedItemAnimatorPresent) {
mSavedItemAnimator = mRecyclerView.getItemAnimator();
mSavedItemAnimatorPresent = true;
}
mRecyclerView.setItemAnimator(null);
} else {
if (mSavedItemAnimatorPresent) {
mRecyclerView.setItemAnimator(mSavedItemAnimator);
mSavedItemAnimator = null;
mSavedItemAnimatorPresent = false;
}
}
// TODO: add support for reverseDrawingOrder: b/112892792
// TODO: add support for pageLayerType: b/112893074
if (transformer == mPageTransformerAdapter.getPageTransformer()) {
return;
}
// 将设置进来的PageTransformer给mPageTransformerAdapter
mPageTransformerAdapter.setPageTransformer(transformer);
requestTransform();
}
public interface PageTransformer {
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -2 is two pages to the left.
* Minimum / maximum observed values depend on how many pages we keep
* attached, which depends on offscreenPageLimit.
*
* @see #setOffscreenPageLimit(int)
*/
void transformPage(@NonNull View page, float position);
}
final class PageTransformerAdapter extends OnPageChangeCallback {
private final LinearLayoutManager mLayoutManager;
private PageTransformer mPageTransformer;
PageTransformerAdapter(LinearLayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
PageTransformer getPageTransformer() {
return mPageTransformer;
}
/**
* Sets the PageTransformer. The page transformer will be called for each attached page whenever
* the scroll position is changed.
*
* @param transformer The PageTransformer
*/
void setPageTransformer(@Nullable PageTransformer transformer) {
// TODO: add support for reverseDrawingOrder: b/112892792
// TODO: add support for pageLayerType: b/112893074
mPageTransformer = transformer;
}
// 在onPageScrolled的事件转换成PageTransformer.transformPage事件
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mPageTransformer == null) {
return;
}
float transformOffset = -positionOffset;
// 能遍历多少,取决于缓存了多少个ItemView
for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
View view = mLayoutManager.getChildAt(i);
if (view == null) {
throw new IllegalStateException(String.format(Locale.US,
"LayoutManager returned a null child at pos %d/%d while transforming pages",
i, mLayoutManager.getChildCount()));
}
//以ViewPager的中心为原点,左为负,右为正,当前View的中心点距离ViewPager的中心的距离。
int currPos = mLayoutManager.getPosition(view);
float viewOffset = transformOffset + (currPos - position);
mPageTransformer.transformPage(view, viewOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
PageTransformerAdapter的作用就是为ViewPager2的PageTransformer,在页面滑动的时候转换成PageTransformer. transformPage()。其中的transformPage(@NonNull View page, float position)方法的参数值可以参考这篇文章PageTransformer详解。ViewPager2如果想要实现页面之间的转换时动画可以重写PageTransformer
FragmentStateAdapter
FragmentStateAdapter
是谷歌为我们实现适配加载Fragment
的RecyclerView.Adapter
,如果我们想用ViewPager2加载Fragment页面,就可以继承FragmentStateAdapter
作为适配器,只要重写其构造方法
和createFragment
、getItemCount
。
class VPFragmentAdapter2 : FragmentStateAdapter {
private val fragmentList: MutableList<Fragment> = ArrayList()
constructor(fragmentActivity: FragmentActivity) : super(fragmentActivity)
constructor(fragment: Fragment) : super(fragment)
constructor(fragmentManager: FragmentManager, lifecycle: Lifecycle) : super(
fragmentManager,
lifecycle
)
fun addFragment(fragment: Fragment) {
fragmentList.add(fragment)
}
fun getCurrentFragment(index: Int): Fragment? {
if (index >= 0 && index < fragmentList.size) {
return fragmentList[index]
}
return null
}
fun clear() {
fragmentList.clear()
}
//
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
// RecyclerView.Adapter的抽象方法
override fun getItemCount(): Int {
return fragmentList.size
}
}
这就是一个简易版的ViewPager2的FragmentAdapter。由此可见FragmentStateAdapter帮我们实现了Adapter的onCreateViewHolder
、onBindViewHolder
抽象方法。在onCreateViewHolder
方法中创建了一个以FrameLayout
为父类的View装载Fragment
。
public abstract class FragmentStateAdapter extends
RecyclerView.Adapter<FragmentViewHolder> implements StatefulAdapter {
@SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
//HashMap (position,Fragment)
final LongSparseArray<Fragment> mFragments = new LongSparseArray<>();
//HashMap (position,Fragment.SavedState)
private final LongSparseArray<Fragment.SavedState> mSavedStates = new LongSparseArray<>();
//HashMap (position,ViewId)
private final LongSparseArray<Integer> mItemIdToViewHolder = new LongSparseArray<>();
public abstract @NonNull Fragment createFragment(int position);
@NonNull
@Override
public final FragmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 创建一个以FrameLayout的父容器
return FragmentViewHolder.create(parent);
}
@Override
public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
final long itemId = holder.getItemId();
// FrameLayout的Id
final int viewHolderId = holder.getContainer().getId();
// 通过HashMap,根据ViewId得到此之前加载的position
final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
// 此ViewHolder之前加载过Fragment,且与需要加载Fragment不同,则将ViewHolder的旧Fragment移除
if (boundItemId != null && boundItemId != itemId) {
// 移除FrameLayout的Fragment和HashMap的数据
removeFragment(boundItemId);
// 移除HashMap的数据
mItemIdToViewHolder.remove(boundItemId);
}
// 重新设置HashMap
mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
ensureFragment(position);
// 特殊情况,当RecyclerView让ItemView保持在Window,但是不在视图树中。
final FrameLayout container = holder.getContainer();
if (ViewCompat.isAttachedToWindow(container)) {
if (container.getParent() != null) {
throw new IllegalStateException("Design assumption violated.");
}
// 当ItemView添加在到RecyclerView中才加载Fragment
container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (container.getParent() != null) {
container.removeOnLayoutChangeListener(this);
// 加载Fragment
placeFragmentInViewHolder(holder);
}
}
});
}
gcFragments();
}
// 移除FrameLayout的Fragment和HashMap的数据
private void removeFragment(long itemId) {
// 得到相应位置的Fragment
Fragment fragment = mFragments.get(itemId);
// fragment为空,则不往下执行
if (fragment == null) {
return;
}
// 将FrameLayout清空子View布局
if (fragment.getView() != null) {
ViewParent viewParent = fragment.getView().getParent();
if (viewParent != null) {
((FrameLayout) viewParent).removeAllViews();
}
}
// 移除旧位置的状态
if (!containsItem(itemId)) {
mSavedStates.remove(itemId);
}
// fragment已经没有依附,则移除mFragments对应的位置,并return
if (!fragment.isAdded()) {
mFragments.remove(itemId);
return;
}
// 应该延迟Fragment交易
if (shouldDelayFragmentTransactions()) {
mHasStaleFragments = true;
return;
}
if (fragment.isAdded() && containsItem(itemId)) {
mSavedStates.put(itemId, mFragmentManager.saveFragmentInstanceState(fragment));
}
mFragmentManager.beginTransaction().remove(fragment).commitNow();
mFragments.remove(itemId);
}
// 更新mFragments的数据
private void ensureFragment(int position) {
long itemId = getItemId(position);
if (!mFragments.containsKey(itemId)) {
// TODO(133419201): check if a Fragment provided here is a new Fragment
Fragment newFragment = createFragment(position);
newFragment.setInitialSavedState(mSavedStates.get(itemId));
mFragments.put(itemId, newFragment);
}
}
}
在重写的onCreateViewHolder
方法中,通过FragmentViewHolder.create
创建一个以FrameLayout
为父容器的ItemView。
在重写的onBindViewHolder
方法中,1. 先检测ItemView是否有加载过Fragment,如果加载过的Fragment与现在的Fragment不相同,则对ItemView和三个HashMap进行数据清空。2. 对两个HashMap设置新的数据。 3. 如果存在特殊情况,则当ItemView添加在到RecyclerView中才加载Fragment。
在ItemView依附在RecyclerView时,开始加载Fragment
具体能有多少个ItemView依附在RecyclerView中,取决于ViewPager2的缓存大小。
@Override
public final void onViewAttachedToWindow(@NonNull final FragmentViewHolder holder) {
// 加载Fragment
placeFragmentInViewHolder(holder);
gcFragments();
}
// 加载Fragment
@SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) {
// 通过mFragments拿到Fragment和Fragment的View
Fragment fragment = mFragments.get(holder.getItemId());
if (fragment == null) {
throw new IllegalStateException("Design assumption violated.");
}
FrameLayout container = holder.getContainer();
View view = fragment.getView();
/ *可能的状态:
-fragment:{添加,未添加}
-视图:{已创建,未创建}
-视图:{已附加,未附加}
组合:
-{f:已添加,v:已创建,v:已附加}->检查是否已附加到正确的容器
-{f:已添加,v:已创建,v:没附加}->将视图附加到容器
-{f:已添加,v:没创建,v:已附加}->不可能
-{f:已添加,v:没创建 ,v:没附加}->计划创建时的调用
-{f:没添加,v:已创建,v:已附加}->非法状态
-{f:没添加,v:已创建,v:没附加}->非法状态
-{f:没添加,v:没创建,v:已附加}->不可能
-{f:没添加,v:没创建,v:没附加}->添加,创建,附加
* /
// { f:没添加, v:已创建, v:attached } -> 非法状态
// { f:没添加, v:已创建, v:notAttached } ->非法状态
if (!fragment.isAdded() && view != null) {
throw new IllegalStateException("Design assumption violated.");
}
// {f:添加,v:没创建,v:没附加}->计划创建时的回调
if (fragment.isAdded() && view == null) {
scheduleViewAttach(fragment, container);
return;
}
//{f:添加,v:创建,v:附加}->检查是否附加到正确的容器
if (fragment.isAdded() && view.getParent() != null) {
if (view.getParent() != container) {
addViewToContainer(view, container);
}
return;
}
//{f:已添加,v:已创建,v:未附加}->将视图附加到容器
if (fragment.isAdded()) {
addViewToContainer(view, container);
return;
}
//{f:没添加,v:没创建,v:未附加}->添加,创建,附加
if (!shouldDelayFragmentTransactions()) {
scheduleViewAttach(fragment, container);
mFragmentManager.beginTransaction()
.add(fragment, "f" + holder.getItemId())
.setMaxLifecycle(fragment, STARTED)
.commitNow();
mFragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(false);
} else {
if (mFragmentManager.isDestroyed()) {
return; // nothing we can do
}
mLifecycle.addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (shouldDelayFragmentTransactions()) {
return;
}
source.getLifecycle().removeObserver(this);
if (ViewCompat.isAttachedToWindow(holder.getContainer())) {
placeFragmentInViewHolder(holder);
}
}
});
}
}
// 检查是否附加到正确的容器,将视图附加到容器
@SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
void addViewToContainer(@NonNull View v, @NonNull FrameLayout container) {
if (container.getChildCount() > 1) {
throw new IllegalStateException("Design assumption violated.");
}
if (v.getParent() == container) {
return;
}
if (container.getChildCount() > 0) {
container.removeAllViews();
}
if (v.getParent() != null) {
((ViewGroup) v.getParent()).removeView(v);
}
container.addView(v);
}
// 计划创建时的回调
private void scheduleViewAttach(final Fragment fragment, @NonNull final FrameLayout container) {
// After a config change, Fragments that were in FragmentManager will be recreated. Since
// ViewHolder container ids are dynamically generated, we opted to manually handle
// attaching Fragment views to containers. For consistency, we use the same mechanism for
// all Fragment views.
mFragmentManager.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
// TODO(b/141956012): Suppressed during upgrade to AGP 3.6.
@SuppressWarnings("ReferenceEquality")
@Override
public void onFragmentViewCreated(@NonNull FragmentManager fm,
@NonNull Fragment f, @NonNull View v,
@Nullable Bundle savedInstanceState) {
if (f == fragment) {
fm.unregisterFragmentLifecycleCallbacks(this);
addViewToContainer(v, container);
}
}
}, false);
}
加载Fragment的流程在就在placeFragmentInViewHolder
方法里,通过检查fragment和View的状态分别作出不同的操作。
关于ViewPager2的源码浅析就讲完了,下面总结一下:
总结
ScrollEventAdapter的作用:是将RecyclerView的滑动事件
转换成ViewPager2的OnPageChangeCallBack
。
PageTransformerAdapter的作用:是将RecyclerView的滑动事件
转换成ViewPager2的PageTransformer
。
FragmentStateAdapter的作用:是为ViewPager2加载Fragment提供基础的Adapter
。