RecyclerView中有兩個Api來綁定數據:onBindViewHolder( RecyclerView.ViewHolder holder, int position)
和帶參數onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)
局部綁定/刷新數據。
場景:RecyclerView使用viewHolder保存數據時,同一個Item總是有兩個viewHolder對象。比如保存上一次的下載時間,百分比等
比如需要在viewHolder中保存上次刷新的數據時,同一個item有兩個viewHolder對象,肯定時不行的,這時只需要在mRecycler中設置setSupportsChangeAnimations()
爲false
即可。
原因: RecyclerView默認情況下會創建ViewHolder的另一個副本,以便將視圖相互淡化。
另外,當時用帶參數的 onBindViewHolder
時,因爲舊的ViewHolder
獲取payloads
,但新的ViewHolder
沒有獲取到payloads
,也需要配置如下代碼。
((SimpleItemAnimator)mRecycler.getItemAnimator()).setSupportsChangeAnimations(false);
RecyclerView源碼:ItemAnimator 決定使用同一個ViewHolder 還是重新創建ViewHolder,canReuseUpdatedViewHolder()默認返回爲true。若爲true,則使用同一個ViewHolder。
SimpleItemAnimator繼承RecyclerView,通過setSupportsChangeAnimations(),給全局變量mSupportsChangeAnimations (默認爲true)設置爲false;那麼canReuseUpdatedViewHolder()返回爲true。
DefaultItemAnimator繼承SimpleItemAnimator:canReuseUpdatedViewHolder()中調用super.canReuseUpdatedViewHolder(viewHolder, payloads);即SimpleItemAnimator中的canReuseUpdatedViewHolder()。
RecyclerView源碼:
/**
* When an item is changed, ItemAnimator can decide whether it wants to re-use
* the same ViewHolder for animations or RecyclerView should create a copy of the
* item and ItemAnimator will use both to run the animation (e.g. cross-fade).
* <p>
* Note that this method will only be called if the {@link ViewHolder} still has the same
* type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
* both {@link ViewHolder}s in the
* {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
*
* @param viewHolder The ViewHolder which represents the changed item's old content.
* @param payloads A non-null list of merged payloads that were sent with change
* notifications. Can be empty if the adapter is invalidated via
* {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
* payloads will be passed into
* {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
* method <b>if</b> this method returns <code>true</code>.
*
* @return True if RecyclerView should just rebind to the same ViewHolder or false if
* RecyclerView should create a new ViewHolder and pass this ViewHolder to the
* ItemAnimator to animate. Default implementation calls
* {@link #canReuseUpdatedViewHolder(ViewHolder)}.
*
* @see #canReuseUpdatedViewHolder(ViewHolder)
*/
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
@NonNull List<Object> payloads) {
return canReuseUpdatedViewHolder(viewHolder);
}
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
return true;
}
SimpleItemAnimator源碼:
boolean mSupportsChangeAnimations = true;
/**
* Returns whether this ItemAnimator supports animations of change events.
*
* @return true if change animations are supported, false otherwise
*/
@SuppressWarnings("unused")
public boolean getSupportsChangeAnimations() {
return mSupportsChangeAnimations;
}
/**
* Sets whether this ItemAnimator supports animations of item change events.
* If you set this property to false, actions on the data set which change the
* contents of items will not be animated. What those animations do is left
* up to the discretion of the ItemAnimator subclass, in its
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
* The value of this property is true by default.
*
* @param supportsChangeAnimations true if change animations are supported by
* this ItemAnimator, false otherwise. If the property is false,
* the ItemAnimator
* will not receive a call to
* {@link #animateChange(ViewHolder, ViewHolder, int, int, int,
* int)} when changes occur.
* @see Adapter#notifyItemChanged(int)
* @see Adapter#notifyItemRangeChanged(int, int)
*/
public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
mSupportsChangeAnimations = supportsChangeAnimations;
}
/**
* {@inheritDoc}
*
* @return True if change animations are not supported or the ViewHolder is invalid,
* false otherwise.
*
* @see #setSupportsChangeAnimations(boolean)
*/
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
return !mSupportsChangeAnimations || viewHolder.isInvalid();
}
DefaultItemAnimator源碼:
/**
* {@inheritDoc}
* <p>
* If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
* When this is the case:
* <ul>
* <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
* ViewHolder arguments will be the same instance.
* </li>
* <li>
* If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
* then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
* run a move animation instead.
* </li>
* </ul>
*/
@Override
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
@NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}