浅探RecyclerView的removeItem操作

来源:互联网 发布:unity3d跑酷游戏素材 编辑:程序博客网 时间:2024/06/04 18:40

最近做一个页面,需要实现点击recyclerView里面的一个item,然后这个item就会被移除.
看文档发现ViewHolder这个类有一个getAdapterPosition()方法,看了说明也不是很懂,于是看了一下源码,做个小结.

现在假设recyclerView刚展示出来,没有进行过任何操作,那么当点击一个item时,在对应的viewHolder内调用getAdapterPosition(),返回的是正确的位置.这个getAdapterPosition()的源码会在后面讲到.
然后,调用recyclerViewAdapter的notifyItemRemoved方法,把位置作为参数.那么现在我们进去看看:

public final void notifyItemRemoved(int position) {            mObservable.notifyItemRangeRemoved(position, 1);        }

看到这个 mObservable,自然想到了观察者模式.
没错,这里就是通知所有注册了的observer.
其实这个mObservable是adapter的内部变量,而且在adapter构造的时候就已经构造好了.
然后当你给recyclerView设置adapter的时候,recyclerView就会把自己注册进去.那我们来看看recyclerView里面的onItemRangeRemoved方法:

public void onItemRangeRemoved(int positionStart, int itemCount) {        assertNotInLayoutOrScroll(null);        if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {                triggerUpdateProcessor();            }        }

itemCount是指要移除的数量,因为是移除一个,所以前面的代码的参数直接传入1
可以看到,里面用到三个方法:
第一个是判断recyclerView是否处于布局或阶段,不是正常状态会抛出异常,这个可以先忽略
现在来看第二个,深入之前先了解一下AdapterHelper:
AdapterHelper这个类不是RecyclerView的内部类,而是一个独立的类
我们知道,UI刷新间隔是16ms, 所以这个类里面有一个缓冲List,叫做mPendingUpdates.
这个List的类型是一个叫UpdateOp的类,这个类的对象里保存了positionStart,itemCount以及操作类型(包括ADD,REMOVE,MOVE,UPDATE)
那么这个onItemRangeRemoved方法会把需要移除的位置信息封装成UpdateOp对象,再放进缓冲List中,如果原来缓冲List中啥也没有,那么会返回true,说明要设置更新
现在,我们回到RecyclerViewDataObserver中,调用mAdapterHelper.onItemRangeRemoved方法会返回boolean, 如果为true,则会再进行一些判断,最后可能会调用view的requestLayout()方法

到此,notifyItemRemoved方法就走完了.总的来说,这个方法就是把需要移除的位置信息放入到缓冲队列中,UI更新时就会读取并清空缓冲队列.
可以看到,这个方法并没有对相应的viewHolder进行操作. 接下来我们进入getAdapterPosition()这个方法看看.

getAdapterPosition():
首先,ViewHolder有一个内部变量: RecyclerView mOwnerRecyclerView;
这个方法先判断mOwnerRecyclerView是否为null,如果为null,说明这个viewHolder已经脱离了RecyclerView,那当然会返回NO_POSITION. 如果不为null,还要进一步判断, 进入recyclerView的getAdapterPositionFor(ViewHolder viewHolder)方法.

getAdapterPositionFor(ViewHolder viewHolder):
这个方法先判断传入的viewHolder里面的一些flag,以此来判断这个viewHolder是否有效.如果无效,则返回NO_POSITION.
如果有效,还要再判断这个viewHolder是否在前面提到的缓冲队列中, 因此调用mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition) 参数为这个viewHolder的内部变量mPosition,这个方法会对position进行处理.
这个mPosition应该是在UI更新的时候才会被更新,所以这个值只代表viewHolder原来的位置.

applyPendingUpdatesToPosition(int position):
源码:

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) {                case UpdateOp.ADD:                    if (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:                    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;    }

说说我的理解吧,缓冲队列的每一个变化都可能会影响到传进来的position,所以要遍历每一个变化,对position进行处理.
以REMOVE为例, 在这个case里面,会判断,如果position在positionStart到positionStart + itemCount之间,就说明这个position要被删除,返回NO_POSITION; 如果在后面的,由于前面有item被删除,所以position应该前移,也就是减小itemCount; 如果在前面的,那就不需要变化.
其他也类似

到此,getAdapterPosition方法就走完了.
可以看出,这个方法会结合viewHolder原来的position,viewHolder是否有效以及变化缓冲队列,得出正确的position. 这也就是官方文档里所说的得出精确的adapter position

来到这里,我们探讨一下删除一个item的正确写法.这里的前提是,删除了item,其他item不需要重画
我的写法:

public void removeItem(int adapterPosition){    if (adapterPosition==RecyclerView.NO_POSITION)      return;    if (adapterPosition >= 0 && adapterPosition < mDatas.size()) {  //mDatas为数据集合                mDatas.remove(adapterPosition);        notifyItemRemoved(adapterPosition);         }  }

有不对的地方欢迎指出~~~

0 0
原创粉丝点击