android 未填充具有FirestoreRecyclerAdapter和LifecycleOwner的嵌套RecyclerView

8yparm6h  于 2023-04-28  发布在  Android
关注(0)|答案(2)|浏览(85)

我试图有一个嵌套的RecyclerView,其中水平RecyclerView将显示为垂直RecyclerView的一个项目。(UI看起来类似于Google Play商店)
由于我的数据集在FirebaseFirestore中,因此我使用FirestoreRecyclerAdapter来实现这一点。
My Fragment的代码(Parent RecyclerView存在于此处):

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    val view = inflater.inflate(R.layout.layout_recyclerview, container, false)
    val query = <some reference>
    val recycler = view.recyclerView
    recycler.setHasFixedSize(true)
    adapter = DashboardAdapter(this,
            FirestoreRecyclerOptions.Builder<Category>().categoryOption(query, this),
            R.layout.item_dashboard_row)
    recycler.adapter = adapter
    return view
}

DashboardAdapter代码段:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DashboardHolder {
    val item = LayoutInflater.from(parent.context)
            .inflate(layout, parent, false)
    return DashboardHolder(item)
}

DashboardHolder代码段:

internal class DashboardHolder(item: View) : RecyclerView.ViewHolder(item) {

private val rowTitle: TextView = item.rowTitle
private val rowRecycler: RecyclerView = item.rowRecycler

fun bind(category: Category, owner: LifecycleOwner) {
    rowTitle.text = category.name
    rowRecycler.setHasFixedSize(true)
    val query = <some query>
    val adapter = DashboardProductsAdapter(
            FirestoreRecyclerOptions.Builder<Product>()
                    .productOption(query, owner),
            R.layout.item_dashboard_product)
    rowRecycler.adapter = adapter
}
}

很明显,DashboardHolder(父视图持有者)中有RecyclerView。在绑定时,子适配器被创建并设置为子RecyclerView。
当我第一次加载Fragment时,一切都很好,加载正常。但是当我单击Home按钮并再次返回应用程序时,只有父RecyclerView被填充,而不是子RecyclerView。
在我开始深入挖掘之后,发现这是因为我在创建FirestoreRecyclerOptions时传递了LifecycleOwner。如果我不设置它并手动调用startListening()stopListening(),那么行为也是一样的。但是如果我不调用stopListening(),它就可以正常工作。
更新了Fragment的代码:

override fun onStart() {
    super.onStart()
    adapter.startListening()
}

override fun onStop() {
    super.onStop()
    // If I comment this out, everything works fine
    // But putting this in code doesn't populate the child RecyclerView 2nd time
    adapter.stopListening()
}

可能的问题是什么?我是否应该在bind()方法之外创建子适配器?我是否应该跳过stopListening()回调,但这可能会导致内存泄漏。

ldxq2e6h

ldxq2e6h1#

我认为有几件事可能会出错。一旦我们弄清楚了,我会更新这个答案。
首先,我想确定您是在onBindViewHolder()中调用bind(),对吗?
接下来,我很确定这并不重要,但是你可以像这样将你的init代码移动到onViewCreated()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
        inflater.inflate(R.layout.layout_recyclerview, container, false)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val query = <some reference>
    val recycler = view.recyclerView
    recycler.setHasFixedSize(true)
    adapter = DashboardAdapter(this,
            FirestoreRecyclerOptions.Builder<Category>().categoryOption(query, this),
            R.layout.item_dashboard_row)
    recycler.adapter = adapter
}

对于视图持有者来说,有一个核心问题需要解决,这个问题有点棘手。bind()将在onStart()onStop之间被调用无数次,这意味着你将留下大量的挂起适配器,因为当适配器被反弹时,stopListening()不会被调用。为了解决这个问题,你需要将适配器保存在一个属性中,并在bind()中每次清除它,如下所示:

private var currentAdapter: FirestoreRecyclerAdapter<...>? = null

fun bind(...) {
    currentAdapter?.let {
        it.stopListening() // Stop listening to database
        lifecycle.removeObserver(it) // Prevent automatic readdition of listeners
    }

    // Init adapter
    currentAdapter = ...
}

如果以上都不起作用,你需要做一些核心调试,一点一点地单步调试代码,直到你看到哪里出了问题。我建议添加以下断点:
1.适配器的startListening()方法,确保调用了addChangeEventListener()
1.适配器的onChildChanged()方法,确保使用正确的值调用该方法
1.遍历堆栈以查看内存,并确保所持有的引用是当前对象,而不是来自onStop()的ghost
1.一切都在正确的地方调用?可能不是FirebaseUI或架构组件的问题。顺便说一句,确保您使用的是最新的FUI版本3.1.0和AAC版本rc1

gr8qqesn

gr8qqesn2#

我刚刚在两个recyclerView对象上调用了setHasFixedSize(false),它对我很有效。

相关问题