androidkotlinandroid-recyclerviewandroid-listadapter

Recyclerview with multiple items types not being displayed


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?


Solution

  • 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"

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

    }

    I did not test this but this is like 90% of what you need.