android - 嵌套 RecyclerView 一次创建所有 ViewHolders

标签 android android-recyclerview android-viewholder nestedrecyclerview

我有一个带有嵌套 RecyclerViews 的相当复杂的列表。我知道嵌套的 RecyclerViews 不是最好的解决方案,但在我的情况下,它是创建结构化代码并满足要求的少数解决方案之一。我附上了结构的图像。你可以以电报为例来提高你对结构的理解。基本上我有一个带有项目 RV-1-Item 的外部 RecyclerView RV-1 和一个带有项目 RV-2-Item 的内部 RecyclerView RV-2。到目前为止一切顺利,我的问题是外部 RecyclerView 按预期回收 View ,但如果 R​​V-1-Items 之一进入 View ,则创建 RV-2 的所有 ViewHolders(这意味着有时会创建超过 100 个 ViewHolders )。总而言之,我的问题是如何强制内部 RecyclerView RV-2 也回收 ViewHolders。

RecyclerView Structure

我知道内部 RecyclerView RV-2 必须有很高的 wrap_content 因为它取决于内部项目的数量,我也无法设置 setHasFixedHeigth(true) (而且我不知道它是否有帮助)因为在运行时新的 RV-2 元素可以添加到 RV-2 中。我还尝试在 RV-2 上设置 setNestedScrollingEnabled(false),因为我在网上阅读了很多关于它的内容,但它也没有帮助我。

所以基本上这就是我的配置方式

房车-1

layoutManager = LinearLayoutManager(context)      
isNestedScrollingEnabled = false

房车-2
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context).apply {
    reverseLayout = true
}

除此之外,我还有一些 ItemDecorators 但它们只在项目之间创建空间,因此它们不必对问题做任何事情。

总结一下:
外部 RV-1 按预期回收 ViewHolders,但内部 RV-2 一次创建所有 ViewHolders,即使它们不在屏幕上。我假设是这种情况,因为 RV-2 的高度为 wrap_content,当需要测量 layout_height 时,它需要创建所有 View 。问题:有没有办法强制 RV-2 回收其 View ?

编辑:
此外,我在所有 RV-2 RecyclerViews 之间使用共享 RecycledViewPool 但这与问题并没有真正的关系,因为即使 ViewHolders 在 RecyclerViews 之间共享,RV-2 RecyclerView 也不应该创建不可见的 ViewHolders当它被初始化时。

编辑2:
很多评论和相关问题说两个垂直嵌套的 RecyclerViews 在 android 中是不可能的,以防这个问题的所有访问者都认为我的问题是:你将如何实现这样的结构。很明显,我可以制作一个包含 IM(圆形 ImageView )和 RV-2-Item 的 View ,并在不需要时让 IM 不可见。在我看来,这在某种程度上使结构更加复杂。此外还有一个要求,RV-1-Item 左侧的 IM 必须具有在 RV-1-Item 中上下移动的能力,这在我目前的结构下显然更容易。

编辑 3:(我的最后一个我保证)
我展示的问题可以通过使用我在编辑 2 中解释的方法来解决,即使它不是最好的解决方案。但问题是我有一个更复杂的屏幕,因为我有三个嵌套的 RecyclerViews,所以这个方法不再起作用。使用 EDIT 2 的方法,我可以将这个数字减少到两个,但我仍然会留下两个嵌套的 RecyclerView,我想不出一种解决方法可以解决剩余的两个嵌套 RecyclerView 的问题。我附上了一张更复杂的屏幕图像,其中包含带有标记部分的应用程序界面,以帮助您理解结构。

RecyclerView Structure 2

最佳答案

(在解决“如何不让 RecyclerView 一次创建所有项目”时,您的具体问题的答案并不完全,但最有可能通过不使用嵌套的 recyclerviews 来解决您的具体问题)

我建议(以与已经建议的非常相似的方式 in this answer )将您的提要扁平化到一个回收站 View 中
(无论您如何调整嵌套的recyclerview 架构,恕我直言,它永远不会比只有一个recyclerview 的性能好,而且由于您不需要嵌套滚动(我猜),因此只有一个recycler View 应该是您的最佳选择)。

我建议不要以您的数据结构方式来考虑您的提要,而是以您想要显示它的方式以及如何将其拆分为“看起来相似”/由相同事物组成的较小项目。

例如,从您的屏幕截图中,我会看到每个聊天项目的以下项目/ View 类型:

  • 聊天标题(带有图标和文本“新组”的事物)
  • 用户徽章(带有文字“Jürgen”的图片)
  • 一个消息项目(一个文本气泡,例如,在底部的屏幕截图中,实际上会有 3 个项目,每条消息一个)
  • 包含日期和操作/回复项目的部分。

  • 这些项目比整个聊天项目小得多,因此可以更快地创建/回收。
    对于这些项目中的每一个,创建一个 View 类型和一个 View 持有者,并将它们视为单独的回收者 View 项目。
    getItemViewType正确使用方法,为您需要的位置创建/准备正确的 View 类型。

    为此,适配器需要添加一些逻辑,因为您的数据很可能会被结构化为

    a list of chats, and each chat has a name and some messages to display



    我们需要它

    the first 6 elements are for the first chat, where the first position is the header, the second the user badge, the next 3 items are message items and then we need an action item.



    因此,您基本上需要计算显示每个聊天项目需要多少个 recyclerview 项目,这可能是这样的计算:

    1 个聊天标题项目 + 1 个用户徽章项目 + 3 个消息项目 + 1 个操作/回复项目 = 6

    需要对数据列表中的每个聊天项单独执行此计算。

    因此,如果您的数据列表中只有一个聊天项目要显示,您实际上需要告诉适配器创建 6 个项目(在本例中通过在 getItemViewCount() 处返回 6)。

    然后,您需要使用 getItemViewType(position: Int) 告诉适配器函数,在recyclerview的哪个位置,适配器需要准备哪种类型的view。
    因此,您再次需要一些逻辑来说明这一点,例如在位置 0,第一个聊天项目的聊天标题应该是,在位置 1,第一个聊天项目的用户徽章,在位置 2-4 的消息项目应该是,在位置 5 的操作项,在位置 6 的聊天标题第二次聊天应该是等等
    (同样,所有聊天项目的逻辑都需要到位,并且它可能会变得非常困惑/复杂,因为要计算每个聊天项目 View 类型的位置,例如,所有先前的聊天元素 View 计数也需要重新计算(为了知道您当前的聊天项目从哪个回收站 View 位置开始))。

    由于这往往会炸毁您的适配器,因此我建议(如果您还没有这样做的话)在其中获得一些管理器/委托(delegate)架构。
    所以例如每个 View 类型都有一个委托(delegate),以及一个计算每个聊天项目所需的 recyclerview 项目/ View 类型数量的管理器。

    仅供引用:

    前段时间我们遇到了和你类似的情况
    (一个设计类似于社交媒体提要的回收站 View ,它应该显示提要中的前 n 条评论,并且我们显示了每个提要项目(这是一个 recyclerview 项目)的评论以及该项目中的另一个 recyclerview)并且在一些我们无法解决的性能问题之后,我们只是扁平化了 recyclerview,并且再也没有遇到性能问题。

    关于android - 嵌套 RecyclerView 一次创建所有 ViewHolders,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57465014/

    相关文章:

    android - 如何防止recyclerview中的admob原生广告在向下/向上滚动时回收或重新加载

    android - onCreateViewHolder(ViewGroup parent, int viewType)

    android - 与 Android WebView 中的 HTML5 视频不一致

    java - Calendar.setTime 设置错误的日期

    java - 如何在android中使用recyclerView传递id而不是位置

    android - 使用 StaggeredGridLayoutManager 预加载 Android Recyclerview 项目

    java - 使用 View 持有者时,listView 中的行回收如何工作?

    java - Android - 使用 LinearLayout 制作 ScrollView

    android - 清除安卓缓存

    java - Fragment、Volley 和 RecyclerView JSON