RecyclerView系列ViewPager or RecyclerviewAndroid

RecyclerView扩展(一)ViewPager2使用与原理

2021-02-28  本文已影响0人  isLJli

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

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

PageTransformerAdapterViewPager2.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是谷歌为我们实现适配加载FragmentRecyclerView.Adapter,如果我们想用ViewPager2加载Fragment页面,就可以继承FragmentStateAdapter作为适配器,只要重写其构造方法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帮我们实现了Adapter的onCreateViewHolderonBindViewHolder抽象方法。在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

上一篇下一篇

猜你喜欢

热点阅读