You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView多Item类型显示异常:预期展示5行仅出现4行问题排查

问题描述

尝试在RecyclerView中实现多视图类型展示,预期结构为1个头部+5条数据行,再接1个头部+5条数据行,实际运行仅显示4条数据行。

日志输出

日志输出截图

核心代码

Adapter代码

class DailyFragAdapter(
    private val activityData: (DetailActivityData) -> Unit,
    private val dates: List<Date>,
    private val list : ArrayList<TabType1>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>()
/*
    ListAdapter<TabType1, RecyclerView.ViewHolder>(Diff())*/ {

    private lateinit var context: Context
    private val HEADER = 1
    private val ROW = 2

    /*private class Diff : DiffUtil.ItemCallback<TabType1>() {
        override fun areItemsTheSame(oldItem: TabType1, newItem: TabType1): Boolean {
            return oldItem.header == newItem.header && oldItem.cps.cps[0].id == newItem.cps.cps[0].id
        }

        override fun areContentsTheSame(
            oldItem: TabType1,
            newItem: TabType1
        ): Boolean {
            return oldItem.hashCode() == newItem.hashCode()
        }

    }*/

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        context = parent.context
        val layoutInflater = LayoutInflater.from(context)
        return if (viewType == 2) {
            DVH(DailyFragRecyclerItemBinding.inflate(layoutInflater, parent, false))
        } else {
            TitleHolder(DailyTableHeaderBinding.inflate(layoutInflater, parent, false))
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val data: TabType1 = list[position]
        if (holder is DVH) {
            data.cps.cps.forEach {
                Timber.i("is: a row")
                holder.bind(it)
            }

        }else{
            Timber.i("is: header")
            holder as TitleHolder
            holder.bind(data.header)
        }
    }


    override fun getItemViewType(position: Int): Int {
        val tabType1 = list[position]
        return if (tabType1.header.title == "") {
            ROW
        } else {
            HEADER
        }
    }

    inner class TitleHolder(binding: DailyTableHeaderBinding) :
        RecyclerView.ViewHolder(binding.root) {
        private val groupHeader: TextView = binding.title
        fun bind(title: Header) {
            groupHeader.text = title.title
        }
    }


    inner class DVH(binding: DailyFragRecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
        private var cpname: TextView = binding.cpName
        private var day1: FrameLayout = binding.frameLayout
        private var day2: FrameLayout = binding.frameLayout2
        private var day3: FrameLayout = binding.frameLayout3
        private var day4: FrameLayout = binding.frameLayout4
        private var dayContainer: ArrayList<FrameLayout> = ArrayList()

        fun bind(cpVo: CheckingPointVo) {

            dayContainer.addAll(arrayListOf(day1, day2, day3, day4))
            cpname.apply {
                text = cpVo.name
                setOnClickListener {
                    activityData.invoke(DetailActivityData(cpVo.id, cpVo.pcID))
                }
            }
            val map = cpVo.chartEntriesMap
            for (i in dates.indices) {

                val dateID = BkpDateUtil.getDateId(dates[i])
                Timber.i("dateID $dateID")
                var ceVo: List<ChartEntryVo>? = map[dateID]
                if (ceVo == null) {
                    ceVo = ArrayList<ChartEntryVo>()
                    ceVo.add(ChartEntryVo(0, dateID, 0L, 0L, "", false))
                }
                val entryValue = ceVo[0].value
                when (cpVo.cpType) {
                    CheckingPointType.YES_NO -> {
                        val fl: FrameLayout = dayContainer[i]
                        fl.setOnClickListener {
                            openYesNoDialog(cpVo, ceVo[0])
                        }
                        if (entryValue == 1L) {
                            setIconInChartEntry(fl, R.drawable.ic_tick, cpVo)
                        } else {
                            setIconInChartEntry(fl, R.drawable.ic_no_entry, cpVo)
                        }
                    }
                    CheckingPointType.QUANTIFIABLE -> {

                    }
                    CheckingPointType.COUNTABLE -> {

                    }
                    CheckingPointType.CATEGORY -> {

                    }
                    CheckingPointType.TEXT -> {

                    }
                }
            }

            dayContainer.clear()


        }

    }

    private fun setIconInChartEntry(fl: FrameLayout, resID: Int, cpVo: CheckingPointVo) {
        fl.getChildAt(0).visibility = GONE
        val image: ImageView = fl.getChildAt(1) as ImageView
        image.visibility = VISIBLE
        image.setImageResource(resID)
        /*image.setOnClickListener {
            activityData.invoke(DetailActivityData(cpVo.id, cpVo.pcID))
        }*/
    }

    private fun openYesNoDialog(cpVo: CheckingPointVo, ce: ChartEntryVo) {
        val builder = AlertDialog.Builder(context)
        val dataSourceArray = BkpUiUtil.getYesNoArray()
        val date = getDateFromId(ce.dateKey)
        val title: String = getDayText(date, 1) + " - " + cpVo.name
        builder.setTitle(title)
        builder.setSingleChoiceItems(
            dataSourceArray, BkpUiUtil.getYesNoIndex(ce.value.toString())
        ) { dialog, which ->
            ce.value = BkpUiUtil.getYesNoId(which)
            activityData.invoke(
                DetailActivityData(
                cpvo = cpVo,
                    cevo = ce,updateChart = true
            ))
            dialog.dismiss()
        }
        val d = builder.create()
        d.show()
    }

    override fun getItemCount() = list.size


}

data class DetailActivityData(var cpID: Long = 0, var pcID: Long = 0, var cpvo:CheckingPointVo = CheckingPointVo(), var cevo:ChartEntryVo= ChartEntryVo(), var updateChart : Boolean = false)


data class TabType1(
    val header: Header = Header(""), val cps: CheckingPointVoList = CheckingPointVoList(
        cps = listOf(
            CheckingPointVo()
        )
    )
)

数据赋值代码

val data: ArrayList<TabType1> = arrayListOf(
                    TabType1(
                        header = Header("Group One")
                    ),
                    TabType1(
                        cps = CheckingPointVoList(it)
                    ),
                    TabType1(
                        header = Header("Group Two")
                    ),
                    TabType1(
                        cps = CheckingPointVoList(it)
                    )
                )

    if(it1.updateChart){
                    vm.updateChartEntry(it1.cpvo,it1.cevo)
                }else{
                    Intent(requireContext(), CpDetailActivity::class.java).apply {
                        putExtra("cprId", it1.cpID)
                        putExtra("pcId", it1.pcID)
                        requireActivity().startActivity(this)
                    }
                }


            }, arraylist,dayslist)

运行效果

运行效果截图

问题排查与修复方案

核心问题

你错误地将整组的N条数据都塞到了同一个TabType1对象中,传给Adapter的列表只有4个元素:2个头部类型的TabType1 + 2个包含整组数据的行类型TabType1,getItemCount()返回4,所以RecyclerView只会渲染4个item。同时你在DVH的绑定逻辑中循环遍历一整组的数据给同一个ViewHolder重复赋值,最终每个行类型的item只会显示最后一次赋值的内容,导致数据展示缺失。

修复步骤

  1. 改造数据构造逻辑,平铺列表元素
    每个头部、每条数据行都单独对应列表里的一个元素,比如两组每组1头+5行,最终传给Adapter的列表应该有12个元素:
val data: ArrayList<TabType1> = arrayListOf()
// 第一组
data.add(TabType1(header = Header("Group One")))
it.forEach { cp ->
    // 每条数据单独生成一个TabType1,只存当前行的单条数据
    data.add(TabType1(cps = CheckingPointVoList(listOf(cp))))
}
// 第二组
data.add(TabType1(header = Header("Group Two")))
it.forEach { cp ->
    data.add(TabType1(cps = CheckingPointVoList(listOf(cp))))
}
  1. 修改行item的绑定逻辑,删除循环
    现在每个行类型的TabType1只存单条数据,直接取第一个元素绑定即可:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val data: TabType1 = list[position]
    if (holder is DVH) {
        // 直接取单条数据绑定,不需要循环
        val cp = data.cps.cps.first()
        Timber.i("is: a row")
        holder.bind(cp)
    }else{
        Timber.i("is: header")
        holder as TitleHolder
        holder.bind(data.header)
    }
}
  1. 可选优化:减少重复操作
    DVH中的dayContainer可以直接在构造时初始化,不需要每次绑定都重复添加、清空元素:
inner class DVH(binding: DailyFragRecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
    private var cpname: TextView = binding.cpName
    private var day1: FrameLayout = binding.frameLayout
    private var day2: FrameLayout = binding.frameLayout2
    private var day3: FrameLayout = binding.frameLayout3
    private var day4: FrameLayout = binding.frameLayout4
    // 构造时直接初始化,不需要每次bind操作
    private val dayContainer: ArrayList<FrameLayout> = arrayListOf(day1, day2, day3, day4)

    fun bind(cpVo: CheckingPointVo) {
        // 删除原有dayContainer.addAll和clear逻辑
        cpname.apply {
            text = cpVo.name
            setOnClickListener {
                activityData.invoke(DetailActivityData(cpVo.id, cpVo.pcID))
            }
        }
        // 剩余原有逻辑保持不变
    }
}

内容的提问来源于stack exchange,提问作者Pemba Tamang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 12:15:04