RecyclerView中的嵌套RecyclerView不流畅

3

有关RecyclerView的一些主题,在RecyclerView内部但我看到大部分都不适合我的情况。我的情况是我有一个RecyclerView(垂直线性布局管理)显示一个CardView列表,每个Cardview包含一个内部RecyclerView(水平线性布局管理)。问题是滚动时的性能问题,根本不流畅。我注意到如果我注释掉内部RecyclerView的setAdapter,滚动会变得更加平滑,但我使CardView没有更新新的列表。代码类似于这样:

onBindViewHolder...{
    holder.innerRecycler.setAdapter(new InnerAdapter((data));
    // comment that line make the outer recyclerview smoothly but the CardView data not updated thanks to the view recycling.
}

我知道在一个可滚动的视图中放置另一个可滚动的视图并不是一个好主意,但我看不到其他选择。有人遇到过这种布局吗?谢谢。

更新(添加更多代码)。

// init outer recyclerview
mOuterRecyclerView = (RecyclerView) findViewById(...);
mOuterRecyclerView.setLayoutManagement(new LinearLayoutManagement(this));
mOuterRecyclerView.setHasFixedSize(true);
mOuterRecyclerView.setAdapter(new OuterAdapter(dataList));

// The adapter class for the outer one
onBindViewHolder(...){
    final dataItem = mItems.get(position);
    holder.innerRecycler.setAdapter(new InnerAdapter(dataItem.getList()));
}

// the holder for the outer
class MyHolder extends ViewHolder{
    RecyclerView innerRecycler;
    public MyHolder(View view){
        super(..);
        innerRecycler = findViewById(...);
    }
}
// the adapter for the inner
onBindViewHolder(...){
    final dataItem = mItems.get(pos);
    // async loading
    holder.tvTitle.setText(dataItem.getTitle);
}

布局很简单,所以我不在这里发布完整代码。 :)

1
是的,你是正确的,这是一种非常糟糕的做法,会降低性能并影响用户体验。我建议您将内部 RecyclerView 的内容移动到其他地方,可能是点击弹出窗口或新窗口中。 至于您的问题,如果没有看到您具体执行的操作,我们无法进行调试。 - Droidman
@Droidman。这就是我所想的,但我想看看是否有人能够以最佳性能实现这种布局。:) 无论如何,感谢您的建议。 - Paul
你需要发布更多的代码。 - ligi
@ligi,已根据要求添加。 - Paul
你能发布内部适配器吗? - ligi
@ligi:你有什么想法吗? :) - Paul
3个回答

7

@Paul 我有同样的需求,以下技巧可以解决。 将父RecyclerView放入NestedScrollView中,在onCreate方法中设置。

mRecyclerView.setHasFixedSize(true);
mRecyclerView.setNestedScrollingEnabled(false);

这可以防止父级RecyclerView滚动,使滚动变得非常平滑。

救命稻草:mRecyclerView.setHasFixedSize(true); 这个方法解决了问题。 - Abhishek Dubey
对我来说,把父RecyclerView放入NestedScrollView中解决了问题。谢谢。 - Michalsx
1
如果你把RecyclerView放在NestedScrollView里面,那么它的作用就没有发挥出来。你的RecyclerView将永远不会回收任何子项。 - Cyph3rCod3r

5
只要它们沿不同轴滚动,这种方法没有问题。您可以启用RecyclerView.startNestedScroll(int),并处理过度滚动等情况。此延迟是因为每次重新初始化适配器。您可以尝试一些不同的东西,例如维护适配器映射,并在bindVH中调用RecyclerView.swapAdapter(args...)
另一个好的步骤也可以使用公共池来回收视图,使用RecyclerView.setRecycledViewPool(args...)
我已创建并使用了100多个项目的列表,其中包含嵌套(不同轴)的recycler,并且没有遇到任何问题。
如果您提供更多代码(在其中编写了async loading),我可以更好地帮助您。但我建议您仔细阅读API和设计模式,这应该解决您的问题。

swapAdapter解决了问题。虽然动画效果不如预期的流畅,但我将其标记为答案。谢谢 :) - Paul
“流畅度”将取决于您的绑定方法如何。优化保持应该就可以解决问题。 - Droidekas
这很有道理。外部适配器的onBind方法非常快(始终为0ms),但内部的onBind方法则需要相当长的时间(1-5ms,有时甚至需要20ms)。我猜这就是外部滚动条出现问题的原因,但内部滚动条非常流畅。 - Paul
是的,这就是原因,你必须缩短那个时间。内部滚动条在所有项目都可用时初始化。对于外部滚动条,内部滚动条适用于即将可见的视图。在绑定方法完成之前,这些视图不会显示出来。 - Droidekas
1
我想我明白了。外部滚动条变得卡顿是因为它需要等待所有内部适配器(可见的适配器)完成其onBind方法(这显然需要时间)。内部滚动是平滑的,因为它只处理一个列表。如果这是真的,那么没有最好的方法,只能优化内部的onBind方法(这会通过算术方式影响外部)。 - Paul
显示剩余3条评论

0

由于有一些用户问我如何存档,我将发布我所做的内容。 @Droidekas提出的建议似乎是一个好点子,但我没有遵循。

我所做的是:

  1. 每个水平滚动视图都是垂直滚动视图的项目

  2. 当我需要为特定的垂直项设置数据(onBindViewHolder)时,我使用水平滚动适配器setNotifyDataSetChanged而不是设置全新的适配器或交换它。这样,我使得垂直滚动非常流畅。

垂直滚动适配器结构可能是:

Vertical Item view holder  -> Vertical Item[Name, List<Horizontal item>,...]
Vertical Item view holder  -> Vertical Item[Name, List<Horizontal item>,...]

在 OnBindViewHolder 中

holder.setData(VerticalItem)

在 Holder 中,我有一个名为 setData 的方法,看起来像这样:
setData(VerticalItem item){
    mItems = item.getHorizontalItems();
    mHorizontalAdapter.notifyDataSetChanged();
}

希望这有所帮助。 :D

我认为这里的关键点是:将LinearLayoutManager和适配器的初始化代码从onBindViewHolder移动到viewHolder中。 - Jeffrey Liu

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