Nested Expandable RecyclerView is a simple and light weight demonstration to achieve nested (multi-level) and expandable/collapsable recyclerview in android. For example - below country -> state -> city multilevel list.
- Add model as per need. And make make their type constant and a constructor in RowModel.kt .
const val COUNTRY = 1 @IntDef(COUNTRY, STATE, CITY) @Retention(AnnotationRetention.SOURCE) annotation class RowType lateinit var country : Country constructor(@RowType type : Int, country : Country, isExpanded : Boolean = false){ this.type = type this.country = country this.isExpanded = isExpanded } - Add ViewHolder class and other methods for newly added model in the adapter
class CountryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { internal val nameTv: TextView = itemView.findViewById(R.id.name_tv) as TextView internal val toggleBtn : ImageButton = itemView.findViewById(R.id.toggle_btn) as ImageButton } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val viewHolder: RecyclerView.ViewHolder = when (viewType) { RowModel.COUNTRY -> CountryViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.country_row, parent, false)) else -> CountryViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.country_row, parent, false)) } return viewHolder } - You also need to do some change expand and collapse for your view type
fun expand(position: Int) { var nextPosition = position val row = rowModels[position] when (row.type) { RowModel.COUNTRY -> { /** * add element just below of clicked row */ for (state in row.country.stateList!!) { rowModels.add(++nextPosition, RowModel(RowModel.STATE, state)) } notifyDataSetChanged() } actionLock = false } fun collapse(position: Int) { val row = rowModels[position] val nextPosition = position + 1 when (row.type) { RowModel.COUNTRY -> { /** * remove element from below until it ends or find another node of same type */ outerloop@ while (true) { if (nextPosition == rowModels.size || rowModels.get(nextPosition).type == RowModel.COUNTRY) { break@outerloop } rowModels.removeAt(nextPosition) } notifyDataSetChanged() } } actionLock = false } - Set source list (should be in nested structure) in the adapter
private fun populateData(){ val cityList1 : MutableList<City> = mutableListOf<City>().also { it.add(City("Patna")) it.add(City("Gaya")) it.add(City("Munger")) it.add(City("Siwan")) it.add(City("Chapra")) } val cityList2 : MutableList<City> = mutableListOf<City>().also { it.add(City("Mumbai")) it.add(City("Pune")) it.add(City("Aurangabad")) it.add(City("Nashik")) it.add(City("Nagpur")) } val stateList1 : MutableList<State> = mutableListOf<State>().also { it.add(State("Bihar", cityList1)) it.add(State("Maharashtra", cityList2)) } val stateList2 : MutableList<State> = mutableListOf<State>().also { it.add(State("New York", null)) } rows.add(RowModel(RowModel.COUNTRY, Country("India", stateList1))) rows.add(RowModel(RowModel.COUNTRY, Country("USA", stateList2))) rowAdapter.notifyItemInserted(rows.size) } 