RecyclerView is flexible and advance version of GridView and ListView. It uses less memory for listing the large amount of datasets and collections. In this turorial, we will look at how to create a listing with multiple headers in RecyclerView.
This is the fifth in a RecyclerView Series which covers the fundamentals of RecyclerView. If you already have a solid understanding of how to create a RecyclerView, carry on. Otherwise consider starting with this.
Jump to section
- Step-1 (Create a new project)
- Step-2 (Add dependencies)
- Step-3 (Project Resources)
- Step-4 (Design the header layout)
- Step-5 (Design the RecyclerView row layout)
- Step-6 (Working with Modal class)
- Step-7 (Create RecyclerView Adapter)
- Step-8 (activity_main.xml file)
- Step-9 (MainActivity.java)
- Step-10 (Download Project Resources)
Demo
Step-1 (Create a new project)
Create a new project and select the blank template
Step-2 (Add dependencies)
Go to the build.gradle.xml file and add the below code.
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Step-3 (Project Resources)
Open the colors.xml file and replace it with the below code
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<!-- App Colors -->
<color name="color_bg_card">#343434</color>
<color name="color_bg">#292929</color>
<color name="color_red">#FF0B0B</color>
<color name="color_header">#F56E0B</color>
</resources>
Go to the strings.xml file and add the below code.
<resources>
<string name="app_name">RecyclerViewListingWithHeaderExample</string>
<string name="demo_title">RecyclerView Listing with Header Example</string>
<string name="discount">25% Off</string>
<string name="available_in_stock">Available in stock</string>
<string name="men_collection">Men Collection</string>
<string name="women_collection">Women Collection</string>
<string name="children_collection">Children Collection</string>
<!--Category Watches-->
<string name="cat_watch_one">Omega</string>
<string name="cat_watch_two">Rolex</string>
<string-array name="arr_men_watches">
<item>@string/cat_watch_one</item>
<item>@string/cat_watch_two</item>
</string-array>
<!--Category Shoes-->
<string name="cat_shoe_one">Adilette Slides</string>
<string name="cat_shoe_two">OG Old Skool LX</string>
<string-array name="arr_men_shoes">
<item>@string/cat_shoe_one</item>
<item>@string/cat_shoe_two</item>
</string-array>
</resources>
Step-4 (Design the header layout)
Create a layout file that defines what the header looks like. Navigate to the app > res > layout > Right-click on it> New > Layout Resource File called adapter_header.xml. Add the below code
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tvTitle"
android:text="@string/men_collection"
android:textSize="22sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_margin="10dp"
android:textColor="@color/color_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Step-5 (Design the RecyclerView row layout)
Create a layout file that defines what the RecyclerView row looks like. Navigate to the app > res > layout > Right-click on it> New > Layout Resource File called adapter_listing.xml. Add the below code
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
app:cardBackgroundColor="@color/color_bg_card"
app:cardUseCompatPadding="true"
app:cardCornerRadius="5dp"
android:layout_height="120dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:padding="10dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/cat_shoe_one"
android:textColor="@color/white"
android:lines="2"
android:ellipsize="end"
android:textSize="18sp"
android:layout_marginEnd="5dp"
app:layout_constraintEnd_toStartOf="@+id/tvDiscount"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvDiscount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/discount"
android:textColor="@color/white"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvStockAvailability"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/available_in_stock"
android:textSize="16sp"
android:textColor="@color/color_red"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Step-6 (Working with Modal class)
We will create two Java files, one for the header and the second for the listing data. Create a Java file, which will have the header type and title of the header and renamed it with HeaderM.java. Add the below code
package com.example.recyclerviewlistingwithheaderexample;
public class HeaderM {
private String headerTitle;
private int headerType; // 1 for the header title and 2 for the list item.
public HeaderM(String headerTitle, int headerType) {
this.headerTitle = headerTitle;
this.headerType = headerType;
}
public String getHeaderTitle() {
return headerTitle;
}
public void setHeaderTitle(String headerTitle) {
this.headerTitle = headerTitle;
}
public int getHeaderType() {
return headerType;
}
public void setHeaderType(int headerType) {
this.headerType = headerType;
}
}
Create a new Java file called ListItemM.java. Add the below code
package com.example.recyclerviewlistingwithheaderexample;
public class ListItemM extends HeaderM{
private String title; // title of the item
private String discount; // discounted price of the item
private boolean isInStock; // bool will check item is in stock or not
public ListItemM(String headerTitle, int HeaderType, String title, String discount, boolean isInStock) {
super(headerTitle,HeaderType);
this.title = title;
this.discount = discount;
this.isInStock = isInStock;
}
public ListItemM(String headerTitle, int HeaderType){
super(headerTitle,HeaderType);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDiscount() {
return discount;
}
public void setDiscount(String discount) {
this.discount = discount;
}
public boolean isInStock() {
return isInStock;
}
public void setInStock(boolean inStock) {
isInStock = inStock;
}
}
Step-7 (Create RecyclerView Adapter)
Create a new Java file and renamed it AdapterListing.java. In the onCreateViewHolder() method, we will return two layout views according to the current view type. onBindViewHolder() will hold the data according to view type and will display it to the current view holder. getItemViewType() returns the type of current rendering row. Add the below code
package com.example.recyclerviewlistingwithheaderexample;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class AdapterListing extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ListItemM> listItemMList;
private Context _context;
AdapterListing(List<ListItemM> listItemMList) {
this.listItemMList = listItemMList;
}
/**
* Listing View Holder which holds the view of listing layout during rendering
*/
private class ListItemViewHolder extends RecyclerView.ViewHolder {
private TextView tvTitle;
private TextView tvDiscount;
public ListItemViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tvTitle);
tvDiscount = itemView.findViewById(R.id.tvDiscount);
}
}
/**
* Header View Holder which holds the view of header layout during rendering
*/
private class HeaderViewHolder extends RecyclerView.ViewHolder {
private TextView tvTitle;
public HeaderViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tvTitle);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
_context = parent.getContext();
if (viewType == MainActivity.TYPE_HEADER) { // return the header layout if current rendering view type of row is 1
View v1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_header, parent, false);
return new HeaderViewHolder(v1);
} else if (viewType == MainActivity.TYPE_LISTING) { // return the listing layout if current rendering view type of row is 2
View v1 = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_listing, parent, false);
return new ListItemViewHolder(v1);
}
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == MainActivity.TYPE_HEADER) { // if rendering row is header then get the HeaderM modal
HeaderM headerM = listItemMList.get(position);
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
headerViewHolder.tvTitle.setText(headerM.getHeaderTitle());
} else if (holder.getItemViewType() == MainActivity.TYPE_LISTING) { // if the rendering row is listing the get the ListItemM modal
ListItemViewHolder listItemViewHolder = (ListItemViewHolder) holder;
ListItemM listItemM = listItemMList.get(position);
listItemViewHolder.tvTitle.setText(listItemM.getTitle());
listItemViewHolder.tvDiscount.setText(listItemM.getDiscount());
}
}
@Override
public int getItemCount() {
// return the size of the list
return listItemMList.size();
}
@Override
public int getItemViewType(int position) {
// return the current row type
return listItemMList.get(position).getHeaderType();
}
}
Step-8 (activity_main.xml file)
Go to the activity_main.xml file and add the below code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
android:background="@color/color_bg"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvDemoTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="@string/demo_title"
android:textColor="@color/white"
android:textSize="22sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcvListing"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvDemoTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step-9 (MainActivity.java)
Go to the MainActivity.java file and add the below code. Comments are added inside the code for understanding the code. If you get confuse post the comment below.
package com.example.recyclerviewlistingwithheaderexample;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView rcvListing;
private AdapterListing adapterListing;
private List<ListItemM> listItemMList;
public final static int TYPE_HEADER = 1;
public final static int TYPE_LISTING = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initComponents();
prepareMenCollection();
prepareWomenCollection();
prepareChildrenCollection();
adapterListing.notifyDataSetChanged();
}
/**
* initialize the components
*/
private void initComponents() {
listItemMList = new ArrayList<>();
adapterListing = new AdapterListing(listItemMList);
rcvListing = findViewById(R.id.rcvListing);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(RecyclerView.VERTICAL);
rcvListing.setLayoutManager(linearLayoutManager);
rcvListing.setAdapter(adapterListing);
}
/**
* prepare the Men collection listing with header
*/
private void prepareMenCollection() {
// header
ListItemM listItemM = new ListItemM(getString(R.string.men_collection), TYPE_HEADER);
listItemMList.add(listItemM);
// body
String[] menShoeCollectionArr = getResources().getStringArray(R.array.arr_men_shoes);
for (String shoe : menShoeCollectionArr) {
ListItemM item = new ListItemM(getString(R.string.men_collection), TYPE_LISTING, shoe, getString(R.string.discount), true);
listItemMList.add(item);
}
String[] menWatchCollectionArr = getResources().getStringArray(R.array.arr_men_shoes);
for (String watch : menWatchCollectionArr) {
ListItemM item = new ListItemM(getString(R.string.men_collection), TYPE_LISTING, watch, getString(R.string.discount), true);
listItemMList.add(item);
}
}
/**
* prepare the Women collection listing with header
*/
private void prepareWomenCollection() {
// header
ListItemM listItemM = new ListItemM(getString(R.string.women_collection), TYPE_HEADER);
listItemMList.add(listItemM);
// body
String[] menShoeCollectionArr = getResources().getStringArray(R.array.arr_men_shoes);
for (String shoe : menShoeCollectionArr) {
ListItemM item = new ListItemM(getString(R.string.men_collection), TYPE_LISTING, shoe, getString(R.string.discount), true);
listItemMList.add(item);
}
String[] menWatchCollectionArr = getResources().getStringArray(R.array.arr_men_shoes);
for (String watch : menWatchCollectionArr) {
ListItemM item = new ListItemM(getString(R.string.men_collection), TYPE_LISTING, watch, getString(R.string.discount), true);
listItemMList.add(item);
}
}
/**
* prepare the Children collection listing with header
*/
private void prepareChildrenCollection() {
// header
ListItemM listItemM = new ListItemM(getString(R.string.children_collection), TYPE_HEADER);
listItemMList.add(listItemM);
// body
String[] menShoeCollectionArr = getResources().getStringArray(R.array.arr_men_shoes);
for (String shoe : menShoeCollectionArr) {
ListItemM item = new ListItemM(getString(R.string.men_collection), TYPE_LISTING, shoe, getString(R.string.discount), true);
listItemMList.add(item);
}
String[] menWatchCollectionArr = getResources().getStringArray(R.array.arr_men_shoes);
for (String watch : menWatchCollectionArr) {
ListItemM item = new ListItemM(getString(R.string.men_collection), TYPE_LISTING, watch, getString(R.string.discount), true);
listItemMList.add(item);
}
}
}
Step-10 (Download Project Resources)
Thanks for reading the tutorial. Subscribe to my YouTube channel. Like and Share my Facebook page with your friends.