Android ListView Section Header Example Group By Header

android listview section header

I will explain about android listview section header with example in this article.

You will make a listview with a specific header between it’s row items.

Read how to make recyclerview with section header.

Last Views

After all of this hard work, you should get the listview like below video

What is Section Header ?

Section header means that a header among listview items to sort them with particular category.

For example, you are making listview with county names. Now countries can be categorized by continents.

So, you will make the first row as a section header which have continent name like Asia. Then in the second, third row you will write Asian countries like India, Chine etc.

After Asia, again you will make listview row as a section header and will name it Europe and further this process continues.

In this example, we will group listview by vehicle type as a header. We will create custom view for header which is different than the normal listview item.

So let us follow all the below steps carefully.

Step 1. Layout Resources

Different layout for header and normal rows requires different layout xml files. Hence, we will create two layout resources files.

Make two xml files res->layout directory.

Give a name lv_child.xml to first file and lv_header.xml to second xml file.

Add below code in lv_child.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:background="#e9771f"
        android:textColor="#ffffff"
        android:textSize="40sp"
        android:id="@+id/tvChild"
        android:text="Hello"/>

</android.support.constraint.ConstraintLayout>

Above file will create the layout for normal listview row items.

Copy following code in lv_header.xml

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:background="#e91fce"
        android:textColor="#13cac7"
        android:textSize="40sp"
        android:id="@+id/tvVehicle"
        android:text="Hello"/>

</LinearLayout>

This code will generate the layout for header rows.

Step 2. Separate Classes

We need to create two java classes for both normal and header rows.

This class will work as a general model class. So make two new java classes and name them HeaderModel.java and ChildModel.java respectively.

Write down the below code in HeaderModel.java

public class HeaderModel implements MainActivity.ListItem{

    String header;

    public void setheader(String header) {
        this.header = header;
    }

    @Override
    public boolean isHeader() {
        return true;
    }

    @Override
    public String getName() {
        return header;
    }
}

isHeader() method will tell compiler whether the listview row is a header row or a normal row.

setheader(String header) method will set the string as a header title which passes as a parameter.

getName() method will return a header text in a string format.

Now add the below code in ChildModel.java

public class ChildModel implements MainActivity.ListItem{

    String child;

    public void setChild(String child) {
        this.child = child;
    }

    @Override
    public boolean isHeader() {
        return false;
    }

    @Override
    public String getName() {
        return child;
    }
}

Similar methods are used here as HeaderModel.java class. Only difference is that get method will return normal row item text and set method will set the text as a row item name.

Step 3. Custom Adapter

As always, every listview requires an adapter class which will provide the data to populate the listview rows.

Make a new java class named CustomeAdapter.java and add below code in it.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;

/**
 * Created by Parsania Hardik on 03-Jan-17.
 */
public class CustomeAdapter extends BaseAdapter {

    private Context context;
    private ArrayList<MainActivity.ListItem> listItemArrayList;

    public CustomeAdapter(Context context,ArrayList<MainActivity.ListItem> listItemArrayList) {

        this.context = context;
        this.listItemArrayList = listItemArrayList;
    }

    @Override
    public int getViewTypeCount() {
        return getCount();
    }
    @Override
    public int getItemViewType(int position) {

        return position;
    }

    @Override
    public int getCount() {
        return listItemArrayList.size();
    }

    @Override
    public Object getItem(int position) {
        return listItemArrayList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            holder = new ViewHolder();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            if(listItemArrayList.get(position).isHeader()){
                convertView = inflater.inflate(R.layout.lv_header, null, true);
                holder.tvLabel = (TextView) convertView.findViewById(R.id.tvVehicle);
            }
            else {
                convertView = inflater.inflate(R.layout.lv_child, null, true);
                holder.tvLabel = (TextView) convertView.findViewById(R.id.tvChild);
            }


            convertView.setTag(holder);
        }else {
            // the getTag returns the viewHolder object set as a tag to the view
            holder = (ViewHolder)convertView.getTag();
        }

        holder.tvLabel.setText(listItemArrayList.get(position).getName());

        return convertView;
    }

    private class ViewHolder {

        protected TextView tvLabel;

    }

}

Here I am populating listview with the help of the listItemArraylist.

listItemArraylist is created in the Main activity and passed as a parameter to the constructor of this adapter class.

Hence, let us first understand the Main Activity so it would be easier to understand adapter class.

Step 4. Final Codes

Now the final task is to write Main Activity code.

Add below code in activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    tools:context=".MainActivity">

    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:id="@+id/listView"/>

</android.support.constraint.ConstraintLayout>

Write the following code in MainActivity.java file

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ListView lv;
    private CustomeAdapter customeAdapter;

    private String[] vehicleTypes = new String[]{"Cars", "Bikes",
            "Air Crafts","Old Vehicles"};

    private ArrayList<ListItem> listItemArrayList;

    private String[] childnames = new String[]{"Range Rover", "Lamborghini",
            "Rolls Royce","Ferrari","Harley davidson","Ducati","BMW","Honda","Boeing","Airbus","Royal Air","Space X","Horse","Elephant","Camel","Donkey"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lv = (ListView) findViewById(R.id.listView);

        listItemArrayList = new ArrayList<>();
        populateList();

        customeAdapter = new CustomeAdapter(this,listItemArrayList);
        lv.setAdapter(customeAdapter);


    }

    public interface ListItem {
         boolean isHeader();
         String getName();
    }

    private void populateList(){

        int headerdone = 0, childdone = 0;

        for(int i = 0; i < 20; i++){

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

    }
}

Diving Deep In Main Activity

Consider below code

  private String[] vehicleTypes = new String[]{"Cars", "Bikes",
            "Air Crafts","Old Vehicles"};
 private String[] childnames = new String[]{"Range Rover", "Lamborghini",
            "Rolls Royce","Ferrari","Harley davidson","Ducati","BMW","Honda","Boeing","Airbus","Royal Air","Space X","Horse","Elephant","Camel","Donkey"};

First string array vehicleTypes will provide the header names.

Secong string array childnames will provide the text names for normal listview rows.

listItemArraylist is defined as below

 private ArrayList<ListItem> listItemArrayList;

Here ListItem is an Interface which in implemented by both model classes :  HeaderModel.java and ChildModel.java.

So the methods present is the ListItem interface are overriden in HeaderModel.java and ChildModel.java.

Following is the code for the Interface ListItem

public interface ListItem {
         boolean isHeader();
         String getName();
    }

As we have seen earlier in Step 2 that both the methods of Interface ListItem are overriden in HeaderModel.java and ChildModel.java.

Now we will create data structure using populateList() method.

Below is the source code for the populateList() method.

 private void populateList(){

        int headerdone = 0, childdone = 0;

        for(int i = 0; i < 20; i++){

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

    }

Here I have make four child rows for each header.

Listview will start with the first header then four rows will be it’s child items. The fifth row will be second header and then four rows will be the child row for second header and it continues in this fashion.

So the first, sixth, eleventh and sixteenth row of the listview will be header rows.

Hence in the for loop if condition will be true for four times.

When the if condition is true, object of the HeaderModel will be added to the listIemArrayList

In all other for loop iterations, object of the ChildModel will be added to the listIemArrayList.

After completion of for loop, listIemArrayList is passed to the adapter constructor.

Now let’s see the adapter code.

Understanding Adapter

Read the below code for getView() method

 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            holder = new ViewHolder();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            if(listItemArrayList.get(position).isHeader()){
                convertView = inflater.inflate(R.layout.lv_header, null, true);
                holder.tvLabel = (TextView) convertView.findViewById(R.id.tvVehicle);
            }
            else {
                convertView = inflater.inflate(R.layout.lv_child, null, true);
                holder.tvLabel = (TextView) convertView.findViewById(R.id.tvChild);
            }


            convertView.setTag(holder);
        }else {
            // the getTag returns the viewHolder object set as a tag to the view
            holder = (ViewHolder)convertView.getTag();
        }

        holder.tvLabel.setText(listItemArrayList.get(position).getName());

        return convertView;
    }

This method will fetch the data from listIemArrayList and will populate the listview using that data.

Look at the following codes

 if(listItemArrayList.get(position).isHeader()){
                convertView = inflater.inflate(R.layout.lv_header, null, true);
                holder.tvLabel = (TextView) convertView.findViewById(R.id.tvVehicle);
            }
            else {
                convertView = inflater.inflate(R.layout.lv_child, null, true);
                holder.tvLabel = (TextView) convertView.findViewById(R.id.tvChild);
            }

It will check for the header object by using isHeader() method.

If isHeader() method returns true then compiler will inflate the lv_header.xml file as a listview row layout.

Otherwise it will inflate the lv_child.xml

After checking for above conditions and inflating the layout file compiler will set the text pf the listview row using the below line

 holder.tvLabel.setText(listItemArrayList.get(position).getName());

Other Resources

Download Source Code for Android ListView Section Header Example

[sociallocker]Download code for Listview Section Header Example[/sociallocker]