草庐IT

java - 在删除项目后更新 StableIdKeyProvider 缓存和 RecyclerView/SelectionTracker 在新选择时崩溃

coder 2023-12-26 原文

准备:

RecyclerViewRecyclerView.Adapter 绑定(bind)到 SQLite Cursor(通过 ContentProvider && Loader)。 RecyclerViewRecyclerView.AdapterSelectionTracker 链接为 design suggests . SelectionTracker 使用 StableIdKeyProvider 构建。

第一步 - 删除一个项目:

  1. 长按选择 RecyclerViews 的项目(为 SelectionTrackerSelectionObserver 干杯),绘制操作栏上下文菜单,开火 删除 Action ,执行SQL删除任务。
  2. SQL删除结束后,用
    做Cursor Loader更新 restartLoader 调用。
  3. onLoadFinished 触发,获得新的 Cursor,on
    调用了 RecyclerView.Adapter 方法 notifyDataSetChanged
  4. 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.NO_POSITION

这就是为什么 RecyclerView 在 StableIdKeyProvider 的缓存包含 ID 的旧快照而没有删除影响之后崩溃。

问题是 - 为什么?以及如何更新 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}.

我不明白这句话的确切含义。也许我遇到了所描述的情况 - notifyDataSetChanged 在不合适的时间调用?或者我需要调用它两次?

附言。 文字描述不好意思,有很多复杂的代码

最佳答案

我结束了玩 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();
                    }
                }

Crash 没有了,RecyclerView 的导航/选择/修改看起来没问题。 StableIdKeyProvider 怎么样?...嗯,可能它不是为处理 RecyclerView 的可变内容而设计的。

更新 2021-12-03

上周我在 RecycleView 上进行了新一轮的斗争。 正如问题中提到的 - 确切的问题是 StableIdKeyProvider 的缓存。切换到 ItemKeyProvider 是解决方法。 正如 StableIdKeyProvider 的代码所解释的那样,chache 绑定(bind)到窗口的事件:附加和分离。所以,我在上面引用的评论 - 正是问题所在:当新的 Cursor 到达时 - 将 Cursor 重新附加到 Adapter并通知 - 需要在正确的时间开火。 “合适的时间”- 将此作业排入布局消息线程。通过这种方式,RecyclerView 和底层“工具箱”可以自行正确执行更新。为此,只需在 post runnable 方法中提供一个新的 Cursor 即可。代码:

@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
    recycler.post(new Runnable() {
        @Override
        public void run() {
            adapter.swapCursor(data);
        }
    });
...

关于java - 在删除项目后更新 StableIdKeyProvider 缓存和 RecyclerView/SelectionTracker 在新选择时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53523318/

有关java - 在删除项目后更新 StableIdKeyProvider 缓存和 RecyclerView/SelectionTracker 在新选择时崩溃的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  4. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  5. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  6. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  7. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  8. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

    我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

  9. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  10. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

随机推荐