如何在RecyclerView中实现通知列表UI展示及Adapter数据绑定
问题根源
你遇到的报错核心原因如下:
item变量不存在:是因为onBindViewHolder中没有根据索引从数据源中取出对应条目,直接调用了未定义的item变量- 类型不匹配:你的Adapter当前持有
NotificationItemsResponse.NotificationItemData(Room实体类)类型的数据源,但ViewHolder的bind方法接收的是NotificationItemUIModel类型参数,两个类型不统一导致报错 - 无效代码问题:你在
onCreateViewHolder中return之后调用setListData,这行代码永远不会执行,属于无效逻辑
完整修改步骤
1. 新增Room实体转UIModel的转换方法
因为两个数据类字段不匹配,先写扩展方法做类型转换,缺省的UIModel字段你根据自身业务逻辑补全即可:
fun NotificationItemsResponse.NotificationItemData.toUIModel(): NotificationItemUIModel { return NotificationItemUIModel( id = this.id, leadFullName = this.name, information = "", // 按需补业务字段值 product = this.product, simpleText = "", // 按需补业务字段值 data = this.data, time = this.time ) }
2. 重构NotificationAdapter逻辑
统一用UIModel作为数据源,同时启用DiffUtil做局部刷新替换全局notifyDataSetChanged提升性能:
class NotificationAdapter( // 点击事件通过构造函数传入 private val onItemClicked: (Int) -> Unit, diffCallback: DiffUtil.ItemCallback<NotificationItemUIModel> = NotificationUIItemComparator ) : RecyclerView.Adapter<NotificationAdapter.NotificationItemViewHolder>() { // 数据源改为UIModel类型,重写set方法自动计算Diff刷新 var items = emptyList<NotificationItemUIModel>() set(value) { val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { override fun getOldListSize() = field.size override fun getNewListSize() = value.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = field[oldItemPosition].id == value[newItemPosition].id override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = field[oldItemPosition] == value[newItemPosition] }) field = value diffResult.dispatchUpdatesTo(this) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationItemViewHolder { val inflater = LayoutInflater.from(parent.context) return LayoutListItemNotificationBinding.inflate(inflater, parent, false) .let(::NotificationItemViewHolder) } override fun onBindViewHolder(holder: NotificationItemViewHolder, position: Int) { val currentItem = items[position] holder.bind(currentItem) holder.itemView.setOnClickListener { onItemClicked(currentItem.id) } } class NotificationItemViewHolder(private val layout: LayoutListItemNotificationBinding) : RecyclerView.ViewHolder(layout.root) { fun bind(item: NotificationItemUIModel) = with(layout) { // 注意字段对应关系,tvName对应UIModel的leadFullName字段 tvName.text = item.leadFullName tvProductNotification.text = item.product tvSimpleText.text = item.simpleText tvInformation.text = item.information tvTime.text = item.time tvData.text = item.data } } override fun getItemCount(): Int = items.size object NotificationUIItemComparator : DiffUtil.ItemCallback<NotificationItemUIModel>() { override fun areItemsTheSame( oldItem: NotificationItemUIModel, newItem: NotificationItemUIModel ) = oldItem.id == newItem.id override fun areContentsTheSame( oldItem: NotificationItemUIModel, newItem: NotificationItemUIModel ) = oldItem == newItem } }
3. Fragment中绑定数据并渲染UI
在Flow收集回调中做类型转换,直接赋值给Adapter的items即可自动刷新列表:
// Fragment全局变量初始化Adapter,传入点击事件回调 private val notificationAdapter = NotificationAdapter( onItemClicked = { itemId -> // 此处写点击item后的业务逻辑,比如跳转详情页 } ) // 在onCreateView中给RecyclerView设置Adapter binding.recyclerView.apply { adapter = notificationAdapter layoutManager = LinearLayoutManager(requireContext()) } // 收集ViewModel的StateFlow数据更新UI vm.test.collectWith(viewLifecycleOwner) { roomDataList -> Log.d("TEST", "test message $roomDataList") // 把Room实体列表批量转成UIModel列表 val uiModelList = roomDataList.map { it.toUIModel() } // 更新Adapter数据源自动刷新列表 notificationAdapter.items = uiModelList }
注意事项
NotificationItemUIModel中information、simpleText等Room实体没有的字段,你根据自身业务需求在转换方法中补全对应值即可- 原有
setListData方法已经不需要,直接给adapter.items赋值就会自动做差分刷新,性能远高于notifyDataSetChanged
内容的提问来源于stack exchange,提问作者chrisu.chrisu
相关产品推荐
相关产品推荐

