RecyclerView:如何清除缓存/回收的视图?

34

我使用RecyclerView来展示一个列表视图,可以通过切换布局方式从列表布局切换到网格布局,但网格布局只显示所有数据中的一部分。当切换到网格布局时,会使用不同的布局XML文件进行呈现。

所有这一切都很好,除了当我滚动时,被回收(缓存)的列表布局视图混合在合适的网格布局视图项中。换句话说,我并不是为RecyclerView中的每一个项都使用layout_grid.xml文件,而是有些项会使用layout_list.xml布局以网格格式呈现。

这告诉我LayoutManager正在正确地工作,从列表布局切换到网格布局。但并不是所有的项视图都使用网格xml布局重新创建,而是使用了被回收的列表布局视图。

我尝试过RecyclerView.removeAllViews()RecyclerView.removeAllViewInLayout()RecyclerView.swapAdapter()(强制重新加载适配器),但都没有成功。

更新:

如果我将列表向下滚动两个位置,然后从列表切换到网格布局,前两个位置不会调用onCreateViewHolder(),直接调用onBindViewHolder(),因此不会强制使用网格布局xml。而是对这前两个位置的项进行了回收(我想),并以列表布局格式显示。


Recycler非常好用,但它有很多奇怪的小细节会导致数小时的挫败感。我很高兴你问了这个问题,因为它对我帮助很大。我点赞了你的问题和答案。谢谢大家! - FoxDonut
8个回答

29

使用

recyclerView.getRecycledViewPool().clear();

1
非常感谢您!我正在创建一个电话号码列表,并测试添加/删除项目。如果我在删除一个项目后添加了一个新项目,则已删除的项目将出现为新项目。我花了几个小时查看我的ArrayList,并尝试多种方法来解决这个问题 - 直到我发现数据被某种方式缓存了。 - FoxDonut

9

看起来被采纳的答案对你的情况是正确的,返回不同的viewType可能是你需要做的。

如果有人需要找到一种清除缓存视图持有者的方法并且最终进入了这个线程,这是我发现可行的。

你需要调用setLayoutManager(null)setAdapter(null)。 如果您想保留适配器,可以像这样做:adapter = getAdapter(); setAdapter(null); setAdapter(adapter);


2
在我的情况下,将 layoutmanager(null)setAdapter(null) 设置为 null 对我很有效。这让我免于抓狂! - Trevor

8

我有同样的问题。 Mann建议的解决方案很有效。另一种选择是将RecyclerView的RecycledViewPool设置为一个新实例。 这也对我有效。

recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());

但我认为覆盖getItemViewType()是更干净的解决方案。


7

好的,现在我明白了。你同时使用两种布局吗?有一种方法可以返回列表/网格元素(id / position)的视图类型。你需要正确地实现此方法,以便适配器可以为新视图循环使用正确的布局。

你明白我的意思吗? :)


是的,没错。否则,RecyclerView 就无法知道要回收哪种视图类型。 - Mann
好的,我明白你的意思。但是我在哪里设置视图类型呢?它似乎对于列表或网格布局都返回0。 - mraviator
啊...我明白了。手动劳动。:-) 我今晚会尝试一下(现在得走了)。感谢您指出这点给我。如果成功,我会在完成后标记答案。 - mraviator
没问题。如果你需要进一步帮助,请让我知道,我可以实现一些基础的东西让您更好理解。 - Mann
好的,这很奇怪,但请将其标记为答案。你知道声望的重要性,看起来我帮了你;)对于误解,我很抱歉。 - Mann
显示剩余4条评论

4

关键在于重写getItemViewType(int position)并设置它,以便我可以检查我正在处理的位置的视图类型(列表或网格),然后在onbindViewHolder()中设置该位置的适当视图处理。下面,viewFormat只是我在用户点击菜单按钮更改格式时设置为列表或网格的整数。

@Override
    public int getItemViewType(int position) {
        if (listData.size() == 0) {
            return EMPTY_VIEW;
        } else if (viewFormat == Constants.LIST_FORMATTED) {
            return Constants.LIST_FORMATTED;
        } else if (viewFormat == Constants.GRID_CONDITION) {
            return Constants.GRID_CONDITION;
        }
        return super.getItemViewType(position);
    }

一位在我的原始帖子上发表评论的用户提醒了我,但不幸的是那些评论已被编辑删除。我感谢这位用户指引我正确方向。


1
final int targetCacheSize = 2;
    recyclerView.setItemViewCacheSize(Integer.MIN_VALUE);
    recyclerView.getRecycledViewPool().clear();
    recyclerView.setItemViewCacheSize(targetCacheSize);

4
欢迎来到Stack Overflow。能否为您的代码添加一些解释,并说明它是如何回答这个问题的? - tshimkus

0

只需要这样做,非常简单!!

adapter.notifyItemRangeRemoved(0,adapter.getItemCount());

  adapter.notifyItemRangeRemoved(0,adapter.getItemCount());
                   for (int i = 0; i < array.length(); i++) {
                        JSONObject object = array.getJSONObject(i);
                         ////
                        data_List.add(data);
                    }

0

Recyclerview具有视图缓存和回收视图池(视图持有者池)。

缓存是视图(带有填充数据的视图)。当LayoutManager告诉recyclerview该视图不再可见且不需要时,recyclerview会检查该视图是否仍对特定位置有效,如果有效,则将其添加到缓存中,因此下一次LayoutManager请求该位置的视图时,它将从缓存中返回。

那么回收视图池是什么?当视图从缓存中移除或不再是有效视图时,与这些视图相关的数据被清除,只保留视图持有者对象在池中。根据itemType的不同,池中可以有不同类型的视图持有者。

现在,在您的情况下,您的视图对于该特定位置无效,因为您想显示完全不同的视图。因此,您的适配器应告诉recyclerview所有视图都无效。唯一的方法是使用notifyDataSetChanged来通知所有项。Recyclerview将从缓存中删除视图并将其添加到回收视图池中。因此,现在,当LayoutManager请求视图时,将返回回收的视图(根据itemType)或新创建的视图,并且适配器将绑定数据到这些视图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接