Sectioned RecyclerView — Kotlin
Introduction: This story describes about sectioned RecyclerView in Android using Kotlin in combination with JetPack components.
What is SectionedList? A sectioned list displays items in a list based on section. For instance displays list of state capitals for each country. Where country is category under which all its related state/capital info is displayed.
Screen shots: The working example that implemented section list will have final output as below :
The above example displays section wise State/capitals of a country .
Steps to implement :
To implement sectioned list we need to process received data from Server/DB in such a way that ,it is categorized as sections.
Step 1:
Create a model as below:
data class StateCapital(
val countries: List<Country>
) {
data class Country(
val country: String, // India
val states: List<State>
) {
data class State(
val capital: String, // Hyderabad
val name: String // Telangana
)
}
}
Step 2: Prepare another model that categorizes data in to Parent (section header) and child (Parent’s corresponding child info)
class ExpandableCountryModel {
companion object{
const val PARENT = 1
const val CHILD = 2
}
lateinit var countryParent: StateCapital.Country
var type : Int
lateinit var countryChild : StateCapital.Country.State
var isExpanded : Boolean
private var isCloseShown : Boolean
constructor( type : Int, countryParent: StateCapital.Country, isExpanded : Boolean = false,isCloseShown : Boolean = false ){
this.type = type
this.countryParent = countryParent
this.isExpanded = isExpanded
this.isCloseShown = isCloseShown
}
constructor(type : Int, countryChild : StateCapital.Country.State, isExpanded : Boolean = false,isCloseShown : Boolean = false){
this.type = type
this.countryChild = countryChild
this.isExpanded = isExpanded
this.isCloseShown = isCloseShown
}
}
Step 3: Process data to achieve sectioned view:
fun prepareDataForSectionedAdapter(stateCapital: StateCapital) : MutableList<ExpandableCountryModel>{
var expandableCountryModel = mutableListOf<ExpandableCountryModel>()
for (states in stateCapital.countries) {
expandableCountryModel.add(ExpandableCountryModel(ExpandableCountryModel.PARENT,states))
for(capitals in states.states){
expandableCountryModel.add(ExpandableCountryModel(ExpandableCountryModel.CHILD,capitals))
}
}
return expandableCountryModel
}
Step 4: Create a adapter to populate the view as below:
class CountryStateSectionedAdapter(var countryClickedListener: CountryClickedListener, var countryStateModelList:MutableList<ExpandableCountryModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var isFirstItemExpanded : Boolean = true
private var actionLock = false
lateinit var countryName:String
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
ExpandableCountryModel.PARENT -> {CountryStateParentViewHolder(LayoutInflater.from(parent.context).inflate(
R.layout.expandable_parent_item, parent, false))}
ExpandableCountryModel.CHILD -> { CountryStateChildViewHolder(LayoutInflater.from(parent.context).inflate(
R.layout.expandable_child_item, parent, false)) }
else -> {CountryStateParentViewHolder(LayoutInflater.from(parent.context).inflate(
R.layout.expandable_parent_item, parent, false))}
}
}
override fun getItemCount(): Int = countryStateModelList.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val row = countryStateModelList[position]
when(row.type){
ExpandableCountryModel.PARENT -> {
(holder as CountryStateParentViewHolder).countryName.text = row.countryParent.country
countryName = row.countryParent.country
holder.closeImage.visibility = View.GONE
holder.upArrowImg.visibility = View.GONE
}
ExpandableCountryModel.CHILD -> {
(holder as CountryStateChildViewHolder).stateName.text = row.countryChild.name
holder.capitalImage.text = row.countryChild.capital
countryName?.let {
holder.layout.tag = it
}
holder.stateName.tag = row.countryChild
holder.layout.setOnClickListener {
var countryInfo = holder.stateName.tag
countryClickedListener.onItemClick(holder.layout.tag.toString(),
countryInfo as StateCapital.Country.State
)
}
}
}
}
override fun getItemViewType(position: Int): Int = countryStateModelList[position].type
class CountryStateParentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var layout = itemView.country_item_parent_container
internal var countryName : TextView = itemView.country_name
internal var closeImage = itemView.close_arrow
internal var upArrowImg = itemView.up_arrow
}
class CountryStateChildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var layout = itemView.country_item_child_container
internal var stateName : TextView = itemView.state_name
internal var capitalImage = itemView.capital_name
}
}
The code for the above sectioned list is available in the below Github link for reference: