先看一下哪里来的stableId
stableId是Adapter中的一个成员变量,默认是false
public static abstract class Adapter<VH extends ViewHolder> { private final AdapterDataObservable mObservable = new AdapterDataObservable(); private boolean mHasStableIds = false;
针对这个变量,Adapter类中有两个方法
其一,hasStableIds()、
public final boolean hasStableIds() { return mHasStableIds; }
其二,setHasStableIds(boolean hasStableIds)
public void setHasStableIds(boolean hasStableIds) { if (hasObservers()) { throw new IllegalStateException("Cannot change whether this adapter has " + "stable IDs while the adapter has registered observers."); } mHasStableIds = hasStableIds; }
但是我们很惊讶的发现,RecyclerView中根本就没有任何一处调用了setHasStableIds(boolean hasStableIds)方法把这个成员变量设为true,所以这很需要我们值得深思了。
所以我们只能根据hasStableIds()方法来弄明白这个mHasStableIds变量的意义。
第一处:hasStableIds()为false,判断条件为||,不影响结果
boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged; mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null && (mDataSetHasChangedAfterLayout || animationTypeSupported || mLayout.mRequestedSimpleAnimations) && (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
第二处:hasStableIds()为false,mState.mFocusedItemId被初始化为NO_ID。
mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;追溯mState.mFocusedItemId,只有一处,此处因为NO_ID,无法进入if。追溯结束。
if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) { focusTarget = findViewHolderForItemId(mState.mFocusedItemId); }
第三处:由注释可知,这个方法返回一个独一无二的key,用于处理改变动画。可以说实在的,有没有stableId都一样
/** * Returns a unique key to be used while handling change animations. * It might be child's position or stable id depending on the adapter type. */ long getChangedHolderKey(ViewHolder holder) { return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition; }
对比这3处第二处是最有嫌疑的,只有当mState.mFocusedItemId 不被赋值为NO_ID且mAdapter.hasStableIds()是true,才进入这个if,非常符合我们想要追寻的结果。
找到使用这个方法的地方。
if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) { focusTarget = findViewHolderForItemId(mState.mFocusedItemId); } View viewToFocus = null; if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView) || !focusTarget.itemView.hasFocusable()) { if (mChildHelper.getChildCount() > 0) { // At this point, RV has focus and either of these conditions are true: // 1. There's no previously focused item either because RV received focused before // layout, or the previously focused item was removed, or RV doesn't have stable IDs // 2. Previous focus child is hidden, or 3. Previous focused child is no longer // focusable. In either of these cases, we make sure that RV still passes down the // focus to one of its focusable children using a best-effort algorithm. viewToFocus = findNextViewToFocus(); } } else { // looks like the focused item has been replaced with another view that represents the // same item in the adapter. Request focus on that. viewToFocus = focusTarget.itemView; } if (viewToFocus != null) { if (mState.mFocusedSubChildId != NO_ID) { View child = viewToFocus.findViewById(mState.mFocusedSubChildId); if (child != null && child.isFocusable()) { viewToFocus = child; } } viewToFocus.requestFocus(); }直接定位到最后一行。
public final boolean requestFocus() { return requestFocus(View.FOCUS_DOWN); }
由此我们得出结论,当你stableId设置为true的时候,等同于调用了viewholder中的view的requestFocus()方法。这个方法的作用是给这个请求requestFocus()的方法的view的下面的那个view焦点。
结合这篇文章,可以顺利使用stableId解决RecyclerView的notify方法使得图片加载时不闪烁。