Kotlin RecyclerView Sectioned Header Android Example

listview section header kotlin, kotlin pick video from gallery or capture from camera, kotlin custom spinner with image, pull to refresh listview android, swipe to refresh recyclerview android, expandablelistview in kotlin, Kotlin Horizontal Progressbar Android, kotlin image slider from url, Kotlin GIF Animated Splash Screen, Kotlin RecyclerView Sectioned Header, kotlin indeterminate circular progressbar, android dexter kotlin, kotlin digital signature view, kotlin alertdialog with edittext, elasticsearch windows, android dexter kotlin

Kotlin RecyclerView Sectioned Header Android Example is the topic of this article.

We will create recyclerview with sectioned headers in android in kotlin language.

Sometimes there are too many child items in the recycler view that we can categorize them for simplification.

For example, we can categorize vehicle manufacturer company by type of vehicles like bikes, cars, air crafts etc.

To sort child items by category we need to put category name first and then display items.

Thus, we need to create two different view types in recycler view. One is for category name which we call header and second is view is for normal child rows.

First of all, see the below video so that you can have idea regarding final output.

Now use the following moves for this example.

Step 1. Gradle Requirements

First of all, make a new project in android studio with Kotlin language.

We need to add some gradle lines in order to use recycler view and card view in our project.

So open your gradle file build.gradle(Module:app) and write the following lines in it

 implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'

First line is integrating recycler view classes in our project.

Similarly, second will help us to use card view classes.

Step 2. Drawable XML files

Now to differentiate header and child view layout we need to create two different layout XML files.

So navigate to the app->res->drawable directory and make a new XML file with name cardview.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_focused="true">
        <shape android:shape="rectangle">
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp"/>
            <gradient android:startColor="#1cb73b" android:endColor="#29ba48" android:angle="270" />

            <corners android:topLeftRadius="4dp" android:topRightRadius="4dp"/>
        </shape>
    </item>

    <item android:state_focused="false">
        <shape android:shape="rectangle">
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp"/>

            <gradient android:startColor="#1cb724" android:endColor="#29ba41" android:angle="270" />

            <corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
        </shape>
    </item>

</selector>

Above file will create the layout structure for header file on the recycler view.

It will use green gradient effect in the background.

Now make another XML file and give it name as cardview_child.xml

You should write the below lines in cardview_child.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_focused="true">
        <shape android:shape="rectangle">
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp"
                />
            <gradient android:startColor="#901cb7" android:endColor="#8529ba" android:angle="270" />

            <corners android:topLeftRadius="4dp" android:topRightRadius="4dp"/>
        </shape>
    </item>

    <item android:state_focused="false"
        >
        <shape android:shape="rectangle">
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp"
                />

            <gradient android:startColor="#8832c1" android:endColor="#8541c4" android:angle="270" />

            <corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
        </shape>
    </item>

</selector>

This file is for making background structure of child row files of the recycler view.

Compiler will use violate color to paint the background of every child rows.

Step 3. Making Main Layout for RecyclerView

Go to app->res->layout folder. Inside layout folder, make a new file and set it’s name as rv_child.xml

This rv_child.xml file should contain the below code structure.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        card_view:cardCornerRadius="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/tv"
                android:height="50dp"
                android:background="@drawable/cardview_child"
                android:gravity="center_vertical"
                android:paddingLeft="10dp"
                android:text="Sub Item"
                android:textColor="#fff"
                android:textStyle="bold"
                android:textSize="30sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/tvChild"
                android:background="#71b9d6"
                android:text="Bike"
                android:padding="10dp"
                android:textColor="#a72b2b"
                android:textSize="25sp"/>


        </LinearLayout>

    </android.support.v7.widget.CardView>
</RelativeLayout>

This file contains two text views. It will use cardview_child.xml in the first text view.

Now make another XML file in the same folder. This time, name of the file should be rv_header.xml

This rv_header.xml is having the following coding lines.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context=".MainActivity">

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        card_view:cardCornerRadius="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/tv"
                android:height="50dp"
                android:background="@drawable/cardview"
                android:gravity="center_vertical"
                android:paddingLeft="10dp"
                android:text="Header : Vehicle Type"
                android:textColor="#fff"
                android:textStyle="bold"
                android:textSize="30sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/tvHeader"
                android:background="#ece4e4"
                android:text="Bike"
                android:padding="10dp"
                android:textColor="#000"
                android:textSize="25sp"/>


        </LinearLayout>

    </android.support.v7.widget.CardView>
</RelativeLayout>

It also has two text views. Among them, first text view has set cardview.xml in it’s background.

Thus, we have two different files. One is for header row and another is for child row of the recycler view.

Step 4. Adapter Class

Make a new Kotlin class with name like CustomAdapter.kt

Write down the below code lines in CustomAdapter.kt file.

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import java.util.ArrayList

class CustomAdapter(private val context: Context, private val listItemArrayList: ArrayList<MainActivity.ListItem>) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val inflater: LayoutInflater

    init {

        inflater = LayoutInflater.from(context)
    }

    override fun getItemCount(): Int {
        return listItemArrayList.size
    }

    override fun getItemViewType(position: Int): Int {
        return if (listItemArrayList[position].isHeader()) {
            LAYOUT_HEADER
        } else
            LAYOUT_CHILD
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        val holder: RecyclerView.ViewHolder
        if (viewType == LAYOUT_HEADER) {
            val view = inflater.inflate(R.layout.rv_header, parent, false)
            holder = MyViewHolderHeader(view)
        } else {
            val view = inflater.inflate(R.layout.rv_child, parent, false)
            holder = MyViewHolderChild(view)
        }

        return holder
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        if (holder.itemViewType == LAYOUT_HEADER) {
            val vaultItemHolder = holder as MyViewHolderHeader
            vaultItemHolder.tvHeader.setText(listItemArrayList[position].getName())
        } else {

            val vaultItemHolder = holder as MyViewHolderChild
            vaultItemHolder.tvchild.setText(listItemArrayList[position].getName())

        }

    }

    internal inner class MyViewHolderHeader(itemView: View) : RecyclerView.ViewHolder(itemView) {

        var tvHeader: TextView

        init {

            tvHeader = itemView.findViewById<View>(R.id.tvHeader) as TextView
        }

    }

    internal inner class MyViewHolderChild(itemView: View) : RecyclerView.ViewHolder(itemView) {

        var tvchild: TextView

        init {

            tvchild = itemView.findViewById<View>(R.id.tvChild) as TextView
        }

    }

    companion object {

        private val LAYOUT_HEADER = 0
        private val LAYOUT_CHILD = 1
    }

}

Adapter is the class which will fill the data in the recycler view.

See the below code of onCreateViewHolder() method.

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        val holder: RecyclerView.ViewHolder
        if (viewType == LAYOUT_HEADER) {
            val view = inflater.inflate(R.layout.rv_header, parent, false)
            holder = MyViewHolderHeader(view)
        } else {
            val view = inflater.inflate(R.layout.rv_child, parent, false)
            holder = MyViewHolderChild(view)
        }

        return holder
    }

In this method, compiler will check if the type of the row is header or child.

You can see the if condition in the above code which is doing this thing.

If the row is header then compiler will inflate rv_header.xml file, otherwise it will inflate rv_child.xml file.

Now attend the below code lines for onBindViewHolder() method.

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        if (holder.itemViewType == LAYOUT_HEADER) {
            val vaultItemHolder = holder as MyViewHolderHeader
            vaultItemHolder.tvHeader.setText(listItemArrayList[position].getName())
        } else {

            val vaultItemHolder = holder as MyViewHolderChild
            vaultItemHolder.tvchild.setText(listItemArrayList[position].getName())

        }

    }

onBindViewHolder() method will set the text value inside the text view.

Here, you can find one if loop to check whether the row is header or child.

Compiler will set the text in the text view of header (tvHeader) for header row and for child row it will set the text value in child text view(tvchild)

Last Step

Open up your activity_main.xml file and write the below text in it.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="#9e9191"
              tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="15dp"/>

</LinearLayout>

I have added just one recycler view in this main layout file.

Now write down the following code text in the MainActivity.kt file.

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.View
import java.util.ArrayList

class MainActivity : AppCompatActivity() {

    private var recyclerView: RecyclerView? = null
    private var customAdapter: CustomAdapter? = null

    private val vehicleTypes = arrayOf("Cars", "Bikes", "Air Crafts", "Old Vehicles")

    private var listItemArrayList: ArrayList<ListItem>? = null

    private val childnames = arrayOf(
        "Range Rover",
        "Lamborghini",
        "Rolls Royce",
        "Ferrari",
        "Harley davidson",
        "Ducati",
        "BMW",
        "Honda",
        "Boeing",
        "Airbus",
        "Royal Air",
        "Space X",
        "Horse",
        "Elephant",
        "Camel",
        "Donkey"
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = findViewById<RecyclerView>(R.id.recycler)

        listItemArrayList = ArrayList()
        populateList()

        customAdapter = CustomAdapter(this, listItemArrayList!!)
        recyclerView!!.adapter = customAdapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
    }

    interface ListItem {
        fun isHeader():Boolean
        fun getName():String
    }

    private fun populateList() {

        var headerdone = 0
        var childdone = 0

        for (i in 0..19) {

            if (i == 0 || (i == 5) or (i == 10) or (i == 15)) {
                val vehicleModel = HeaderModel()
                vehicleModel.setheader(vehicleTypes[headerdone])
                listItemArrayList!!.add(vehicleModel)
                headerdone = headerdone + 1
            } else {
                val childModel = ChildModel()
                childModel.setChild(childnames[childdone])
                listItemArrayList!!.add(childModel)
                childdone = childdone + 1
            }
        }

    }
}

Some Details

First of all, see the below code lines

 private var recyclerView: RecyclerView? = null
    private var customAdapter: CustomAdapter? = null

    private val vehicleTypes = arrayOf("Cars", "Bikes", "Air Crafts", "Old Vehicles")

    private var listItemArrayList: ArrayList<ListItem>? = null

First line is the object of Recyclerview class and second is for object of CustomAdapter class.

Third line is the string array which holds the names of the vehicle types.

Last line is the arraylist with the objects of the interface named ListItem

Now check the following lines of code

  private val childnames = arrayOf(
        "Range Rover",
        "Lamborghini",
        "Rolls Royce",
        "Ferrari",
        "Harley davidson",
        "Ducati",
        "BMW",
        "Honda",
        "Boeing",
        "Airbus",
        "Royal Air",
        "Space X",
        "Horse",
        "Elephant",
        "Camel",
        "Donkey"
    )

Another string variable with the names of the Vehicle companies. These are the child rows.

Now below is the code for the method populateList() method.

 private fun populateList() {

        var headerdone = 0
        var childdone = 0

        for (i in 0..19) {

            if (i == 0 || (i == 5) or (i == 10) or (i == 15)) {
                val vehicleModel = HeaderModel()
                vehicleModel.setheader(vehicleTypes[headerdone])
                listItemArrayList!!.add(vehicleModel)
                headerdone = headerdone + 1
            } else {
                val childModel = ChildModel()
                childModel.setChild(childnames[childdone])
                listItemArrayList!!.add(childModel)
                childdone = childdone + 1
            }
        }

    }

This method will create data source of the example. An arraylist (listItemArrayList) with object of the interface ListItem is the main datasource.

This method will create the data source listItemArrayList by sorting them by header and child rows.

Now see the below code structure

   customAdapter = CustomAdapter(this, listItemArrayList!!)
        recyclerView!!.adapter = customAdapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)

Compiler will initialize the object of the CustomAdapter class and it will pass listItemArrayList  in it’s second parameter.

Adapter will use this listItemArrayList  to create child and header rows of the Recycler view.

Finally, compiler will bind the object of the CustomAdapter class to the Recycler view.

Download Code from Github

Kotlin RecyclerView Download Code