Home > Software engineering >  Recyclerview with multiple items types not being displayed
Recyclerview with multiple items types not being displayed

Time:10-23

I am trying to show multiple type of views in a recyclerview with a header and 5 rows followed by another header and 5 rows but only 4 rows are appearing.

Here is the log output from the adapter in the onBindViewHolder

enter image description here

Here is the code from the 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()
        )
    )
)

This is how the adapter is given the data.

 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)

This is the output that I get.

enter image description here

What am I missing?

CodePudding user response:

An Adapter should adapt ONE list of data, not multiple lists nested within others. Meaning you need an Adapter and RecyclerView for each list.

If you want to do that you need to add an Adapter/RecyclerView to DVH and display your CheckingPoint list in there.

look here -> override fun getItemCount() = list.size

Its only going to bind 4 times because you only gave it 4 items.

And here you are just rebinding the same ViewHolder, you need to pass in the data to the ViewHolder and have it display the list of "cps"

data.cps.cps.forEach {
    Timber.i("is: a row")
    holder.bind(it)
}

CodePudding user response:

OnBindViewHolder gives you a view or you can imagine it as a row , then you bind data to that row (setting text, etc..)

let's look at what you actually did in OnBindViewHolder

       data.cps.cps.forEach { // you bind the data to the same row multiple times
            Timber.i("is: a row")
            holder.bind(it)
        }

But there is only one row.
To solve this you need to pass a list of header items and row items only

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
    }
}

The Transform your row and header data to the DataItem Like this

  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

So your adapter now will create a new ViewHoler to each item in the list and you can check for it as

   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")
        }
    }

then bind data to each view as

   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)
        }
    }

Finally you need to adjust 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
        }
    }
  • Related