日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

RecyclerView 删除元素后,点击报 IndexOutOfBoundsException 解决方法

發(fā)布時(shí)間:2023/12/16 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RecyclerView 删除元素后,点击报 IndexOutOfBoundsException 解决方法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

今天使用 RecyclerView ,刪除某個(gè)元素后,再點(diǎn)擊后面的元素,會(huì)奔潰:

java.lang.IndexOutOfBoundsException: Invalid index 2, size is 2at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)at java.util.ArrayList.get(ArrayList.java:308)at com.yuntu.yaomaiche.common.adapters.BuyCarPlanLoadMoreAdapter.getItem(BuyCarPlanLoadMoreAdapter.java:111)at com.yuntu.yaomaiche.common.adapters.BuyCarPlanLoadMoreAdapter$1.onClick(BuyCarPlanLoadMoreAdapter.java:122)at android.view.View.performClick(View.java:4457)at android.view.View$PerformClick.run(View.java:18496)at android.os.Handler.handleCallback(Handler.java:733)at android.os.Handler.dispatchMessage(Handler.java:95)at android.os.Looper.loop(Looper.java:136)at android.app.ActivityThread.main(ActivityThread.java:5291)at java.lang.reflect.Method.invokeNative(Native Method)at java.lang.reflect.Method.invoke(Method.java:515)at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)at dalvik.system.NativeStart.main(Native Method)

看 log 的意思是數(shù)組越界,之前兩個(gè)元素,刪除一個(gè)后,第二個(gè)不應(yīng)該變成第一個(gè)嗎?怎么點(diǎn)擊時(shí)對(duì)應(yīng)的 position 還是 2 ?

點(diǎn)擊事件的注冊(cè)是在 RecyclerView 的 onBindViewHolder 中:

public void onBindViewHolder(BuyCarPlanItemViewHolder holder, int position) {//...holder.rootView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mClickListener != null) {mClickListener.onClick(v, getItem(position)); //這里使用 position 獲取數(shù)據(jù)}}});//...}

上面代碼在點(diǎn)擊事件 onClick() 中使用 onBindViewHolder() 方法中的參數(shù) position 來(lái)獲取數(shù)據(jù), Android Studio 中有個(gè)提示:

Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later less… (?F1)

RecyclerView will not call onBindViewHolder again when the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined.

For this reason, you should only use the position parameter while acquiring the related data item inside this method, and should not keep a copy of it.

If you need the position of an item later on (e.g. in a click listener), use getAdapterPosition() which will have the updated adapter position.

大概意思就是:

RecyclerView 中的數(shù)據(jù)有位置改變(比如刪除)時(shí)一般不會(huì)重新調(diào)用 onBindViewHolder() 方法,除非這個(gè)元素不可用。

也就是說(shuō) onBindViewHolder() 方法中的位置參數(shù) position 不是實(shí)時(shí)更新的,所以在我們刪除元素后,item 的 position 沒(méi)有改變。

為了實(shí)時(shí)獲取元素的位置,RecyclerView 為我們提供了 ViewHolder.getAdapterPosition() 方法。

當(dāng)我把上面奔潰的代碼中的 position 換成 holder.getAdapterPosition() 就解決了問(wèn)題。

ViewHolder 和 RecyclerView 的關(guān)系我們知道,就是存儲(chǔ)、復(fù)用指定位置對(duì)于的 ItemView。

RecyclerView 一般情況下不會(huì)處理任何 adapter 的更新,除非重新繪制界面。這導(dǎo)致有時(shí)候用戶想象中的和實(shí)際 RecyclerView 呈現(xiàn)的不一致。

下面看看 ViewHolder.getAdapterPosition 的源碼:

public final int getAdapterPosition() {if (mOwnerRecyclerView == null) {return NO_POSITION;}return mOwnerRecyclerView.getAdapterPositionFor(this);}

ViewHolder.getAdapterPosition 方法返回當(dāng)前 ViewHolder 在整個(gè) adapter 中的位置,實(shí)時(shí)更新,用來(lái)獲取數(shù)據(jù)比較靠譜。只有當(dāng)重新繪制、未繪制的時(shí)候會(huì)返回 -1,不過(guò)這只在繪制效率比較低的時(shí)候才會(huì)發(fā)生。

ViewHolder.getAdapterPosition 方法 調(diào)用了 RecyclerView.getAdapterPositionFor(this) :

private int getAdapterPositionFor(ViewHolder viewHolder) {if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)|| !viewHolder.isBound()) {return RecyclerView.NO_POSITION;}return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition); }

可以看到,第一個(gè) if 里就是處理當(dāng)前 ViewHolder 不可用的狀態(tài),正常情況下調(diào)用 mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition) :

public int applyPendingUpdatesToPosition(int position) {final int size = mPendingUpdates.size();for (int i = 0; i < size; i ++) {UpdateOp op = mPendingUpdates.get(i);switch (op.cmd) { //不同的更新邏輯,對(duì) position 進(jìn)行不同的修改case UpdateOp.ADD: // 增加了元素,就增加 positionif (op.positionStart <= position) {position += op.itemCount;}break;case UpdateOp.REMOVE: //刪除了元素就減少if (op.positionStart <= position) {final int end = op.positionStart + op.itemCount;if (end > position) {return RecyclerView.NO_POSITION;}position -= op.itemCount;}break;case UpdateOp.MOVE: // 移動(dòng)了元素后,針對(duì)當(dāng)前位置前后的元素,有不同的處理if (op.positionStart == position) {position = op.itemCount;//position end} else {if (op.positionStart < position) {position -= 1;}if (op.itemCount <= position) {position += 1;}}break;}}return position; }

可以看到在 applyPendingUpdatesToPosition() 方法里針對(duì)我們對(duì) RecyclerView Item 不同的操作,對(duì)元素的位置有了響應(yīng)的加減,保證拿到的是最準(zhǔn)確的位置。

總結(jié)

以上是生活随笔為你收集整理的RecyclerView 删除元素后,点击报 IndexOutOfBoundsException 解决方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。