如何在 Adapter 类的 RecyclerView 中提供子项的条件可见性?

Posted

技术标签:

【中文标题】如何在 Adapter 类的 RecyclerView 中提供子项的条件可见性?【英文标题】:How to provide conditional visibility of a child-item in a RecyclerView in the Adapter class? 【发布时间】:2021-07-05 09:31:51 【问题描述】:

我正在开发一个项目,该项目为每个屏幕实现 2 个视图、一个普通用户视图和一个管理员视图。与普通用户相比,管理员视图具有更多权限,例如从数据库中删除某些帖子或用户本身。

因此,如果 admin 权限为真(我在初始化适配器时将其作为参数值传递),我将这些功能按钮的可见性设置为 GONE )。但我正在努力解决的问题是,我在哪里设置可见性,在onCreateViewHolder 方法或onBindViewHolder 方法中?我现在已经在 onCreateViewHolder 方法中设置了它,因为我已经阅读了一些 *** 答案,只是我们应该避免在 onBindViewHolder 方法中进行繁重的操作。但我想知道一个明确的答案。

以下是代码示例供参考:

适配器类声明:

class NoticesAdapter(options: FirestoreRecyclerOptions<NoticeModel>,
                    private val isAdmin: Boolean,
                    private val listener: INoticeListAdapter):
        FirestoreRecyclerAdapter<NoticeModel, NoticesAdapter.NoticeViewHolder>(options)

onCreateViewHolder 方法:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoticeViewHolder 

        val noticeListView = LayoutInflater.from(parent.context).inflate(R.layout.item_notice, parent, false)
        val noticeListViewHolder = NoticeViewHolder(noticeListView)

        if (!isAdmin)
        
            noticeListViewHolder.deleteNoticeBtn.visibility = GONE
        

        // On clicking the delete button on a notice by the admin
        noticeListViewHolder.deleteNoticeBtn.setOnClickListener 
            val noticeSnapshot = snapshots.getSnapshot(noticeListViewHolder.adapterPosition)
            listener.deleteNoticeBtnListener(noticeSnapshot)
        

        return noticeListViewHolder
    

onBindViewHolder 方法:

override fun onBindViewHolder(holder: NoticeViewHolder, position: Int, model: NoticeModel) 
        holder.noticeText.text = model.noticeText
        holder.noticeAuthor.text = MyUtils.getUserName()
        holder.noticePostDate.text = model.datePosted
        holder.noticePostTime.text = model.timePosted

    

【问题讨论】:

对于同一个持有者实例,将调用多少次onCreateViewHolderonBindViewHolder? ...越低越好...所以“确定的答案”是: .... @Selvin onCreateViewHolder,对吧? onCreateViewHolder 在第一次创建回收器视图时调用一次,而 onBindViewHolder 在每次数据更改时调用。只是想确认一下。再次感谢 是的,你也可以使用不同的布局(没有这个按钮)并且不要在点击监听器上设置 【参考方案1】:

RecyclerView.Adapter 的作用是:回收物品(顾名思义)。该列表不会同时对数据源上的每个项目有一个视图。适配器确保在内存中有足够的视图,以便始终平滑地呈现列表。当一行通过滚动离开视野时,该视图将被回收以在下一个进入屏幕大小的视图中重新使用。

这意味着onCreateViewHolder 仅在需要创建视图时调用。通常在适配器启动时,当用户快速或不规律地滚动以及数据集发生变化和需要时。

每次需要更新行上的数据以更新视图时,都会调用另一个方法onBindViewHolder。每次有一行进入屏幕的视图字段时都会调用它。

所以教科书的答案是:在onBindViewHodlder 上执行,因为如果属性isAdmin 发生更改,则需要更新该行。通过在onCreateViewHolder 上执行此操作,只会在创建行时发生一次。

但是,您的 isAdmin 是构造函数上的一个 val,无法重新分配,因此这意味着当创建行时,按钮将永远隐藏或可见。这无关紧要,因为您的结构是要确定 admin 是否来自另一个与行数据结构派生的来源分开的来源。

如果在某些情况下你想:

使其在未来更加灵活和易于维护 或者您可能知道会有一个包含管理员而不是管理员行的列表

然后解决方案是将isAdming 属性移动到您的NoticeModel,这将意味着更改您的数据结构。

如果您想验证上述任何内容,请获取一个包含大量项目的数据源,然后添加 2 个日志,一个在 onCreateViewHolder 上,一个在 onBindViewHolder 上。你会看到 on create 只是有时被调用,而 on bind 总是被调用。

【讨论】:

另外为了解决问题中提到的问题,更改视图的可见性绝对不是“繁重”的操作。 因为如果属性 isAdmin 发生变化,那么该行将需要更新问题 如果管理员权限为真(我将其作为参数值传递时初始化适配器) 所以不,它不会改变每个项目 @Selvin But, your isAdmin is a val on the constructor that can not be reassigned 是的,也包括在If in some case you want to... 感谢您的回答!好吧,您猜对了,admin 参数不会针对给定用户动态更改。所以我会避免在 onBindViewHolder 中设置可见性,因为它会使整个操作变得昂贵。我已经拥有来自 HubModel 类的 isAdmin 属性,所以我已经在那里处理了。再次感谢您的详细见解! @HenryTwist 作为一般规则,任何适配器都不应该有繁重的操作,但是是的

以上是关于如何在 Adapter 类的 RecyclerView 中提供子项的条件可见性?的主要内容,如果未能解决你的问题,请参考以下文章

View Binding 是不是支持 Adapter 类的视图绑定

适配器模式(Adapter Pattern)

七适配器(Adapter)模式--结构模式(Structural Pattern)

Adapter

Adapter 适配器模式(设计模式03)

设计模式--适配器模式(Adapter)详解