Java 更新StableIdKeyProvider缓存和RecyclerView/SelectionTracker在删除项目后因新选择而崩溃
准备:Java 更新StableIdKeyProvider缓存和RecyclerView/SelectionTracker在删除项目后因新选择而崩溃,java,android,android-recyclerview,Java,Android,Android Recyclerview,准备: RecyclerView与RecyclerView.Adapter绑定到SQLiteCursor(通过ContentProvider和&Loader)RecyclerView和RecyclerView.Adapter链接到SelectionTrackeras。 SelectionTracker使用StableIdKeyProvider构建 在第一步中-删除项目: 长按选择RecyclerViews是一个项目(为SelectionTracker的SelectionObserver干杯),绘
RecyclerView
与RecyclerView.Adapter
绑定到SQLiteCursor
(通过ContentProvider
和&Loader)RecyclerView
和RecyclerView.Adapter
链接到SelectionTracker
as。
SelectionTracker
使用StableIdKeyProvider
构建
在第一步中-删除项目:
RecyclerViews
是一个项目(为SelectionTracker
的SelectionObserver
干杯),绘制操作栏上下文菜单,启动
在删除操作中,执行SQL删除任务更新游标加载程序
restartLoader
调用onLoadFinished
已激发,新的光标已获得,位于
RecyclerView.Adapter
methodnotifyDataSetChanged
已调用
RecyclerView.Adapter
redrawRecyclerView
内容,所有内容都是外观
好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。无位置
这就是为什么在-StableIdKeyProvider
的缓存包含旧的ID快照而不影响删除后,RecyclerView崩溃的原因
问题是——为什么?以及如何更新StableIdKeyProvider
的缓存
另一项说明:
当我阅读RecyclerView
代码时,我看到以下评论:
我不明白这句话的确切意思。也许我遇到了所描述的情况-notifydatasetchange
在不合适的时间调用?或者我需要打两次电话
附言。
很抱歉,关于文字描述,有很多复杂的代码我最终使用了
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();
}
}
新建ItemKeyProvider(ItemKeyProvider.SCOPE\u映射){
@凌驾
公共长getKey(int位置){
返回适配器.getItemId(位置);
}
@凌驾
public int getPosition(@NonNull长键){
RecyclerView.ViewHolder ViewHolder=recyclerList.findViewHolderForItemId(键);
return viewHolder==null?RecyclerView.NO_位置:viewHolder.getLayoutPosition();
}
}
崩溃消失,RecyclerView
的导航/选择/修改看起来正常。
那么,StableIdKeyProvider呢?。。嗯,可能它不是为处理
RecyclerView
的可变内容而设计的 我在StableIdKeyProvider中遇到了同样的问题。编写ItemKeyProvider的自定义实现似乎可以做到这一点。以下是一个基本的Kotlin实现,您可以在为RecyclerView构建选择跟踪器时使用:
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
}
}
类RecycleServiceWidKeyProvider(私有val RecycleView:RecycleView)
:ItemKeyProvider(ItemKeyProvider.SCOPE\u映射){
覆盖趣味getKey(位置:Int):长{
返回recyclerView.adapter?.getItemId(位置)
?:抛出IllegalStateException(“未设置RecyclerView适配器!”)
}
覆盖位置(键:长):Int{
val viewHolder=recyclerView.findViewHolderForItemId(键)
返回viewHolder?.layoutPosition?:RecyclerView.NO\u位置
}
}
我的问题通过在循环视图适配器中设置sethasstableId(true)
并重写getItemId
得到了解决,似乎跟踪器同时需要sethasstableId(true)
和在适配器中重写getItemId
在未重写getItemId>的情况下设置了稳定的Ids true后出现了这个错误
init {
setHasStableIds(true)
}
override fun getItemId(position: Int) = position.toLong()
override fun getItemViewType(position: Int) = position
遇到同样的问题,经过长时间的搜索,我找到了答案: 您只需要在Recycler Adapter视图中重写该方法
override fun getItemId(position: Int): Long = position.toLong()
在我的例子中,问题与
ViewHolder
中的ItemDetailsLookup.ItemDetails
的初始化有关。事实证明,getAdapterPosition()
可能在ViewHolder
绑定期间返回错误的位置。解决方案是在调用itemtailsLookup中的getAdapterPosition()
时调用getAdapterPosition()
,通过将初始化和selectiontracker设置到适配器的最末端,我解决了这个问题
capturedThumbnailListAdapter = new CapturedThumbnailListAdapter(this);
capturedThumbnailListAdapter.setCapturedThumbnailList(capturedThumbnailList);
viewBinding.capturedImagesRv.setLayoutManager(new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, true));
viewBinding.capturedImagesRv.setAdapter(capturedThumbnailListAdapter);
SelectionTracker<Long> tracker = new SelectionTracker.Builder<Long>("thumb_selection",
viewBinding.capturedImagesRv, new StableIdKeyProvider(viewBinding.capturedImagesRv),
new CapturedThumbnailListAdapter.ItemLookUp(viewBinding.capturedImagesRv),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build();
capturedThumbnailListAdapter.setSelectionTracker(tracker);
capturedThumbnailListAdapter=新的capturedThumbnailListAdapter(此);
CapturedThumbNailtListAdapter.setCapturedThumbNailtList(CapturedThumbNailtList);
viewBinding.capturedImagesRv.setLayoutManager(新的LinearLayoutManager(此,
LinearLayoutManager.HORIZONTAL,true));
viewBinding.capturedImagesRv.setAdapter(CapturedHumbnailListAdapter);
SelectionTracker tracker=新建SelectionTracker.Builder(“thumb_selection”,
viewBinding.capturedImagesRv,新的StableIdKeyProvider(viewBinding.capturedImagesRv),
新CapturedHumbnailListAdapter.ItemLookUp(viewBinding.capturedImagesRv),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build();
capturedThumbnailListAdapter.setSelectionTracker(跟踪器);
问题不在这里;言行一致
/** Constructor */
public SomeAdapter() {
this.setHasStableIds(true);
}
适配器确实必须@覆盖公共长getItemId()
:
这也发生在我使用SelectionTracker的RecyclerView上。此解决方案使用自定义ItemKeyProvider f替换Google的StableIdKeyProvider
/** Constructor */
public SomeAdapter() {
this.setHasStableIds(true);
}
@Override
public long getItemId(int position) {
if(this.mItems == null) {return -1L;}
return this.mItems.get(position).getId();
}