未显示具有多种项目类型的 Recyclerview

Recyclerview with multiple items types not being displayed

我试图在 recyclerview 中显示多种类型的视图,其中有一个 header 和 5 行,然后是另一个 header 和 5 行,但只显示了 4 行。

这是 onBindViewHolder

中适配器的日志输出

这是来自适配器的代码

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)

这是我得到的输出。

我错过了什么?

Adapter 应该适应 ONE 数据列表,而不是嵌套在其他列表中的多个列表。这意味着每个列表都需要 AdapterRecyclerView

如果您想这样做,您需要向 DVH 添加 Adapter/RecyclerView 并在其中显示您的 CheckingPoint 列表。

看这里 -> override fun getItemCount() = list.size

它只会绑定 4 次,因为你只给了它 4 个项目。

在这里你只是重新绑定相同的 ViewHolder,你需要将 data 传递给 ViewHolder 并让它显示“cps”列表[=22] =]

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val data: TabType1 = list[position]
    if (holder is DVH) {
        holder.bind(data)  //HERE
    }else{
        Timber.i("is: header")
        holder as TitleHolder
        holder.bind(data.header)
    }
}



inner class DVH(binding: DailyFragRecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
    private var checkingPointVoRecyclerView: RecyclerView = binding.recyclerVew
    private var checkingPointVoAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder> = CheckingPointVoAdapter(dates)
    checkingPointVoRecyclerView.adapter = checkingPointVoAdapter
    fun bind(data: TabType1) {
         checkingPointVoAdapter.list = data.cps
         checkingPointVoAdapter.notifyDataSetChanged()
    }
}


class CheckingPointVoAdapter(
    private val dates: List<Date>,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
    public var list: List<CheckingPointVo> = ArrayList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        context = parent.context
        val layoutInflater = LayoutInflater.from(context)
            return ViewHolder(CheckingPointRecyclerItemBinding.inflate(layoutInflater, parent, false))
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val cpv: CheckingPointVo = list[position]
        holder as ViewHolder
        holder.bind(cpv)
   }
inner class ViewHolder(binding: CheckingPointRecyclerItemBinding) : 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()


    }

}

}

我没有对此进行测试,但这大约是你需要的 90%。

OnBindViewHolder 给你一个 view 或者你可以把它想象成一行 ,然后将数据绑定到该行(设置文本等)

让我们看看您在 OnBindViewHolder

中实际做了什么
       data.cps.cps.forEach { // you bind the data to the same row multiple times
            Timber.i("is: a row")
            holder.bind(it)
        }

但是只有一排
要解决这个问题,您需要传递 header itemsrow items 列表

sealed class DataItem {
    abstract val id: Long

    data class RowItem(val row: Row) : DataItem() {
        override val id = row.id
    }

    data class HeaderItem(val header: Header) : DataItem() {
        override val id = header.id
    }
}

将您的行和 header 数据转换为 DataItem 像这样

  val rowItems: List<DataItem> = rowList.map { DataItem.RowItem(it) }
    
  val headertems: List<DataItem> = headerList.map { DataItem.BankItem(it) }
    
  val items = rowItems+ headertems
  //then pass the items to the adapter

因此您的适配器现在将为列表中的每个项目创建一个新的 ViewHoler,您可以检查它

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            ITEM_VIEW_TYPE_ITEM_ROW -> DVH(DailyFragRecyclerItemBinding.inflate(layoutInflater, parent, false))
            ITEM_VIEW_TYPE_ITEM_HEADER -> TitleHolder(DailyTableHeaderBinding.inflate(layoutInflater, parent, false))
            else -> throw ClassCastException("Unknown viewType $viewType")
        }
    }

然后将数据绑定到每个视图,如

   override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is DVH-> {
               val item = getItem(position) as DataItem.RowItem
               holder.bind(item)
            }
            is TitleHolder -> holder.bind(item)
        }
    }

最后你需要调整getItemViewType

   override fun getItemViewType(position: Int): Int {
        return when (getItem(position)) {
            is DataItem.RowItem -> ITEM_VIEW_TYPE_ITEM_ROW
            is DataItem.HeaderItem -> ITEM_VIEW_TYPE_ITEM_HEADER
        }
    }