RecyclerView ViewHolder getAdapterPosition()返回-1(NO_POSITION)

RecyclerView 自带删除Item方法notifyItemRemoved(position);频繁点击删除时,突然抛出越界异常显示 position = -1 ;

java.lang.ArrayIndexOutOfBoundsException: length=10; index=-1
at java.util.ArrayList.get(ArrayList.java:310)

WTF???活见鬼!

查看getAdapterPosition()源码:

        public final int getAdapterPosition() {
            return this.mOwnerRecyclerView == null ? -1 : this.mOwnerRecyclerView.getAdapterPositionFor(this);
        }


    int getAdapterPositionFor(RecyclerView.ViewHolder viewHolder) {
        return !viewHolder.hasAnyOfTheFlags(524) && viewHolder.isBound() ? this.mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition) : -1;
    }

从getAdapterPositionFor(ViewHolder viewHolder)看不出什么信息,切换低版本源码查看:

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);
}

当ViewHolder处于FLAG_REMOVED 等状态时会返回NO_POSITION。
我们看一下官方文档解释:

Returns the Adapter position of the item represented by this ViewHolder.
Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet.
RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.
Note that if you’ve called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION.
The adapter position of the item if it still exists in the adapter. NO_POSITION if item has been removed from the adapter, notifyDataSetChanged() has been called after the last layout pass or the ViewHolder has already been recycled.

在这里插入图片描述
其中Note that if you’ve called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION。解释了返回-1的原因,大意是:notify未完成时,调用getAdapterPosition就会返回NO_POSITION(-1)。

我看网上有人说采用getLayouPosition()避免-1,然并卵。
二者何异?
在这里插入图片描述

When adapter contents change (and you call notify*) RecyclerView requests a new layout. From that moment, until layout system decides to calculate a new layout (<16 ms), the layout position and adapter position may not match because layout has not reflected adapter changes yet.

adapter和layout的位置会有时间差(<16ms), 如果你改变了Adapter的数据然后刷新视图, layout需要过一段时间才会更新视图, 在这段时间里面, 这两个方法返回的position会不一样。

另外stackoverflow上有答案还提到, 在notifyDataSetChanged之后并不能马上获取Adapter中的position, 要等布局结束之后才能获取到.而对于Layout的position, 在notifyItemInserted之后, Layout不能马上获取到新的position, 因为布局还没更新(需要<16ms的时间刷新视图), 所以只能获取到旧的, 但是Adapter中的position就可以马上获取到最新的position。

但是这都不能解决返回-1突发的crash,我们怎么做?

  • 方法一:在ViewHolder中持有数据源Bean。 通过bean在list中的位置来获取position
  • 方法二:在使用position地方判断 if(position<0) return;

官方文档 原文地址:https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder#getadapterposition

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值