一個關於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
相關文章
- 關於UIInterfaceOrientation的一個bugUI
- Oracle關於nvl的一個BugOracle
- MySQL關於timestamp和mysqldump的一個“bug”MySql
- 一個關於臨時物件的BUG(下) (轉)物件
- 發現了一個關於 gin 1.3.0 框架的 bug框架
- [BUG反饋]兩個關於釋出文章的BUG
- 關於Oracle中重啟資料庫的一個bugOracle資料庫
- 關於'kksfbc child completion' wait的一個bugAI
- 踩到一個關於分散式鎖的非比尋常的BUG!分散式
- 關於RecyclerView.ItemDecoration的自定義View
- 舒服了,踩到一個關於分散式鎖的非比尋常的BUG!分散式
- 關於記憶體的一些bug (轉)記憶體
- [BUG反饋]關於設定選單的BUG
- 關於Sqlite的一個demoSQLite
- oracle的一個bugOracle
- 關於contenteditable的IE下的bug
- Kotlin 打造一個RecyclerView的通用Adapter(一)KotlinViewAPT
- 關於教程的一個問題
- 終於,幫開發寫了一個bug
- 拍案驚奇—vue-cli5專案關於中的一個快取詭異bug分析Vue快取
- 關於elementUI樹狀結構的bugUI
- 用RecyclerView做一個小清新的Gallery效果View
- 關於Integer面試的一個問題面試
- 關於range的一個知識點
- 關於一個公益專案的探究
- 聽同事關於一個ASM的故事ASM
- 一個關於Java Excel的問題JavaExcel
- 一個關於SessionBean呼叫的問題。SessionBean
- 一個關於prototype模式的問題?模式
- 一個關於支付的SQL-ProcedureSQL
- [BUG反饋]關於後臺無法釋出文章的BUG
- 一個線上全文索引BUG的排查:關於類阿拉件數字的分詞與檢索索引分詞
- 用RecyclerView打造一個輪播圖View
- Java的一個關於“星球”的列舉Java
- 關於 vs code 中文語言包的 bug
- 關於BUGZILLA的說明和安裝
- 一個排序引發的BUG排序
- Hive partition prune的一個BugHive