一個關于recyclerView的bug

前些日子在github上遇到了一個有趣的問題,是在一個叫“xRecyclerView”的項目里的issue,一哥們說他給列表加兩個head就會報錯,而我則不會。隨后又有兩個哥們表示他們也有同樣的問題:

java.lang.IllegalArgumentException: called detach on an already detached child ViewHolder{11ae0c3d position=2 id=-1, oldPos=-1, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}...

于是我貼上了我的代碼:

 View view1 = LayoutInflater.from(activity).inflate(layoutId1, null);

 View view2 = LayoutInflater.from(activity).inflate(layoutId2, null);

 xRecyclerView.addHeaderView(view1);

 xRecyclerView.addHeaderView(view2);

而其中兩個哥們的錯誤代碼如下:

headerRecommend=LayoutInflater.from(getActivity()).inflate(R.layout.fragment_find_item_recommend, (ViewGroup) mView.findViewById(android.R.id.content), false);
headerRank=LayoutInflater.from(getActivity()).inflate(R.layout.fragment_find_item_rank, (ViewGroup) mView.findViewById(android.R.id.content), false);

rvTipsRecommendList.addHeaderView(headerRecommend);

rvTipsRecommendList.addHeaderView(headerRank);

headerview = new CustomizeHeaderLayout(this);

headerview1 = new CustomizeHeader1Layout(this);

headerview2 = new CustomizeHeader2Layout(this);

listview.addHeaderView(headerview);

listview.addHeaderView(headerview1);

listview.addHeaderView(headerview2);

public CustomizeHeaderLayout(Context context) {

   super(context);

   init();

}

private void init() {

   LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

   View view = inflater.inflate(R.layout.layout_customize_headview, null);
   ...
}

先看源碼,recyclerView和XRecyclerView的源碼。在recyclerView的源碼里拋出這個異常的地方是這樣判斷的:

public void detachViewFromParent(int offset) {

   if (vh != null) {

     if (vh.isTmpDetached() && !vh.shouldIgnore()) {     
        throw new IllegalArgumentException("called detach on an already"
                                + " detached child " + vh);
     }...
     vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);

   }
}

這個vh.isTmpDetached()和!vh.shouldIgnore()是一些c里面的二進制運算,先放在一邊。我們回過頭看log,可以看到vh對象打印出的東西有點不一樣,作者應該是重寫了toString,一找果然是,?
@Override public String toString() { final StringBuilder sb = new StringBuilder("ViewHolder{" + Integer.toHexString(hashCode()) + " position=" + mPosition + "id="+ mItemId + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition); if (isScrap()) { sb.append(" scrap ") .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]"); } if (isInvalid()) sb.append(" invalid"); if (!isBound()) sb.append(" unbound"); if (needsUpdate()) sb.append(" update"); if (isRemoved()) sb.append(" removed"); if (shouldIgnore()) sb.append(" ignored"); if (isTmpDetached()) sb.append(" tmpDetached"); if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")"); if (isAdapterPositionUnknown()) sb.append(" undefined adapter position"); if (itemView.getParent() == null) sb.append(" no parent"); sb.append("}"); return sb.toString(); }
??我的正常的log打出的是
ViewHolder{335f5b75 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} ViewHolder{1e435ef3 position=1 id=-1, oldPos=-1, pLpos:-1 no parent} ViewHolder{f598161 position=2 id=-1, oldPos=-1, pLpos:-1 no parent}
??對比可以得出結論是,我的在isScrap()和isTmpDetached()這兩個判斷是始終沒有走的,而他們兩個都走到這個判斷里面去了。那就是這個isScrap()和isTmpDetached()里有問題!再結合上文判斷異常部分的代碼,isTmpDetached()這個方法顯得詭異了許多。
??通過我的有道神器,知道了Detached是‘分離’的意思,那detachViewFromParent就可以理解為‘從父view中分離子view’吧,如果不報錯,還會addFlags(ViewHolder.FLAG_TMP_DETACHED),雖然我看不懂大神的二進制算法,但是現在我好像可以從字里行間里隱約感覺到是怎么回事了。那就是在調用了detachViewFromParent之后,使isTmpDetached()成立,又一次調用了這個方法,就報了這個錯誤!想明白的瞬間背上似乎有了層涼氣,在凌晨2點的夜里還是蠻嚇人的...但我還是不明白detachViewFromParent的調用機制,歸根結底我還是沒有明白這個bug出現的原因,只能明天再做調查了。
ps:有個挪威小哥也有同樣的問題:
https://github.com/martijnvdwoude/recycler-view-merge-adapter/issues/4
但我沒有看太懂
——2017-3-1 2:32

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 簡介: 提供一個讓有限的窗口變成一個大數據集的靈活視圖。 術語表: Adapter:RecyclerView的子類...
    酷泡泡閱讀 5,212評論 0 16
  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,868評論 0 23
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,915評論 18 139
  • 最近身邊的朋友很多精疲力乏的,有說心累的,有說不開森的,有說生活沒意思的,上班無趣的,更多是無聊的。 大多數...
    愛上此時閱讀 284評論 0 0
  • \
    LOL佛教閱讀 184評論 0 0