ViewPager2的源码分析

一 ViewPager2新特性

基于RecyclerView实现。这意味着RecyclerView的优点将会被ViewPager2所继承。

2.支持竖直滑动。只需要一个参数就可以改变滑动方向。

3.支持关闭用户输入。通过setUserInputEnabled来设置是否禁止用户滑动页面。

4.支持通过编程方式滚动。通过fakeDragBy(offsetPx)代码模拟用户滑动页面。

5.CompositePageTransformer 支持同时添加多个PageTransformer。

6.支持DiffUtil ,可以添加数据集合改变的item动画。

7.支持RTL (right-to-left)布局。

二 基本使用

  1. 引入viewpager2库
// module的build.gradle文件
implementation("androidx.viewpager2:viewpager2:1.0.0")

  2 在布局中添加viewpager2

  

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

3 继承FragmentStateAdapter创建adapter

class DemoAapdater(activity: FragmentActivity) : FragmentStateAdapter(activity) {

    override fun getItemCount(): Int {
        return 6
    }

    override fun createFragment(position: Int): Fragment {
        val demoFragment = DemoFragment()
        demoFragment.arguments = Bundle().apply {
            putInt("TEXT", position)
        }
        return demoFragment
    }
}

4  为viewpager2设置adapter

demoAapdater = DemoAapdater(this)
viewPager2.adapter = demoAapdater

三 ViewPager2源码分析

  ViewPager初始化

    private void initialize(Context context, AttributeSet attrs) {
    	// .... 省约部分
        
        // 初始化RecyclerView
        mRecyclerView = new RecyclerViewImpl(context);
        mRecyclerView.setId(ViewCompat.generateViewId());
        // 初始化LayoutManager
        mLayoutManager = new LinearLayoutManagerImpl(context);
        mRecyclerView.setLayoutManager(mLayoutManager);
        setOrientation(context, attrs);

        mRecyclerView.setLayoutParams(
                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        mRecyclerView.addOnChildAttachStateChangeListener(enforceChildFillListener());

        // 创建滑动事件转换器的对象
        mScrollEventAdapter = new ScrollEventAdapter(mLayoutManager);
        // 创建模拟拖动事件的对象
        mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);
        // 创建PagerSnapHelper对象,用来实现页面切换的基本效果
        mPagerSnapHelper = new PagerSnapHelperImpl();
        mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
    
        mRecyclerView.addOnScrollListener(mScrollEventAdapter);
        // ······
    }

1.其中mRecyclerView = new RecyclerViewImpl(context);和mLayoutManager = new LinearLayoutManagerImpl(context); 属于重新封装的RecyclerView和LinearLayoutManager, 是分别为了处理事件拦截和ViewPager缓存页面的问题。

2.给RecyclerView设置了滑动监听事件,涉及到的组件是ScrollEventAdapter,后面的基本功能都需要这个组件的支持; (2) 设置了PagerSnapHelper,目的是实现切面切换的效果

 1.PagerSnapHelper分析

这个类主要继承了SnapHelper 然后实现了里面3个重要的方法

calculateDistanceToFinalSnap()
findSnapView()
findTargetSnapPosition()

这三个方法在RecyclerView 滑动时调用

1.手指在快速滑动一个RecyclerView,在手指离开屏幕之前,如上的三个方法都不会被调用。

2.如果手指如果手指离开了屏幕,接下来就是Fling事件来滑动RecyclerView,在Fling事件触发之际,findTargetSnapPosition方法会被调用,此方法的作用就是用来计算Fling事件能滑动到位置。

@Override
    public boolean onFling(int velocityX, int velocityY) {
        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
        if (layoutManager == null) {
            return false;
        }
        RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
        if (adapter == null) {
            return false;
        }
        int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
        return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
                 1
                && snapFromFling(layoutManager, velocityX, velocityY);
    }
private boolean snapFromFling(@NonNull RecyclerView.LayoutManager layoutManager, int velocityX,
            int velocityY) {
        if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
            return false;
        }

        RecyclerView.SmoothScroller smoothScroller = createScroller(layoutManager);
        if (smoothScroller == null) {
            return false;
        }

        int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
        if (targetPosition == RecyclerView.NO_POSITION) {
            return false;
        }

        smoothScroller.setTargetPosition(targetPosition);
        layoutManager.startSmoothScroll(smoothScroller);
        return true;
    }

3.当Fling事件结束之际,RecyclerView会回调SnapHelper内部OnScrollListener接口的onScrollStateChanged方法。

 private final RecyclerView.OnScrollListener mScrollListener =
            new RecyclerView.OnScrollListener() {
                boolean mScrolled = false;

                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
                        mScrolled = false;
                        1 此时RecyclerView的滑动状态为   RecyclerView.SCROLL_STATE_IDLE,所以就会分别调用findSnapView方法来找到需要显示在RecyclerView的最前面的View。找到目标View之后,就会调用calculateDistanceToFinalSnap方法来计算需要滑动的距离,然后调动RecyclerView相关方法进行滑动。
                        snapToTargetExistingView();
                    }
                }

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    if (dx != 0 || dy != 0) {
                        mScrolled = true;
                    }
                }
            };

接着分析 

snapToTargetExistingView

当RecyclerView在Fling时,如果想要不去拦截Fling时间,想让RecyclerView开心的Fling,可以直接在findTargetSnapPosition方法返回RecyclerView.NO_POSITION即可,或者我们可以在findTargetSnapPosition方法来计算滑动的最终位置,然后通过SmoothScroller来实现滑动。。

 /**
     * Snaps to a target view which currently exists in the attached {@link RecyclerView}. This
     * method is used to snap the view when the {@link RecyclerView} is first attached; when
     * snapping was triggered by a scroll and when the fling is at its final stages.
     */
    void snapToTargetExistingView() {
        if (mRecyclerView == null) {
            return;
        }
        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
        if (layoutManager == null) {
            return;
        }
        注释1 
        View snapView = findSnapView(layoutManager);
        if (snapView == null) {
            return;
        }
        注释2 
        int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, snapView);
        if (snapDistance[0] != 0 || snapDistance[1] != 0) {
            mRecyclerView.smoothScrollBy(snapDistance[0], snapDistance[1]);
        }
    }

因为viewPager2是不支持快速滑动Filing事件的,所以在PagerSnapHelper中的findTargetSnapPosition()方法中做了处理下面看看

@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
        int velocityY) {
    // ······
    // 1 找到与当前View相邻的View,包括左相邻和右响铃,并且计算滑动的距离
    for (int i = 0; i < childCount; i++) {
        final View child = layoutManager.getChildAt(i);
        if (child == null) {
            continue;
        }
        final int distance = distanceToCenter(layoutManager, child, orientationHelper);

        if (distance <= 0 && distance > distanceBefore) {
            // Child is before the center and closer then the previous best
            distanceBefore = distance;
            closestChildBeforeCenter = child;
        }
        if (distance >= 0 && distance < distanceAfter) {
            // Child is after the center and closer then the previous best
            distanceAfter = distance;
            closestChildAfterCenter = child;
        }
    }

    // 2 根据滑动的方向来返回的相应位置
    final boolean forwardDirection = isForwardFling(layoutManager, velocityX, velocityY);
    if (forwardDirection && closestChildAfterCenter != null) {
        return layoutManager.getPosition(closestChildAfterCenter);
    } else if (!forwardDirection && closestChildBeforeCenter != null) {
        return layoutManager.getPosition(closestChildBeforeCenter);
    }

    // 3 最后兜底计算
    View visibleView = forwardDirection ? closestChildBeforeCenter : closestChildAfterCenter;
    if (visibleView == null) {
        return RecyclerView.NO_POSITION;
    }
    int visiblePosition = layoutManager.getPosition(visibleView);
    int snapToPosition = visiblePosition
            + (isReverseLayout(layoutManager) == forwardDirection ? -1 : +1);

    if (snapToPosition < 0 || snapToPosition >= itemCount) {
        return RecyclerView.NO_POSITION;
    }
    return snapToPosition;
}

从上面看出为了拦截Filing事件,PagerSnapHelper中的findTargetSnapPosition()方法直接返回当前ItemView的上一个ItemView或者下一个ItemView的位置,只要不返回RecyclerView.NO_POSITION就不会有Fling效果! ——> 怎么阻止的Fling事件的触发呢?看看原因:(1)会调用父类(SnapHelper)里面的snapFromFling方法,只要findTargetSnapPosition方法返回不为RecyclerView.NO_POSITION,就会直接走startSmotthiScroll(),所以RecyclerView最终滑到的位置为当前位置的上一个或者下一个,不会产生Fling的效果。

接着分析findSnapView 

    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
        if (layoutManager.canScrollVertically()) {
            return findCenterView(layoutManager, getVerticalHelper(layoutManager));
        } else if (layoutManager.canScrollHorizontally()) {
            return findCenterView(layoutManager, getHorizontalHelper(layoutManager));
        }
        return null;
    }
    private View findCenterView(RecyclerView.LayoutManager layoutManager,
            OrientationHelper helper) {
        int childCount = layoutManager.getChildCount();
        if (childCount == 0) {
            return null;
        }

        View closestChild = null;
        final int center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
        int absClosest = Integer.MAX_VALUE;

        for (int i = 0; i < childCount; i++) {
            final View child = layoutManager.getChildAt(i);
            int childCenter = helper.getDecoratedStart(child)
                    + (helper.getDecoratedMeasurement(child) / 2);
            int absDistance = Math.abs(childCenter - center);
	//注释说了很清楚,滑动中间时相邻的那个最近,就确定距离屏幕中心最近的页面
            /* if child center is closer than previous closest, set it as closest  */
            if (absDistance < absClosest) {
                absClosest = absDistance;
                closestChild = child;
            }
        }
        return closestChild;
    }

1 当recyclerView滑动完之后,就会调用findSnapView这个方法,来确认最终位置的ItemView

2 在findSnapView内部,调用findCenterView方法,找到当前中心距离屏幕中心最近的ItemView

距离屏幕中心最近的页面。findCenterView方法的作用便是如此。

接着分析 snapToTargetExistingView 中注释2处 calculateDistanceToFinalSnap 方法

public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
        @NonNull View targetView) {
    int[] out = new int[2];
    if (layoutManager.canScrollHorizontally()) {
        out[0] = distanceToCenter(layoutManager, targetView,
                getHorizontalHelper(layoutManager));
    } else {
        out[0] = 0;
    }

    if (layoutManager.canScrollVertically()) {
        out[1] = distanceToCenter(layoutManager, targetView,
                getVerticalHelper(layoutManager));
    } else {
        out[1] = 0;
    }
    return out;
}
    private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
            @NonNull View targetView, OrientationHelper helper) {
        final int childCenter = helper.getDecoratedStart(targetView)
                + (helper.getDecoratedMeasurement(targetView) / 2);
        final int containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
        return childCenter - containerCenter;
    }

由上面的代码可知

calculateDistanceToFinalSnap()方法就是计算RecyclerView需要滑动的距离,主要通过distanceToCenter方法来计算,确定最终要展示的positon

PagerSnapHelper在最后总结 PagerSnapHelper可以实现页面切换的效果

(1)首先阻止RecyclerView的Fling事件,阻止的方式就是重写findTargetSnapPosition方法,当RecyclerView触发了Fling事件之后,直接滑动到下一个或者上一个。

(2)如果RecyclerView没有触发Fling事件,或者Fling阶段未能滑动到正确位置,此时需要findSnapView方法和calculateDistanceToFinalSnap来保证滑动到正确的页面。

2.ScrollEventAdapter分析

  //表示当前ViewPager2处于停止状态
  private static final int STATE_IDLE = 0;
  //表示当前ViewPager2处于手指拖动状态
  private static final int STATE_IN_PROGRESS_MANUAL_DRAG = 1;
  //表示当前ViewPager2处于缓慢滑动的状态。这个状态只在调用了ViewPager2的setCurrentItem方法才有可能出现。
  private static final int STATE_IN_PROGRESS_SMOOTH_SCROLL = 2;
  //表示当前ViewPager2处于迅速滑动的状态。这个状态只在调用了ViewPager2的setCurrentItem方法才有可能出现。
  private static final int STATE_IN_PROGRESS_IMMEDIATE_SCROLL = 3;
  //表示当前ViewPager2未使用手指滑动,而是通过FakerDrag实现的。
  private static final int STATE_IN_PROGRESS_FAKE_DRAG = 4;

ScrollEventAdapter实现了recyclerView的OnScrollListener事件,我们只需要看onScrollStateChanged()和onScolled方法

1.onScrollStateChanged()

public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
         // 1. 开始拖动 会调用startDrag方法表示拖动开始
        if ((mAdapterState != STATE_IN_PROGRESS_MANUAL_DRAG
               || mScrollState != SCROLL_STATE_DRAGGING)
               && newState == RecyclerView.SCROLL_STATE_DRAGGING) {
            startDrag(false);
            return;
        }
		
         // 2. 拖动手势的释放,此时ViewPager2会准备滑动到正确的位置
        if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_SETTLING) {
            // Only go through the settling phase if the drag actually moved the page
            if (mScrollHappened) {
                dispatchStateChanged(SCROLL_STATE_SETTLING);
                mDispatchSelected = true;
            }
            return;
        }
        
		//3.滑动结束 此时ScrollEventAdapter会调用相关的方法更新状态
        // Drag is finished (dragging || settling -> idle)
        if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_IDLE) {
            boolean dispatchIdle = false;
            updateScrollEventValues();
            if (!mScrollHappened) {
                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();
            }
        }
    }

2.onScrolled()

public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
    mScrollHappened = true;
    // 1 
    updateScrollEventValues();

    if (mDispatchSelected) {
        // 2 
        mDispatchSelected = false;
        boolean scrollingForward = dy > 0 || (dy == 0 && dx < 0 == isLayoutRTL());
        mTarget = scrollingForward && mScrollValues.mOffsetPx != 0
                ? mScrollValues.mPosition + 1 : mScrollValues.mPosition;
        if (mDragStartPosition != mTarget) {
            dispatchSelected(mTarget);
        }
    } else if (mAdapterState == STATE_IDLE) {
        // 调用了setAdapter方法
        dispatchSelected(mScrollValues.mPosition);
    }

    dispatchScrolled(mScrollValues.mPosition, mScrollValues.mOffset, mScrollValues.mOffsetPx);

    // 3
    if ((mScrollValues.mPosition == mTarget || mTarget == NO_POSITION)
            && mScrollValues.mOffsetPx == 0 && !(mScrollState == SCROLL_STATE_DRAGGING)) {
        dispatchStateChanged(SCROLL_STATE_IDLE);
        resetState();
    }
}

1 处新且计算当前position和offset的值

2 拖动手势释放,ViewPager2正在滑动到正确的位置

3 因为调用了setCurrentItem(x, false)不会触发IDLE状态的产生

onScrolled()方法中主要是调用updateScrollEventValues()方法更新状态ScrollEventValues里面3个变量,(2)调用相关dispatch...方法更新状态和分发状态下去

    private static final class ScrollEventValues {
        int mPosition;//从开始滑动到滑动结束,一直记录着当前滑动到的位置。
        float mOffset;//从一个页面滑动到另一个页面,记录着滑动的百分比。
        int mOffsetPx;//记录着从开始滑动的页面与当前状态的滑动。每次滑动结束之后,会被重置。
    }

ScrollEventAdapter总结:

(1)当调用ViewPager2的setAdapter方法时,此时应该回调一次dispatchSelected方法。
(2)当调用setCurrentItem(x, false)方法,不会调用onScrollStateChanged方法,于是不会产生idle状态,
   所以,咱们须要在onScrolled方法特殊处理(onScrolled方法会被调用)。
(3)正常的拖动和释放,就是onScrollStateChanged方法和onScrolled方法的正常回调。

3.PageTransformerAdapter

    void setPageTransformer(@Nullable PageTransformer transformer) {
       // TODO: add support for reverseDrawingOrder: b/112892792
       // TODO: add support for pageLayerType: b/112893074
       mPageTransformer = transformer; 
   }
   
    @Override
   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
       if (mPageTransformer == null) {
           return;
       }

       float transformOffset = -positionOffset;
       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()));
           }
           int currPos = mLayoutManager.getPosition(view);
           float viewOffset = transformOffset + (currPos - position);
           mPageTransformer.transformPage(view, viewOffset);
       }
   }

PageTransformerAdapter实现也非常简单,PageTransformerAdapter实现于OnPageChangeCallback接口,监听的是ScrollEventAdapter的页面滑动事件,然后将页面滑动事件转换成为上面特殊的事件

4.FragmentStateAdapter

    //(1)key为itemId,value为Fragment。表示position与所放Fragment的对应关系(itemId与position有对应关系)
    final LongSparseArray<Fragment> mFragments = new LongSparseArray<>();
    //(2)key为itemId,value为Fragment的状态
    private final LongSparseArray<Fragment.SavedState> mSavedStates = new LongSparseArray<>();
    //(3)key为itemId, value为ItemView的id。
    private final LongSparseArray<Integer> mItemIdToViewHolder = new LongSparseArray<>();
    private FragmentMaxLifecycleEnforcer mFragmentMaxLifecycleEnforcer;
    boolean mIsInGracePeriod = false;
    private boolean mHasStaleFragments = false;

1 onCreateViewHolder

public final class FragmentViewHolder extends ViewHolder {
    private FragmentViewHolder(@NonNull FrameLayout container) {
        super(container);
    }

    @NonNull static FragmentViewHolder create(@NonNull ViewGroup parent) {
        FrameLayout container = new FrameLayout(parent.getContext());
        container.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT));
        container.setId(ViewCompat.generateViewId());
        container.setSaveEnabled(false);
        return new FragmentViewHolder(container);
    }

    @NonNull FrameLayout getContainer() {
        return (FrameLayout) itemView;
    }
}

创建FrameLayout视图

2 onBindViewHolder()

    public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
        final long itemId = holder.getItemId();
        final int viewHolderId = holder.getContainer().getId();
        final Long boundItemId = itemForViewHolder(viewHolderId); 
        // 1 如果当前ItemView已经加载了Fragment,并且不是同一个Fragment
        // 那么就移除
        if (boundItemId != null && boundItemId != itemId) {
            removeFragment(boundItemId);
            mItemIdToViewHolder.remove(boundItemId);
        }

        mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
        // 2 保证对应位置的Fragment已经初始化,并且放在mFragments中-->确定相应的Fragment,
        ensureFragment(position);
        final FrameLayout container = holder.getContainer();
        // 3 特殊情况处理,当RecyclerView让ItemView保持在Window,但是不在视图树中。
        if (ViewCompat.isAttachedToWindow(container)) {
            if (container.getParent() != null) {
                throw new IllegalStateException("Design assumption violated.");
            }
            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);
                        placeFragmentInViewHolder(holder);
                    }
                }
            });
        }

        gcFragments();
    }
    
  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);
        }
    }

3 onViewAttachedToWindow (ViewHolder与Fragment进行绑定)

@Override
public final void onViewAttachedToWindow(@NonNull final FragmentViewHolder holder) {
   placeFragmentInViewHolder(holder);
   gcFragments();
}

调用placeFragmentInViewHolder方法加载Fragment

4.onViewRecycled (ViewHolder与Fragment解除绑定)

    @Override
    public final void onViewRecycled(@NonNull FragmentViewHolder holder) {
        final int viewHolderId = holder.getContainer().getId();
        final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
        if (boundItemId != null) {
            removeFragment(boundItemId);
            mItemIdToViewHolder.remove(boundItemId);
        }
    }

当ViewHolder被回收到回收池中,onViewRecycled方法会被调用 。

有在onViewDetachedFromWindow中去删除Fragment,而是在onViewRecycled中回收。因为当onViewRecycled方法被调用,表示当前ViewHolder已经彻底没有用了,被放入回收池,等待后面被复用,此时存在的情况可能有:(1).当前ItemView手动移除掉了;(2).当前位置对应的视图已经彻底不在屏幕中,被当前屏幕中某些位置复用了。所以在onViewRecycled方法里面移除Fragment比较合适 2.不在onViewDetachedFromWindow中删除的原因呢? 因为每当一个页面被滑走,都会调用这个方法,如果对其Fragment进行卸载,此时用户又滑回来,又要重新加载一次,这性能就下降了很多

5 .placeFragmentInViewHolder

 void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) {

        // ······
        // 1.Fragment未添加到ItemView中,但是View已经创建
        // 非法状态
        if (!fragment.isAdded() && view != null) {
            throw new IllegalStateException("Design assumption violated.");
        }

        // 2.Fragment添加到ItemView中,但是View未创建
        // 先等待View创建完成,然后将View添加到Container。
        if (fragment.isAdded() && view == null) {
            scheduleViewAttach(fragment, container);
            return;
        }

        // 3.Fragment添加到ItemView中,同时View已经创建完成并且添加到Container中
        // 需要保证View添加到正确的Container中。
        if (fragment.isAdded() && view.getParent() != null) {
            if (view.getParent() != container) {
                addViewToContainer(view, container);
            }
            return;
        }

        // 4.Fragment添加到ItemView中,同时View已经创建完成但是未添加到Container中
        // 需要将View添加到Container中。
        if (fragment.isAdded()) {
            addViewToContainer(view, container);
            return;
        }

        // 5.Fragment未创建,View未创建、未添加
        if (!shouldDelayFragmentTransactions()) {
            scheduleViewAttach(fragment, container);
            mFragmentManager.beginTransaction()
                    .add(fragment, "f" + holder.getItemId())
                    .setMaxLifecycle(fragment, STARTED)  //懒加载
                    .commitNow();
          mFragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(false);
        } else {
            // 调用了第5步,但是Fragment还未真正创建
            if (mFragmentManager.isDestroyed()) {
                return; // nothing we can do
            }
            mLifecycle.addObserver(new GenericLifecycleObserver() {
                @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);
                    }
                }
            });
        }
    }

最后总结

1 ViewPager2本身是一个ViewGroup,没有特殊作用,只是用来装一个RecyclerView。
2 PagerSnapHelper实现页面切换效果的原因是calculateDistanceToFinalSnap阻止RecyclerView的Fling事件,直接让它滑动相邻页面;findSnapView方法和findTargetSnapPosition用来辅助滑动到正确的位置。
3 ScrollEventAdapter的作用将RecyclerView的滑动事件转换成为ViewPager2的页面滑动事件。
4 PageTransformerAdapter的作用将普通的页面滑动事件转换为特殊事件。
5 FragmentStateAdapter完美实现了使用Adapter加载Fragment。在FragmentStateAdapter中,完美地考虑到ViewHolder的复用,Fragment加载和卸载。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ViewPagerViewPager2都是Android中的视图控件,用于实现滑动切换不同页面的功能。 ViewPager是Android SDK中的一个类,它可以在同一个Activity中展示多个Fragment,通过左右滑动来切换不同的Fragment。ViewPager可以实现无限循环滑动,但是它的性能不够好,存在一些问题,比如在嵌套使用时会出现滑动冲突等。 ViewPager2是ViewPager的升级版,它是在AndroidX库中的一个类,它解决了ViewPager存在的一些问题,比如滑动冲突、性能问题等。ViewPager2支持嵌套滑动,可以实现更加灵活的布局,同时还支持横向和纵向滑动。因此,ViewPager2是更加推荐使用的视图控件。 ### 回答2: ViewPagerViewPager2 都是 Android 平台上的视图容器,它们都用于实现左右滑动切换多个视图的效果。不过,它们也有一些不同的特点。 ViewPager 是 Android 系统自带的视图容器,它主要用于在同一个 Activity 中切换多个 Fragment。ViewPager 会将多个 Fragment 放置在同一个视图中,通过滑动切换 Fragment 来实现左右滑动的效果。ViewPager 比较易用、稳定,使用起来也比较简单,但是在一些功能上有一定的局限性。 ViewPager2 是 ViewPager 的升级版,它是在 AndroidX 中新增加的一个控件。相较于 ViewPagerViewPager2 有一些更加高级和灵活的功能。首先,ViewPager2 支持 RecyclerView.Adapter,这样用户可以通过 RecyclerView.Adapter 来实现 ViewPager2 中的数据管理,这大大提高了数据操作的灵活性。其次,ViewPager2 支持垂直滑动的效果,这使得用户可以通过上下滑动切换多个视图。此外,ViewPager2 还支持滑块(PageTransformer)和视图预加载(OffscreenPageLimit)等高级功能,让用户可以更加方便地自定义 ViewPager2 的效果和行为。 总的来说,如果只是想要简单实现左右滑动切换多个 Fragment 的效果,可以使用 ViewPager。如果需要更加高级、灵活的功能,或者需要在 ViewPager 中嵌套 RecyclerView 或其他视图控件,则可以选择 ViewPager2。同时,最好在使用 ViewPager2 时,将所有 Fragment 替换为 RecyclerView,这样能够充分利用 ViewPager2 的强大功能。 ### 回答3: ViewPagerViewPager2是Android平台上常用的 View容器 组件。它们最主要的作用是管理多个子view的滑动显示,类似于滑动的页面。 ViewPager从Android API Level 11就被引入,它支持从左往右滑动查看多个子视图,以轻松实现流畅的“屏幕滑动”效果,常见的使用场景包括相册、图库、图片轮播图等。在使用ViewPager时,开发者需要自己实现适配器,根据需要返回子View。且ViewPager中每个页面的宽度是相等的,无法进行自由的布局。 而ViewPager2是新增的一个组件,它是AndroidX中的一部分,于2019年发布。ViewPager2相对于ViewPager的最大改进就在于支持不同宽度的页面。除了滑动方向以外,ViewPager2还支持从RecyclerView中使用适配器,从而不仅仅可以使用View,还可以使用任何RecyclerView的特性和布局(如GridLayoutManager等)。另一个重要的改进是支持了多层嵌套,并且同步了更多的触摸事件,增强了原生的滑动手势支持。 总之,ViewPager2是ViewPager的升级版,它具有更多灵活的布局和更好的性能。开发者可以根据自己的需求选择使用ViewPagerViewPager2,相信在未来的Android开发中,ViewPager2会成为首选。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值