准备工作:
具有RecyclerView
的 RecyclerView.Adapter
与SQLite Cursor
绑定(通过ContentProvider
&& Loader)。 RecyclerView
和RecyclerView.Adapter
与SelectionTracker
链接为design suggests。
SelectionTracker
与StableIdKeyProvider
构建在一起。
第一步-删除项目:
RecyclerViews
的项目(欢呼SelectionTracker
的{{1}}),绘制动作栏上下文菜单,然后启动
删除操作,执行SQL删除任务。SelectionObserver
通话。restartLoader
被解雇,新的onLoadFinished
在Cursor
方法RecyclerView.Adapter
被调用。notifyDataSetChanged
重绘了RecyclerView.Adapter
的内容,看起来很漂亮
好。第二步-选择其他项目。崩溃:
RecyclerView
删除项目进行中时,我在第一步中看到的内容。
java.lang.IllegalArgumentException
at androidx.core.util.Preconditions.checkArgument(Preconditions.java:38)
at androidx.recyclerview.selection.DefaultSelectionTracker.anchorRange(DefaultSelectionTracker.java:269)
at androidx.recyclerview.selection.MotionInputHandler.selectItem(MotionInputHandler.java:60)
at androidx.recyclerview.selection.TouchInputHandler.onLongPress(TouchInputHandler.java:132)
at androidx.recyclerview.selection.GestureRouter.onLongPress(GestureRouter.java:96)
at android.view.GestureDetector.dispatchLongPress(GestureDetector.java:779)
at android.view.GestureDetector.access$200(GestureDetector.java:40)
at android.view.GestureDetector$GestureHandler.handleMessage(GestureDetector.java:293)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
对StableIdKeyProvider
onDetached
项目执行内部工作时,看不到适配器中先前分配的ViewHolder
的位置:
ViewHolder
void onDetached(@NonNull View view) {
RecyclerView.ViewHolder holder = mRecyclerView.findContainingViewHolder(view);
int position = holder.getAdapterPosition();
long id = holder.getItemId();
if (position != RecyclerView.NO_POSITION && id != RecyclerView.NO_ID) {
是int position
这就是为什么RecyclerView在-RecyclerView.NO_POSITION
的缓存中包含ID的旧快照而没有删除影响后崩溃的原因。
问题是-为什么?以及如何更新StableIdKeyProvider
的缓存?
另一个说明:
当我阅读StableIdKeyProvider
代码时,看到了以下评论:
RecyclerView
我不明白这句话的确切含义。也许我遇到了所描述的情况- * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
* next layout pass, the return value of this method will be {#NO_POSITION}.
在不适当的时间打电话了?或者我需要打两次电话?
PS。 对不起,关于文学的描述,有很多复杂的代码
答案 0 :(得分:7)
我最终开始玩StableIdKeyProvider
并切换到我自己的ItemKeyProvider实现:
new ItemKeyProvider<Long>(ItemKeyProvider.SCOPE_MAPPED) {
@Override
public Long getKey(int position) {
return adapter.getItemId(position);
}
@Override
public int getPosition(@NonNull Long key) {
RecyclerView.ViewHolder viewHolder = recyclerList.findViewHolderForItemId(key);
return viewHolder == null ? RecyclerView.NO_POSITION : viewHolder.getLayoutPosition();
}
}
崩溃消失了,RecyclerView
的导航/选择/修改看起来还不错。
怎么样StableIdKeyProvider
?..嗯,也许不是为了处理RecyclerView
的可变内容而设计的。
答案 1 :(得分:0)
我在StableIdKeyProvider中遇到了相同的问题。编写ItemKeyProvider的自定义实现似乎可以解决问题。这是为RecyclerView构建选择跟踪器时可以使用的基本Kotlin实现:
class RecyclerViewIdKeyProvider(private val recyclerView: RecyclerView)
: ItemKeyProvider<Long>(ItemKeyProvider.SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return recyclerView.adapter?.getItemId(position)
?: throw IllegalStateException("RecyclerView adapter is not set!")
}
override fun getPosition(key: Long): Int {
val viewHolder = recyclerView.findViewHolderForItemId(key)
return viewHolder?.layoutPosition ?: RecyclerView.NO_POSITION
}
}
答案 2 :(得分:0)
我的问题已解决,方法是在“回收”视图适配器中设置setHasStableIds(true)
并覆盖getItemId
,看来Tracker在适配器中既需要setHasStableIds(true)
也需要覆盖getItemId
,但出现此错误设置稳定的Id为true后,不覆盖getItemId
init {
setHasStableIds(true)
}
override fun getItemId(position: Int) = position.toLong()
override fun getItemViewType(position: Int) = position
答案 3 :(得分:0)
遇到同样的问题,经过长时间的搜索,我找到了答案: 您只需要在“回收站适配器”视图中覆盖该方法即可。
override fun getItemId(position: Int): Long = position.toLong()
答案 4 :(得分:0)
就我而言,问题与ItemDetailsLookup.ItemDetails
中ViewHolder
的初始化有关。事实证明,getAdapterPosition()
在绑定ViewHolder
时可能返回错误的位置。解决方案是在getAdapterPosition()
中的getItemDetails()
调用时调用ItemDetailsLookup
。
答案 5 :(得分:0)
StableIdKeyProvider
不是问题;它做它所说的。
/** Constructor */
public SomeAdapter() {
this.setHasStableIds(true);
}
适配器确实必须@Override public long getItemId()
:
@Override
public long getItemId(int position) {
if(this.mItems == null) {return -1L;}
return this.mItems.get(position).getId();
}