0

I have a bug RecyclerView where items are not coming as expected. RecyclerView is using multiple ViewType (Category and items) but Currently, The adapter is printing items like this. (The Category & Items doesn't seems to be mapped correctly)

Category 1
Category 2
Category 3
    - item
    - item
    - item
    - item
    - item
    - item

I want the adapter to print items like this

Category 1
    - item
    - item
Category 2
    - item
    - item
    - item
Category 2
    - item

My adapter looks like this

    class MyProfileAdapter(private val context: Context, private val list: List<HelpResourceModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        const val VIEW_TYPE_RESOURCE_CATEGORY = 1
        const val VIEW_TYPE_RESOURCE_ITEM = 2
    }

    private val resourceCategories = list.groupBy { it.ProfileCategory }.keys.toList()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            VIEW_TYPE_RESOURCE_CATEGORY -> {
                val binding = HelphulCategoryRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                ProfileCategoryViewHolder(binding)
            }
            VIEW_TYPE_RESOURCE_ITEM -> {
                val binding = ItemMyProfileAdapterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                ViewHolder(binding)
            }
            else -> throw IllegalArgumentException("Invalid view type")
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder.itemViewType) {
            VIEW_TYPE_RESOURCE_CATEGORY -> {
                val viewHolder = holder as ProfileCategoryViewHolder
                viewHolder.bind(resourceCategories[position])
            }
            VIEW_TYPE_RESOURCE_ITEM -> {
                val viewHolder = holder as ViewHolder
                val resource = getItemByPosition(position)
                viewHolder.bind(resource)
            }
        }
    }

    override fun getItemCount() = list.size + resourceCategories.size

    override fun getItemViewType(position: Int): Int {
        return if (isProfileCategory(position)) {
            VIEW_TYPE_RESOURCE_CATEGORY
        } else {
            VIEW_TYPE_RESOURCE_ITEM
        }
    }

    private fun isProfileCategory(position: Int): Boolean {
        return position in resourceCategories.indices
    }

    private fun getItemByPosition(position: Int): HelpResourceModel {
        return list[position - resourceCategories.size]
    }

    inner class ViewHolder(val binding: ItemMyProfileAdapterBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(model: HelpResourceModel) {
            binding.apply {
                tvTitle.text = model.title
                tvType.text = context.getString(if (model.getType() == HelpResourceTypeEnum.FAQ) R.string.faq else R.string.resource)
                tvType.background = (ContextCompat.getDrawable(context, if (model.getType() == HelpResourceTypeEnum.FAQ) R.drawable.dark_blue_30_top_plan_box else R.drawable.orange_30_top_plan_box))
                tvType.setTextColor(ContextCompat.getColor(context, if (model.getType() == HelpResourceTypeEnum.FAQ) R.color.darkBlue else R.color.orange))
            }
        }
    }

    inner class ProfileCategoryViewHolder(val binding: HelphulCategoryRowBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(ProfileCategory: String) {
            binding.category.text = ProfileCategory
        }
    }
}

My HelpResourceModel looks like this

    @Keep
@Parcelize
data class MyProfileModel(
    val title: String = "",
    val contents: String = "",
    val resourceType: String = "",
    val persona: String = "",
    val resourceCategory: String = "",
) : Parcelable {

    fun getType(): MyProfileTypeEnum {
        return when (resourceType) {
            MyProfileTypeEnum.FAQ.name -> MyProfileTypeEnum.FAQ
            MyProfileTypeEnum.ResourceArticle.name,
            MyProfileTypeEnum.RESOURCE.name -> MyProfileTypeEnum.RESOURCE
            else -> MyProfileTypeEnum.FAQ
        }
    }
}

Not sure what I am doing wrong here

GeekWithGlasses
  • 572
  • 2
  • 12
  • 30
  • This issue has to do with how you create the data source list. You should add the relevant code where you create that list. – Kozmotronik Apr 21 '23 at 12:00
  • @Kozmotronik The data is coming from the API it's a listOfData: ArrayList which is passed to the adapter directly. However, I have also tried using `listOfData.sortBy { it.resourceCategory }` before passing it to the adapter. I still see no change in the result – GeekWithGlasses Apr 21 '23 at 12:06
  • What's the values in `resourceCategories` list . Does this list contains the correct values for position you want your 2nd view type? – Nitish Apr 21 '23 at 12:20

2 Answers2

3

your problem lies in this method:

private fun isResourceCategory(position: Int): Boolean {
    return position in resourceCategories.indices
}

it returns true for positions 0, 1 and 2, as resourceCategories have three items

you should preserve "original position" of each category in val list: List<HelpResourceModel>, currently you are creating new list with categories only, so they have new positions in this new list

snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • What should it return Ideally then? the sample output is just for reference. A category can have 0 or multiple items. It should act like a group-by filter – GeekWithGlasses Apr 21 '23 at 12:12
  • sadly this needs some deep dive in your logic, I don't have time for that currently... I would go with creating new list, which would contain all `list` items + categories placed on proper positions in this list – snachmsm Apr 21 '23 at 12:23
2

I think you have issue with onBindViewHolder()

please try..

   override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (isResourceCategory(position)) {
            VIEW_TYPE_RESOURCE_CATEGORY -> {
                val viewHolder = holder as ResourceCategoryViewHolder
                viewHolder.bind(resourceCategories[position])
            }
            VIEW_TYPE_RESOURCE_ITEM -> {
                val viewHolder = holder as ViewHolder
                val resource = getItemByPosition(position)
                viewHolder.bind(resource)
            }
        }
    }
    
Manish Ahire
  • 550
  • 4
  • 19