美文网首页RecyclerView系列
RecyclerView的Adapter源码分析

RecyclerView的Adapter源码分析

作者: isLJli | 来源:发表于2020-12-08 11:01 被阅读0次

什么时候调用onCreatViewHolder、onBindViewHolder?

通过上一遍的RecyclerView的复用和回收机制文章,我们知道RecyclerView的拿取View是通过tryGetViewHolderForPositionByDeadline这个方法复用和创建ViewHolder。所以Adapter的onCreatViewHolder、onBindViewHolder的也是从此方法中执行生成ViewHolder。来看看方法

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
      boolean dryRun, long deadlineNs) {
//...前面四大缓存复用还是找不到holder
if (holder == null) {
  // 调用Adapter.createViewHolder方法
  holder = mAdapter.createViewHolder(RecyclerView.this, type);
  }

// 到这里一定有了ViewHolder,根据ViewHolder的状态判断是否执行BindViewHolder绑定数据
if (mState.isPreLayout() && holder.isBound()) {
  holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
  // tryBindViewHolderByDeadline 方法中执行Adapter.bindViewHolder
  bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}

}

// 最后调用mAdapter.bindViewHolder绑定数据
private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
      int position, long deadlineNs) {
   //...
  mAdapter.bindViewHolder(holder, offsetPosition);
  //...
  return true;
}

如果在四级缓存中都没有拿到holder,则调用AdaptercreateViewHolder去执行我们的抽象方法onCreateViewHolder创建ViewHolder。
然后根据ViewHolderFlags的状态(是否需要更新、是否已失效)决定是否调用tryBindViewHolderByDeadline,tryBindViewHolderByDeadline进而调用AdapterbindViewHolder方法。继续往下看AdaptercreateViewHolderbindViewHolder方法:

public abstract static class Adapter<VH extends ViewHolder> {
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
      try {
          TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
          // 调用onCreateViewHolder抽象方法
          final VH holder = onCreateViewHolder(parent, viewType);
          if (holder.itemView.getParent() != null) {
              throw new IllegalStateException("ViewHolder views must not be attached when"
                      + " created. Ensure that you are not passing 'true' to the attachToRoot
                      + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
          }
          // 给viewholder赋值viewtype
          holder.mItemViewType = viewType;
          return holder;
      } finally {
          TraceCompat.endSection();
      }
  }

  public final void bindViewHolder(@NonNull VH holder, int position) {
     boolean rootBind = holder.mBindingAdapter == null;
     // 首次创建时,设置position,itemId,Flags
     if (rootBind) {
         holder.mPosition = position;
         if (hasStableIds()) {
             holder.mItemId = getItemId(position);
         }
         holder.setFlags(ViewHolder.FLAG_BOUND,
                 ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
                         | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
         TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
     }
     holder.mBindingAdapter = this;
     //执行onBindViewHolder抽象方法
     onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
     if (rootBind) {
        // payload的清除
         holder.clearPayload();
         final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
         if (layoutParams instanceof RecyclerView.LayoutParams) {
             ((LayoutParams) layoutParams).mInsetsDirty = true;
         }
         TraceCompat.endSection();
     }
 }

 public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
 public abstract void onBindViewHolder(@NonNull VH holder, int position);

}

Adapter的源码可见,createViewHolder会先调用onCreateViewHolder创建一个ViewHolder,然后给这个ViewHolder设置ViewType。
bindViewHolder先判断ViewHolder是否有绑定Adapter,如果还没有绑定Adapter则先给ViewHolder设置positionItemIdFlags变量。如果已经绑定的Adapter,则更新绑定和调用onBindViewHolder绑定数据。
代码很简单容易看懂,所以AdapteronCreateViewHolderonBindViewHolder是在方法tryGetViewHolderForPositionByDeadline方法被调用的,onCreateViewHolder的调用时机是前面的四大缓存集合都没有找到可 复用的ViewHolder,所以调用onCreateViewHolder来创建一个新的ViewHolder。onBindViewHolder的调用时机是根据holderFlags的状态来判断是否执行。

为什么notifyDataSetChanged()不会执行动画,而notifyItemChanged(1)会执行动画?

1.为什么一调用Adapter的notify就会让RecyclerView执行跟新操作?
就是使用观察者模式通知RecyclerView去执行更新。Adapter做为被观察者AdapterDataObservableRecyclerView作为观察者RecyclerViewDataObserver。只要Adapter的notify系列方法被被调用,就会通知观察者做相应的操作。所以,当我们调用AdapternotifyDataSetChangednotifyItemChanged(1)其真正的操作实现方法是在观察者类RecyclerViewDataObserver实例的方法是如何执行,看看这个类的源码。

private class RecyclerViewDataObserver extends AdapterDataObserver {
  RecyclerViewDataObserver() {
  }
 
  // notifyDataSetChanged()执行的方法
  @Override
  public void onChanged() {
      assertNotInLayoutOrScroll(null);
      // 结构改变设置为true
      mState.mStructureChanged = true;
      // 将所有ViewHolder的Flags设置为无效的
      processDataSetCompletelyChanged(true);
      // 如果准备更新的集合mPendingUpdates的大小<=0时,执行requestLayout()
      if (!mAdapterHelper.hasPendingUpdates()) {
          requestLayout();
      }
  }

  // notifyItemChanged(1)执行的方法
  @Override
  public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
      assertNotInLayoutOrScroll(null);
      //增加一个更新状态的UpdateOp到准备更新集合mPendingUpdates中,如果mPendingUpdates集合只有一个,就执行triggerUpdateProcessor()方法。
      if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
          // 相当于开始绘制流程
          triggerUpdateProcessor();
      }
  }

}

通过上面代码的注释可以知道:

  • 调用notifyDataSetChanged()方法后,就是将所有的ViewHolderflags都标记为INVALID无效状态,这也是没有执行动画的原因。并且mPendingUpdates集合没有内容时就会执行requestLayout开始重绘。
  • 调用notifyItemChanged(1)方法后,先是往mPendingUpdates集合中加入一个跟新的UpdateOp对象,并返回一个布尔值,当mPendingUpdates的集合只有一个时,返回true。如果返回true则执行triggerUpdateProcessor方法,此方法也相当于开始绘制流程。

notifyDataSetChanged()方法不执行动画的原因是因为给ViewHolder设置为INVALID无效状态,那为什么设置了无效状态就不执行动画呢?这就要从绘制流程源码看起了。

到这里,接下来就是走requestLayout的绘制流程了。根据上篇文章RecyclerView的绘制三大流程知道在绘制流程的测量和布局中,分别会调用dispatchLayoutStep1dispatchLayoutStep2dispatchLayoutStep3三个方法,其作用分别为:

dispatchLayoutStep1:如果有动画,保存未变化之前的ViewHolder和ViewHolder的位置信息ItemHolderInfo
dispatchLayoutStep2:调用LayoutManger实例进行测量和布局
dispatchLayoutStep3:如果有动画,保存现在ViewHolder和ViewHolder的位置信息ItemHolderInfo。并执行动画。
来看看源码:

private void dispatchLayoutStep1() {
  if (mState.mRunSimpleAnimations) {
          // Step 0: Find out where all non-removed items are, pre-layout
      int count = mChildHelper.getChildCount();
      for (int i = 0; i < count; ++i) {
          // 遍历未改变前的ViewHolder
          final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
          //notifyDataSetChanged()已经设置了无效状态,所以会被continue,无法保存旧ViewHolder的信息和位置
          if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
              continue;
          }
          // 旧ViewHolder的位置信息
          final ItemHolderInfo animationInfo = mItemAnimator
                  .recordPreLayoutInformation(mState, holder,
                          ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
                          holder.getUnmodifiedPayloads());
          // 保存旧ViewHolder和位置信息
          mViewInfoStore.addToPreLayout(holder, animationInfo);
          if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
                  && !holder.shouldIgnore() && !holder.isInvalid()) {
              long key = getChangedHolderKey(holder);
              // 根据key,保存旧ViewHolder
              mViewInfoStore.addToOldChangeHolders(key, holder);
          }
      }
  }
}

private void dispatchLayoutStep2() {
  mAdapterHelper.consumeUpdatesInOnePass();
  // Step 2: Run layout
  mState.mInPreLayout = false;
  // fill->循环 layoutChunk()->tryGetViewHolderForPositionByDeadline()
  mLayout.onLayoutChildren(mRecycler, mState);
}

// 代码稍长,但逻辑不难
private void dispatchLayoutStep3() {
  if (mState.mRunSimpleAnimations) {
      // 遍历布局后的ViewHolder,
      for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
          ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
          if (holder.shouldIgnore()) {
              continue;
          }
          // 得到key
          long key = getChangedHolderKey(holder);
          // 现在ViewHolder的位置信息
          final ItemHolderInfo animationInfo = mItemAnimator
                  .recordPostLayoutInformation(mState, holder);
          //根据key得到旧ViewHolder
          ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
          if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
              // 如果有旧ViewHolder,执行下面代码
              // 判断新旧ViewHolder是否消失
              final boolean oldDisappearing = mViewInfoStore.isDisappearing(
                      oldChangeViewHolder);
              final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
             
              if (oldDisappearing && oldChangeViewHolder == holder) {
                  // 旧ViewHolder已经消失,并且新的holder跟旧的一样
                  // run disappear animation instead of change
                  // 保存新viewHolder和位置信息
                  mViewInfoStore.addToPostLayout(holder, animationInfo);
              } else {
                  // 得到旧ViewHolder的位置信息
                  final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
                          oldChangeViewHolder);
                  // we add and remove so that any post info is merged.
                   // 保存新viewHolder和位置信息
                  mViewInfoStore.addToPostLayout(holder, animationInfo);
                  // 新ViewHolder的位置信息
                  ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
                  
                  if (preInfo == null) {
                      handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
                  } else {
                      animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
                              oldDisappearing, newDisappearing);
                  }
              }
          } else {
              // 没有旧ViewHolder,就保存现在ViewHolder和其位置信息
              mViewInfoStore.addToPostLayout(holder, animationInfo);
          }
      }
       // 执行动画
      // Step 4: Process view info lists and trigger animations
      mViewInfoStore.process(mViewInfoProcessCallback);
  }
}

notifyDataSetChanged()的ViewHolder被设置了INVALID无效,所以在dispatchLayoutStep1方法中将不会保存旧ViewHolder和位置信息,而notifyDataSetChanged(1)则可以保存旧ViewHolder和位置信息
所以在dispatchLayoutStep3notifyDataSetChanged()将无法得到旧ViewHolder的位置信息,所以就执行保存了改变后ViewHolder和其位置信息。而动画是根据新旧相对位置的来执行的,所以notifyDataSetChanged()就不可以执行动画。我们继续追查mViewInfoStore.process执行动画的源码。

  //一个为ViewHolder为key,位置信息为value的HashMap
  final SimpleArrayMap<RecyclerView.ViewHolder, InfoRecord> mLayoutHolderMap = new SimpleArrayMap<>();

  void process(ProcessCallback callback) {
      for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
          final RecyclerView.ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
          final InfoRecord record = mLayoutHolderMap.removeAt(index);
          if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
              // Appeared then disappeared. Not useful for animations.
              callback.unused(viewHolder);
          } else if ((record.flags & FLAG_DISAPPEARED) != 0) {
              // Set as "disappeared" by the LayoutManager (addDisappearingView)
              if (record.preInfo == null) {
                  // similar to appear disappear but happened between different layout passes.
                  // this can happen when the layout manager is using auto-measure
                  callback.unused(viewHolder);
              } else {
                  callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
              }
          } else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {
              // Appeared in the layout but not in the adapter (e.g. entered the viewport)
              callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
          } else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
              // Persistent in both passes. Animate persistence
              callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
          } else if ((record.flags & FLAG_PRE) != 0) {
              // Was in pre-layout, never been added to post layout
              callback.processDisappeared(viewHolder, record.preInfo, null);
          } else if ((record.flags & FLAG_POST) != 0) {
              // Was not in pre-layout, been added to post layout
              callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
          } else if ((record.flags & FLAG_APPEAR) != 0) {
              // Scrap view. RecyclerView will handle removing/recycling this.
          } else if (DEBUG) {
              throw new IllegalStateException("record without any reasonable flag combination:/");
          }
          InfoRecord.recycle(record);
      }
  }

process(ProcessCallback callback)方法先遍历mLayoutHolderMap,mLayoutHolderMap是一个HashMap存储着新旧的ViewHolder和ViewHolder的位置信息,之后一个一个的根据状态执行参数接口实例方法。那就看看参数ProcessCallback实例。

  // 动画默认为DefaultItemAnimator
  ItemAnimator mItemAnimator = new DefaultItemAnimator();

  private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
          new ViewInfoStore.ProcessCallback() {
              @Override
              public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
                      @Nullable ItemHolderInfo postInfo) {
                  mRecycler.unscrapView(viewHolder);
                  // 此方法后续调用 mItemAnimator.animateDisappearance
                  animateDisappearance(viewHolder, info, postInfo);
              }

              @Override
              public void processAppeared(ViewHolder viewHolder,
                      ItemHolderInfo preInfo, ItemHolderInfo info) {
                  // 此方法后续调用 mItemAnimator.animateAppearance
                  animateAppearance(viewHolder, preInfo, info);
              }

              @Override
              public void processPersistent(ViewHolder viewHolder,
                      @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
                  viewHolder.setIsRecyclable(false);
                  if (mDataSetHasChangedAfterLayout) {
                      // 调用mItemAnimator.animateChange
                      if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
                              postInfo)) {
                         // 调用动画执行方法
                          postAnimationRunner();
                      }
                  } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
                     //调用了mItemAnimator.animatePersistence
                     // 调用动画执行方法
                      postAnimationRunner();
                  }
              }

              // 这个方法并没有调用动画,而是进行了ViewHolder回收
              @Override
              public void unused(ViewHolder viewHolder) {
                  mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
              }
          };

  void animateDisappearance(@NonNull ViewHolder holder,
          @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
      addAnimatingView(holder);
      holder.setIsRecyclable(false);
      if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
          // 调用动画执行方法
          postAnimationRunner();
      }
  }

  void animateAppearance(@NonNull ViewHolder itemHolder,
          @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
      itemHolder.setIsRecyclable(false);
      if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
          // 调用动画执行方法
          postAnimationRunner();
      }
  }

  void postAnimationRunner() {
      if (!mPostedAnimatorRunner && mIsAttached) {
          // 创建一个异步任务执行动画方法
          ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
          mPostedAnimatorRunner = true;
      }
  }

  private Runnable mItemAnimatorRunner = new Runnable() {
      @Override
      public void run() {
          if (mItemAnimator != null) {
              // 调用动画的执行方法
              mItemAnimator.runPendingAnimations();
          }
          mPostedAnimatorRunner = false;
      }
  };

上面代码逻辑也不难,就是每个方法相应的调用动画类ItemAnimatoranimateDisappearanceanimateAppearanceanimatePersistenceanimateChange抽象方法。最后执行postAnimationRunner()去创建一个异步任务去执行temAnimator.runPendingAnimations()动画。至于unused则没有调用ItemAnimator的方法,而是给了CacheView、RecylerViewPool回收ViewHolder。所以ProcessCallback的接口实例除unused()方法外,其他方法都是调用ItemAnimator的方法进行动画。而动画是根据新旧ViewHolder相关信息进行的,notifyDataSetChanged()没有旧ViewHolder自然没有参照物执行不了动画。

看看动画实例类DefaultItemAnimator的最终执行change的方法:

  @Override
  public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
          int fromX, int fromY, int toX, int toY) {
      if (oldHolder == newHolder) {
          // Don't know how to run change animations when the same view holder is re-used.
          // run a move animation to handle position changes.
          return animateMove(oldHolder, fromX, fromY, toX, toY);
      }
      final float prevTranslationX = oldHolder.itemView.getTranslationX();
      final float prevTranslationY = oldHolder.itemView.getTranslationY();
      final float prevAlpha = oldHolder.itemView.getAlpha();
      resetAnimation(oldHolder);
      int deltaX = (int) (toX - fromX - prevTranslationX);
      int deltaY = (int) (toY - fromY - prevTranslationY);
      // recover prev translation state after ending animation
      oldHolder.itemView.setTranslationX(prevTranslationX);
      oldHolder.itemView.setTranslationY(prevTranslationY);
      oldHolder.itemView.setAlpha(prevAlpha);
      if (newHolder != null) {
          // carry over translation values
          resetAnimation(newHolder);
          newHolder.itemView.setTranslationX(-deltaX);
          newHolder.itemView.setTranslationY(-deltaY);
          newHolder.itemView.setAlpha(0);
      }
      mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
      return true;
  }

这里的oldHolder == newHolder时,会调用animateMove的方法,这不就是调用notifyDataSetChanged执行的吗,所以我们继续看animateMove方法。

@Override
  public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
          int toX, int toY) {
      final View view = holder.itemView;
      fromX += (int) holder.itemView.getTranslationX();
      fromY += (int) holder.itemView.getTranslationY();
      resetAnimation(holder);
      int deltaX = toX - fromX;
      int deltaY = toY - fromY;
     // 新旧viewHolder一样,所以都等于0
      if (deltaX == 0 && deltaY == 0) {
          dispatchMoveFinished(holder);
          return false;
      }
      if (deltaX != 0) {
          view.setTranslationX(-deltaX);
      }
      if (deltaY != 0) {
          view.setTranslationY(-deltaY);
      }
      mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
      return true;
  }

我们的新旧ViewHolder都一样,所以deltaX、deltaY都为0,由代码可以看出执行了dispatchMoveFinishedreturn false,就是不往下执行动画。这就是notifyDataSetChanged不执行动画原因。

下一篇来分析RecyclerView的动画的实现类源码。

相关文章

网友评论

    本文标题:RecyclerView的Adapter源码分析

    本文链接:https://www.haomeiwen.com/subject/hxaewktx.html