RecyclerView擴(kuò)展(一)ViewPager2使用與原理淺析

ViewPager2的使用

ViewPager2實(shí)際是內(nèi)部的RecyclerView在起作用,所以ViewPager2一定要設(shè)置RecyclerView.Adapter賦值,并且Adapter的ItemView的布局必須為match_parent。才能正常使用。ViewPager2+Fragment提供了一個新的適配器FragmentStateAdapter。

ViewPager2+Fragment代碼實(shí)例

FragmentStateAdapter實(shí)例

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初始化設(shè)置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
      // 頁面滑動監(jiān)聽
      viewPage?.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {  //監(jiān)聽事件
          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)
          }
      })
      // 設(shè)置轉(zhuǎn)場動畫
      val compositePageTransformer = CompositePageTransformer()
      compositePageTransformer.addTransformer(MarginPageTransformer(40))
      compositePageTransformer.addTransformer(ScaleTransformer())
      viewPage?.setPageTransformer(compositePageTransformer)

      viewPage?.beginFakeDrag() // 開始模擬滑動
      if (viewPage?.fakeDragBy(-500f) == true) { //模擬滑動
          viewPage?.endFakeDrag() //結(jié)束模擬滑動
       }

以上就是ViewPager2的使用和屬性設(shè)置,功能還是比較強(qiáng)大的,接下來看看Viewpager2的源碼進(jìn)行原理分析。

ViewPager2原理淺析

從ViewPager2的構(gòu)造函數(shù)開始分析:

  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,所以我們不用設(shè)置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());

      //創(chuàng)建滑動事件轉(zhuǎn)換器對象,通過監(jiān)聽RecyclerView的監(jiān)聽把狀態(tài)傳給ViewPager2
      mScrollEventAdapter = new ScrollEventAdapter(this);
      // 創(chuàng)建模擬拖動器對象,用于提供代碼模擬滑動
      mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);

      // 創(chuàng)建PagerSnapHelper對象,實(shí)現(xiàn)頁面切換居中且只滑動一頁的效果
      mPagerSnapHelper = new PagerSnapHelperImpl();
      mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
      // 設(shè)置RecyclerView的滑動監(jiān)聽
      mRecyclerView.addOnScrollListener(mScrollEventAdapter);
      //....
  }

  // 重新封裝了RecyclerView,處理攔截事件
  private class RecyclerViewImpl extends RecyclerView {
       // 設(shè)置用戶是否可以滑動控件
      @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 {
      // 設(shè)置緩存頁面
      @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,處理模擬拖動時的目標(biāo)View
  private class PagerSnapHelperImpl extends PagerSnapHelper {
       // 如果正在模擬拖動則不設(shè)置目標(biāo)View
      @Nullable
      @Override
      public View findSnapView(RecyclerView.LayoutManager layoutManager) {
          return isFakeDragging() ? null : super.findSnapView(layoutManager);
      }
  }

在ViewPager2的構(gòu)造方法中都會調(diào)用initialize方法進(jìn)行初始化,其中實(shí)例化了一個封裝的RecyclerView,并為這個RecyclerView設(shè)置了layoutManager、OnScrollListener監(jiān)聽、SnapHelper。

  • 封裝了RecyclerView,主要是onTouchEvent、onInterceptTouchEvent多加了isUserInputEnabled變量來判斷是否攔截事件,從而實(shí)現(xiàn)控制用戶是否可以滑動功能。
  • 封裝了LinearLayoutManager,主要是在calculateExtraLayoutSpace方法中根據(jù)OffscreenPageLimit擴(kuò)展可用的空間,從而實(shí)現(xiàn)頁面的緩存功能。
  • 封裝了PagerSnapHelper,主要是在findSnapView中判斷是否正在模擬滑動中,如果是則不設(shè)置目標(biāo)View。它的作用是使RecyclerView每次只能滑動一頁,而且ItemView會自動居中顯示。PagerSnapHelper在這篇不在分析,不熟悉可以看看之前寫的文章:RecycerView擴(kuò)展SnapHepler源碼分析
  • mScrollEventAdapter是RecyclerView滑動監(jiān)聽的對象,把RecyclerView的滑動監(jiān)聽狀態(tài)轉(zhuǎn)換成ViewPager2的OnPageChangeCallback的三種狀態(tài)。
  • mFakeDragger是實(shí)現(xiàn)模擬拖動器的對象。

ScrollEventAdapter

ScrollEventAdapter直譯意思是:滑動事件適配器。類如其名,它的作用就是將RecyclerView的滑動事件適配成ViewPager2的OnPageChangeCallback的事件

  public abstract static class OnPageChangeCallback {
      // 頁面正在滑動時調(diào)用
      public void onPageScrolled(int position, float positionOffset,
              @Px int positionOffsetPixels) {
      }

      // 頁面切換后調(diào)用
      public void onPageSelected(int position) {
      }

      // 頁面狀態(tài)發(fā)生變化后調(diào)用:開始滑動前、手勢離開后、滑動停止
      public void onPageScrollStateChanged(@ScrollState int state) {
      }
  }
final class ScrollEventAdapter extends RecyclerView.OnScrollListener {

  private OnPageChangeCallback mCallback;

  ScrollEventAdapter(@NonNull ViewPager2 viewPager) {
      // 根據(jù)ViewPager2拿到一些相關(guān)信息
      mViewPager = viewPager;
      mRecyclerView = mViewPager.mRecyclerView;
      //noinspection ConstantConditions
      mLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
      mScrollValues = new ScrollEventValues();
      resetState(); // 變量初始化
  }

  // 給ViewPager2設(shè)置
  void setOnPageChangeCallback(OnPageChangeCallback callback) {
      mCallback = callback;
  }

   // 將RecyclerView的滑動適配成 頁面滑動狀態(tài)變化
  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的滑動適配成 頁面切換完成狀態(tài)
  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滑動之前、手勢離開屏幕后、滑動結(jié)束后會調(diào)用。
  @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) {
          // 此方法隨后調(diào)用  dispatchStateChanged(SCROLL_STATE_DRAGGING)適配成Pager的事件
          startDrag(false);
          return;
      }

      // 手指離開后
      if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_SETTLING) {
          if (mScrollHappened) {
               // 調(diào)用dispatchStateChanged(SCROLL_STATE_SETTLING) 適配成Page事件
              dispatchStateChanged(SCROLL_STATE_SETTLING);
              mDispatchSelected = true;
          }
          return;
      }

      // 滑動結(jié)束后
      if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_IDLE) {
          boolean dispatchIdle = false;
          updateScrollEventValues();
          if (!mScrollHappened) {
              // 在拖動過程中頁面沒有移動,因此我們位于列表的開頭或結(jié)尾。或根本沒有頁面。 
              // 在第一種情況下,ViewPager的合同要求至少一個滾動事件。在第二種情況下,不要發(fā)送該滾動事件
              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) {
               // 滑動狀態(tài)改變事件
              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);
              }
               // 滑動狀態(tài)發(fā)生變化事件
              dispatchStateChanged(SCROLL_STATE_IDLE);
              resetState();
          }
      }
  }

    // recyclerView滑動時會調(diào)用
  @Override
  public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
      mScrollHappened = true;
      updateScrollEventValues();

      // 在手指滑動時離開屏幕時mDispatchSelected設(shè)置為true
      if (mDispatchSelected) {
           // 檢車是否滑動完成,調(diào)用dispatchSelected()方法適配調(diào)用
          // 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);
      }

      // 調(diào)用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的實(shí)例類。在監(jiān)聽RecyclerView的滑動狀態(tài)的兩個方法onScrollStateChanged、onScrolled中,根據(jù)不同的狀態(tài)調(diào)用dispatchStateChanged、dispatchSelected、dispatchScrolled設(shè)配成ViewPager2的頁面滑動接口OnPageChangeCallback。

PageTransformerAdapter

PageTransformerAdapterViewPager2.OnPageChangeCallback的實(shí)例類,作用是把OnPageChangeCallback.onPageScrolled的事件適配成PageTransformer.transformPage()事件。
先看看PageTransformerAdapter在ViewPager2被調(diào)用的地方:

  private void initialize(Context context, AttributeSet attrs) {
      // ....
      // 初始化PageTransformerAdapter,并把實(shí)例添加進(jìn)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;
      }
      // 將設(shè)置進(jìn)來的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的事件轉(zhuǎn)換成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的中心為原點(diǎn),左為負(fù),右為正,當(dāng)前View的中心點(diǎn)距離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,在頁面滑動的時候轉(zhuǎn)換成PageTransformer. transformPage()。其中的transformPage(@NonNull View page, float position)方法的參數(shù)值可以參考這篇文章PageTransformer詳解。ViewPager2如果想要實(shí)現(xiàn)頁面之間的轉(zhuǎn)換時動畫可以重寫PageTransformer

FragmentStateAdapter

FragmentStateAdapter是谷歌為我們實(shí)現(xiàn)適配加載FragmentRecyclerView.Adapter,如果我們想用ViewPager2加載Fragment頁面,就可以繼承FragmentStateAdapter作為適配器,只要重寫其構(gòu)造方法createFragmentgetItemCount

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幫我們實(shí)現(xiàn)了Adapter的onCreateViewHolder、onBindViewHolder抽象方法。在onCreateViewHolder方法中創(chuàng)建了一個以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) {
      // 創(chuàng)建一個以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,根據(jù)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的數(shù)據(jù)
          removeFragment(boundItemId);
          // 移除HashMap的數(shù)據(jù)
          mItemIdToViewHolder.remove(boundItemId);
      }

      // 重新設(shè)置HashMap
      mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
      ensureFragment(position);

      // 特殊情況,當(dāng)RecyclerView讓ItemView保持在Window,但是不在視圖樹中。
      final FrameLayout container = holder.getContainer();
      if (ViewCompat.isAttachedToWindow(container)) {
          if (container.getParent() != null) {
              throw new IllegalStateException("Design assumption violated.");
          }
           // 當(dāng)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的數(shù)據(jù)
  private void removeFragment(long itemId) {
      // 得到相應(yīng)位置的Fragment
      Fragment fragment = mFragments.get(itemId);
      // fragment為空,則不往下執(zhí)行
      if (fragment == null) {
          return;
      }

      // 將FrameLayout清空子View布局
      if (fragment.getView() != null) {
          ViewParent viewParent = fragment.getView().getParent();
          if (viewParent != null) {
              ((FrameLayout) viewParent).removeAllViews();
          }
      }

      // 移除舊位置的狀態(tài)
      if (!containsItem(itemId)) {
          mSavedStates.remove(itemId);
      }

      // fragment已經(jīng)沒有依附,則移除mFragments對應(yīng)的位置,并return
      if (!fragment.isAdded()) {
          mFragments.remove(itemId);
          return;
      }
       // 應(yīng)該延遲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的數(shù)據(jù)
  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創(chuàng)建一個以FrameLayout為父容器的ItemView。
在重寫的onBindViewHolder方法中,1. 先檢測ItemView是否有加載過Fragment,如果加載過的Fragment與現(xiàn)在的Fragment不相同,則對ItemView和三個HashMap進(jìn)行數(shù)據(jù)清空。2. 對兩個HashMap設(shè)置新的數(shù)據(jù)。 3. 如果存在特殊情況,則當(dāng)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();

      / *可能的狀態(tài):
      -fragment:{添加,未添加}
      -視圖:{已創(chuàng)建,未創(chuàng)建}
      -視圖:{已附加,未附加}
      組合:
      -{f:已添加,v:已創(chuàng)建,v:已附加}->檢查是否已附加到正確的容器
      -{f:已添加,v:已創(chuàng)建,v:沒附加}->將視圖附加到容器
      -{f:已添加,v:沒創(chuàng)建,v:已附加}->不可能
      -{f:已添加,v:沒創(chuàng)建 ,v:沒附加}->計劃創(chuàng)建時的調(diào)用
      -{f:沒添加,v:已創(chuàng)建,v:已附加}->非法狀態(tài)
      -{f:沒添加,v:已創(chuàng)建,v:沒附加}->非法狀態(tài)
      -{f:沒添加,v:沒創(chuàng)建,v:已附加}->不可能
      -{f:沒添加,v:沒創(chuàng)建,v:沒附加}->添加,創(chuàng)建,附加
      * /

      // { f:沒添加, v:已創(chuàng)建, v:attached } -> 非法狀態(tài)
      // { f:沒添加, v:已創(chuàng)建, v:notAttached } ->非法狀態(tài)
      if (!fragment.isAdded() && view != null) {
          throw new IllegalStateException("Design assumption violated.");
      }

      // {f:添加,v:沒創(chuàng)建,v:沒附加}->計劃創(chuàng)建時的回調(diào)
      if (fragment.isAdded() && view == null) {
          scheduleViewAttach(fragment, container);
          return;
      }

      //{f:添加,v:創(chuàng)建,v:附加}->檢查是否附加到正確的容器
      if (fragment.isAdded() && view.getParent() != null) {
          if (view.getParent() != container) {
              addViewToContainer(view, container);
          }
          return;
      }

      //{f:已添加,v:已創(chuàng)建,v:未附加}->將視圖附加到容器
      if (fragment.isAdded()) {
          addViewToContainer(view, container);
          return;
      }

      //{f:沒添加,v:沒創(chuàng)建,v:未附加}->添加,創(chuàng)建,附加
      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);
  }

  // 計劃創(chuàng)建時的回調(diào)
  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的狀態(tài)分別作出不同的操作。

關(guān)于ViewPager2的源碼淺析就講完了,下面總結(jié)一下:

總結(jié)

ScrollEventAdapter的作用:是將RecyclerView的滑動事件轉(zhuǎn)換成ViewPager2的OnPageChangeCallBack
PageTransformerAdapter的作用:是將RecyclerView的滑動事件轉(zhuǎn)換成ViewPager2的PageTransformer。
FragmentStateAdapter的作用:是為ViewPager2加載Fragment提供基礎(chǔ)的Adapter

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容