I am implementing a RecyclerView, holding itemViews of both VIEW_HEADER and VIEW_ITEM. Each header view item appears above a triplet of viewItem. When i am implementing both type of Views, i am only getting the first header in the run time of the recyclerview. When i disable the header View, i can get the results of the itemviews in sequential order.
Here is the declaration of the code for the recyclerview adapter:
package com.example.cvdriskestimator.CustomClasses
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.cvdriskestimator.MainActivity
import com.example.cvdriskestimator.R
class leaderBoardRecyclerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val VIEW_ITEM = 0
val VIEW_HEADER = 1
private lateinit var prActivity: MainActivity
var nameDataSet = ArrayList<String>()
var participantAvatars = ArrayList<Drawable>()
private var currentPartId = 1
fun setActivity(mainActivity: MainActivity)
{
prActivity = mainActivity
}
override fun getItemViewType(position: Int): Int {
var view_type = (position % 4)
if (view_type == 0)
return VIEW_HEADER
else
return VIEW_ITEM
return VIEW_ITEM
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when(viewType)
{
1 ->
{
val view : View = LayoutInflater.from(prActivity.applicationContext).inflate(R.layout.leaderboard_header_item_layout, parent , false)
return leaderHeaderViewHolder(view)
}
0 ->
{
val view : View = LayoutInflater.from(prActivity.applicationContext).inflate(R.layout.leaderboard_item_layout , parent , false)
return leaderBoardViewHolder(view)
}
}
val view = View(prActivity.applicationContext)
return leaderBoardViewHolder(view)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(getItemViewType(position))
{
0 ->
{
//populate data for the viewHolder
var leaderBoardViewHolder = holder as leaderBoardViewHolder
leaderBoardViewHolder.part_name.text = nameDataSet[currentPartId - 1]
leaderBoardViewHolder.part_order_id.text = (currentPartId).toString()
leaderBoardViewHolder.part_avatar.setImageDrawable(participantAvatars[currentPartId-1])
currentPartId ++
leaderBoardViewHolder.part_score.text = leaderBoardViewHolder.calculateScore(currentPartId).toString()
}
1 ->
{
var leaderHeaderViewHolder = holder as leaderHeaderViewHolder
leaderHeaderViewHolder.leader_header_txtV.text = leaderHeaderViewHolder.setGroupLetter(currentPartId-1)
}
}
}
override fun getItemCount(): Int {
return nameDataSet.size
}
}
open class leaderBoardViewHolder : RecyclerView.ViewHolder
{
var part_order_id : TextView
var part_name : TextView
var part_avatar : ImageView
var part_rel_layout : RelativeLayout
var part_score : TextView
constructor(itemView: View) : super(itemView) {
part_rel_layout = itemView.findViewById(R.id.partRelLayout)
part_order_id = itemView.findViewById(R.id.partorderTxtV)
part_score = itemView.findViewById(R.id.partScoreTxtV)
part_name = itemView.findViewById(R.id.parttxtV)
part_avatar = itemView.findViewById(R.id.partAvatorImgV)
}
fun calculateScore(id : Int) : Int
{
val score = 10000 - (id * 500)
return score
}
}
class leaderHeaderViewHolder : RecyclerView.ViewHolder
{
var leader_header_txtV : TextView
constructor(itemView : View) : super(itemView) {
leader_header_txtV = itemView.findViewById(R.id.groupleadTxtV)
}
fun setGroupLetter(position : Int) : String
{
var result = (position / 3)
var letter = "A"
when(result)
{
0 ->
{
letter = "GROUP A"
}
1 ->
{
letter = "GROUP B"
}
2 ->
{
letter = "GROUP C"
}
3 ->
{
letter = "GROUP D"
}
}
return letter
}
}
And on the fragment, the declaration of the RecyclerView:
private fun initLeaderRecyclerView(view : View)
{
leaderboardRecyclerView = view.findViewById(R.id.leaderBoardRecyclerView)
populateDataForRecyclerView()
leaderBoardRecyclerAdapter = leaderBoardRecyclerAdapter()
leaderBoardRecyclerAdapter.setActivity(mainActivity)
leaderBoardRecyclerAdapter.nameDataSet = participantNames
leaderBoardRecyclerAdapter.participantAvatars = participantAvatars
leaderboardRecyclerView.apply {
adapter = leaderBoardRecyclerAdapter
}
var linearLayoutManager = LinearLayoutManager(mainActivity.applicationContext)
linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
leaderboardRecyclerView.layoutManager = linearLayoutManager
}
Any ideas of why might this be occuring?
Thank you in advance,
Lampros
Related
I have two item models in RecyclerView, when I want to show one of them through in my app, it crashes but only when I have different item models(the same models except his color) they work. How can I make different models(1 has 2 string, another has 3 string) and show it in one holder?
My code(different colors)
Adapter and Holder:
private inner class CrimeHolder(view: View) : RecyclerView.ViewHolder(view),
View.OnClickListener {
private val titleTextView: TextView = itemView.findViewById(R.id.crime_title)
private val dateTextView: TextView = itemView.findViewById(R.id.crime_date)
private lateinit var crime: Crime
init {
itemView.setOnClickListener(this)
}
fun bind(crime: Crime) {
this.crime = crime
titleTextView.text = crime.title
dateTextView.text = crime.date.toString()
}
override fun onClick(v: View?) {
Toast.makeText(context, "${crime.title} pressed!", Toast.LENGTH_SHORT).show()
}
}
private inner class CrimeAdapter(var crimes: List<Crime>) :
RecyclerView.Adapter<CrimeHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
var view:View
if(viewType == 1){
view = layoutInflater.inflate(R.layout.list_item_crime_police,parent,false)
}else{
view = layoutInflater.inflate(R.layout.list_item_crime,parent,false)
}
return CrimeHolder(view)
}
override fun getItemViewType(position: Int): Int {
return when (crimes[position].requiresPolice) {
true -> 1
else -> 2
}
}
override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
val crime = crimes[position]
holder.bind(crime)
}
override fun getItemCount() = crimes.size
}
Crime:
data class Crime(
val id: UUID = UUID.randomUUID(),
var title: String = "",
var date: Date = Date(),
var isSolved: Boolean = false,
var requiresPolice : Boolean = false
)
Resource where I take "Crimes":
class CrimeListViewModel : ViewModel() {
val crimes = mutableListOf<Crime>()
init {
for (i in 0 until 100) {
val crime = Crime()
crime.title = "Crime #$i"
crime.isSolved = i % 2 == 0
crime.requiresPolice = i % 2 == 0
crimes += crime
}
}
}
This is my Output
I saw different tutorials on the internet to create a nested recyclerView. It works as expected but all the items are collapsed when I initialize the adapter, while I need to show all of them opened:
Namely at the moment I have:
Category 1
Category 2
Category 3
Whereas I want :
Category1
Dish1
Dish2
Category2
Dish 1
Category 3
Dish 1
Dish2
I have an handle can click on every Category, for instance Category 1, and a click listener loads the adapter on the basis of the position, but I want to open all the items when the adapter is initialized
I tried also to furnish from the view that calls the Recyclerview a callback from onLayoutLoaded, but if I call the expand method, from the view it works only if i furnish one position ex. expandRow(0) For instance to open the first item, but if I call all the positions on the basis of the count, the size of the list just does not work
Here my adapter with at the end of the class attached the datamodel
class CategoryListExpandableAdapter(var context: Context, var categoriesList:MutableList<ExpandableCategoryModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var shouldCategoryExpanded by Delegates.notNull<Boolean>()
var presentPosition by Delegates.notNull<Int>()
init {
shouldCategoryExpanded = true
}
val parentLayout = R.layout.parent_category_item
val childLayout = R.layout.child_dish_item
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
parentLayout -> {ParentCategoryViewHolder(LayoutInflater.from(parent.context).inflate(
parentLayout, parent, false))}
else -> { ChildDishViewHolder(LayoutInflater.from(parent.context).inflate(
childLayout, parent, false)) }
}
}
override fun getItemCount(): Int = categoriesList.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val row = categoriesList[position]
presentPosition = position
when(row.type){
CATEGORY_PARENT -> {
(holder as ParentCategoryViewHolder).categoryName.text = row.categoryParent.categoryName
arrowDownButtonListener(holder, row, position)
upArrowButtonListener(holder, row, position)
}
DISH_CHILD -> {
(holder as ChildDishViewHolder).descriptionDishText.text = row.dishChild.description
holder.nameDishText.text = row.dishChild.name
}
}
}
private fun upArrowButtonListener(holder: ParentCategoryViewHolder, row: ExpandableCategoryModel, position: Int) {
holder.upArrowImage.setOnClickListener {
collapseRow(position)
holder.upArrowImage.visibility = View.GONE
holder.downArrowImage.visibility = View.VISIBLE
}
}
private fun arrowDownButtonListener(holder: ParentCategoryViewHolder, row: ExpandableCategoryModel, position: Int) {
holder.downArrowImage.setOnClickListener {
holder.upArrowImage.visibility = View.VISIBLE
holder.downArrowImage.visibility = View.GONE
expandRow(position)
}
}
override fun getItemViewType(position: Int): Int {
return when (categoriesList[position].type) {
1 -> parentLayout
else -> childLayout
}
}
private fun expandRow(position: Int){
val row = categoriesList[position]
var nextPosition = position
when (row.type) {
CATEGORY_PARENT -> {
for(dishes in row.categoryParent.dishesList){
categoriesList.add(++nextPosition, ExpandableCategoryModel(DISH_CHILD, dishes))
}
notifyDataSetChanged()
}
DISH_CHILD -> {
notifyDataSetChanged()
}
}
}
private fun collapseRow(position: Int){
val row = categoriesList[position]
var nextPosition = position + 1
when (row.type) {
CATEGORY_PARENT -> {
outerloop# while (true) {
if (nextPosition == categoriesList.size || categoriesList[nextPosition].type == CATEGORY_PARENT) {
break#outerloop
}
categoriesList.removeAt(nextPosition)
}
notifyDataSetChanged()
}
}
}
class ParentCategoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var categoryName : TextView = itemView.category_name
internal var downArrowImage = itemView.arrow_down
internal var upArrowImage = itemView.up_arrow
}
class ChildDishViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var descriptionDishText : TextView = itemView.child_dish_item_description
internal var nameDishText = itemView.child_dish_item_name
}
}
class ExpandableCategoryModel {
lateinit var categoryParent: Category
var type : Int
lateinit var dishChild : Dish
constructor(type : Int, categoryParent: Category){
this.type = type
this.categoryParent = categoryParent
}
constructor(type : Int, categoryChild : Dish){
this.type = type
this.dishChild = categoryChild
}
}
data class CategoryList(val categories: List<Category>)
data class Category(
val categoryName: String,
val dishesList: List<Dish>
)
data class Dish(
val name: String,
val description: String
)
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
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.
What am I missing?
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.
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
}
}
I've spent quite a while trying to find the cause of this problem. I have a RecyclerView with several different view types but the problem I have is with the click event in 1 of my view holders (ViewHolderA). Since I get these errors, I don't know what else I should using instead. I think my item class may be the problem but I'm not sure where I've gone wrong.
'getter for position: Int' is deprecated in Java
Activity class
class MainActivity : AppCompatActivity() {
private var mList: RecyclerView? = null
private var mRecyclerAdapter: MyAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mList = findViewById(R.id.listRV)
val listEntities = arrayListOf<Any>()
listEntities.add(ItemA("A1", "A2"))
listEntities.add(ItemB("B1"))
listEntities.add(ItemC("C1", "C2", "C3", "C4"))
listEntities.add(ItemD("D1", "D2", "D3", "D4"))
val layoutManager = LinearLayoutManager(this)
mList!!.layoutManager = layoutManager
mRecyclerAdapter = MyAdapter(listEntities)
mList!!.adapter = mRecyclerAdapter
}
}
Adapter class
class MyAdapter(var mData: List<Any>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val typeA: Int = 1
private val typeB: Int = 2
private val typeC: Int = 3
private val typeD: Int = 4
override fun getItemViewType(position: Int): Int {
return when {
mData[position] is ViewHolderA -> {
typeA
}
mData[position] is ViewHolderB -> {
typeB
}
mData[position] is ViewHolderC -> {
typeC
}
mData[position] is ViewHolderD -> {
typeD
}
else -> -1
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
var layout = 0
val viewHolder: RecyclerView.ViewHolder?
when (viewType) {
typeA -> {
layout = R.layout.rv_item_a
val viewA = LayoutInflater.from(parent.context).inflate(layout, parent, false)
viewHolder = ViewHolderA(viewA)
}
typeB -> {
layout = R.layout.rv_item_b
val viewB = LayoutInflater.from(parent.context).inflate(layout, parent, false)
viewHolder = ViewHolderB(viewB)
}
typeC -> {
layout = R.layout.rv_item_c
val viewC = LayoutInflater.from(parent.context).inflate(layout, parent, false)
viewHolder = ViewHolderC(viewC)
}
typeD -> {
layout = R.layout.rv_item_d
val viewD = LayoutInflater.from(parent.context).inflate(layout, parent, false)
viewHolder = ViewHolderD(viewD)
}
else -> viewHolder = null
}
return viewHolder!!
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
typeA -> {
val detailsA = mData[position] as ItemA
(holder as ViewHolderA).showDetails(detailsA)
val mClickListener = View.OnClickListener {
detailsA.isExpanded = !detailsA.isExpanded
notifyItemChanged(position)
val isExpandable : Boolean = detailsA.isExpanded
holder.tvA2.visibility = if (isExpandable) View.VISIBLE else View.GONE
holder.ibA.setImageResource(if (isExpandable) R.drawable.expand_less else R.drawable.expand_more)
}
holder.headerEConstraintLayout.setOnClickListener(mClickListener)
holder.headerEImageButtonA.setOnClickListener(mClickListener)
}
typeB -> {
val detailsB = mData[position] as ItemB
(holder as ViewHolderB).showDetails(detailsB)
}
typeC -> {
val detailsC = mData[position] as ItemC
(holder as ViewHolderC).showDetails(detailsC)
}
typeD -> {
val detailsD = mData[position] as ItemD
(holder as ViewHolderD).showDetails(detailsD)
}
}
}
override fun getItemCount(): Int {
return mData.size
}
inner class ViewHolderA(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val itemAcl: ConstraintLayout = itemView.findViewById(R.id.clA)
private val itemAib: ImageButton = itemView.findViewById(R.id.ibA)
private val itemA1: TextView = itemView.findViewById(R.id.tvA1)
private val itemA2: TextView = itemView.findViewById(R.id.tvA2)
}
inner class ViewHolderB(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val itemB1: TextView = itemView.findViewById(R.id.tvB1)
fun showDetails(detailsB: ItemB) {
val txtB1 = detailsB.txtB1
itemB1.text = txtB1
}
}
inner class ViewHolderC(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val itemCib: ImageButton = itemView.findViewById(R.id.ibC)
private val itemC1: TextView = itemView.findViewById(R.id.tvC1)
private val itemC2: TextView = itemView.findViewById(R.id.tvC2)
private val itemC3: TextView = itemView.findViewById(R.id.tvC3)
private val itemC4: TextView = itemView.findViewById(R.id.tvC4)
fun showDetails(detailsC: ItemC) {
val txtC1 = detailsC.txtC1
val txtC2 = detailsC.txtC2
val txtC3 = detailsC.txtC3
val txtC4 = detailsC.txtC4
itemC1.text = txtC1
itemC2.text = txtC2
itemC3.text = txtC3
itemC4.text = txtC4
}
}
inner class ViewHolderD(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val itemD1: TextView = itemView.findViewById(R.id.tvD1)
private val itemD2: TextView = itemView.findViewById(R.id.tvD2)
private val itemD3: TextView = itemView.findViewById(R.id.tvD3)
private val itemD4: TextView = itemView.findViewById(R.id.tvD4)
fun showDetails(detailsD: ItemD) {
val txtD1 = detailsD.txtD1
val txtD2 = detailsD.txtD2
val txtD3 = detailsD.txtD3
val txtD4 = detailsD.txtD4
itemD1.text = txtD1
itemD2.text = txtD2
itemD3.text = txtD3
itemD4.text = txtD4
}
}
}
Item A class
data class ItemA(val txtA1: String,
val txtA2: String,
var isExpanded: Boolean = false)
Item B class
data class ItemB(val txtB1: String)
Item C class
data class ItemC(val txtC1: String,
val txtC2: String,
val txtC3: String,
val txtC4: String,
var isExpanded: Boolean = false)
Item D class
data class ItemD(val txtD1: String,
val txtD2: String,
val txtD3: String,
val txtD4: String)
Update
I think the issue is with this line in the adapter
val isExpandable : Boolean = mData[position].isExpanded
You're basically using the getPosition() function of the adapter. If you must have the click listener in the adapter, put it in onBindViewHolder instead, this way you have the position parameter that's more accurate since you have more than one type of list item
As you know, you can create RecyclerView with multiple ViewHolders follow step:
getItemViewType()
onCreateViewHolder()
onBindViewHolder()
But i can only create two viewholder in other row, example:
I have a list (item 1 - type 1, item 2 - type 1, item 3 - type 2)
So when i use GridLayoutManager with span column is 2, item-1 and item-2 is same row because it same type,
**I have another list (item 1 - type 1, item 2 - type 3, item 3 - type 2)
**
They will be arranged 3 three rows, so i want combine item1 and item 2 same row, not combine layout. Can anyone have an idea?
package info.androidhive.recyclerview
import android.annotation.TargetApi
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
class MoviesAdapter(private val moviesList: List<Movie>) : RecyclerView.Adapter<MoviesAdapter.ViewHolder>() {
var pos = true
fun notifyLayoutChanged(context: Context) {
val isLanscape = context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
convert(moviesList, isLanscape)
notifyDataSetChanged()
}
#TargetApi(Build.VERSION_CODES.N)
private fun convert(moviesList: List<Movie>, isLanscape: Boolean) {
if (isLanscape) {
moviesList.forEach {
if (it.type == Movie.Type.PICKUP) {
it.type = Movie.Type.BANNER
}
}
}
}
class MyViewHolder(view: View) : ViewHolder(view) {
var title: TextView
var year: TextView? = null
var genre: TextView
init {
title = view.findViewById<View>(R.id.title) as TextView
genre = view.findViewById<View>(R.id.genre) as TextView
}
override fun bind(movie: Movie) {
title.text = movie.title
genre.text = movie.genre
}
companion object {
fun create(context: Context): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context)
.inflate(R.layout.movie_list_row, null, false))
}
}
}
class BannerViewHolder(view: View) : ViewHolder(view) {
var view1: MovieView
var view2: MovieView
init {
view1 = view.findViewById<View>(R.id.item1) as MovieView
view2 = view.findViewById<View>(R.id.item2) as MovieView
}
override fun bind(movie: Movie) {
view1.bind(movie)
view2.bind(movie)
}
companion object {
fun create(context: Context): BannerViewHolder {
return BannerViewHolder(LayoutInflater.from(context)
.inflate(R.layout.banner, null, false))
}
}
}
abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(movie: Movie)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
if (viewType == Movie.Type.BANNER.ordinal) {
if (pos) {
pos = false
return MyViewHolder.create(parent.context)
} else {
pos = true
return BannerViewHolder.create(parent.context)
}
} else {
return MyViewHolder.create(parent.context)
}
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView?) {
super.onDetachedFromRecyclerView(recyclerView)
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
if (holder != null) {
val movie = moviesList[position]
holder.bind(movie)
}
}
fun getItem(position: Int): Movie {
return moviesList[position]
}
override fun getItemViewType(position: Int): Int {
val movie = moviesList[position]
return movie.type.ordinal
}
override fun getItemCount(): Int {
return moviesList.size
}
}