Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A SectionedGridRecyclerViewAdapter: use this class to realize a simple sectioned grid `RecyclerView.Adapter`.

You can use this class to realize a simple sectioned grid RecyclerView.Adapter without changing your code.

Screen

The RecyclerView has to use a GridLayoutManager.

This is a porting of the class SimpleSectionedListAdapter provided by Google

If you are looking for a sectioned list RecyclerView.Adapter you can take a look here

Example:

         //Your RecyclerView
        mRecyclerView = (RecyclerView) getActivity().findViewById(R.id.list);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(),4));

        //Your RecyclerView.Adapter
        mAdapter = new SimpleAdapter(getActivity());

        //This is the code to provide a sectioned grid
        List<SectionedGridRecyclerViewAdapter.Section> sections =
                new ArrayList<SectionedGridRecyclerViewAdapter.Section>();

        //Sections
        sections.add(new SectionedGridRecyclerViewAdapter.Section(0,"Section 1"));
        sections.add(new SectionedGridRecyclerViewAdapter.Section(5,"Section 2"));
        sections.add(new SectionedGridRecyclerViewAdapter.Section(12,"Section 3"));
        sections.add(new SectionedGridRecyclerViewAdapter.Section(14,"Section 4"));
        sections.add(new SectionedGridRecyclerViewAdapter.Section(20,"Section 5"));

        //Add your adapter to the sectionAdapter
        SectionedGridRecyclerViewAdapter.Section[] dummy = new SectionedGridRecyclerViewAdapter.Section[sections.size()];
        SectionedGridRecyclerViewAdapter mSectionedAdapter = new
                SectionedGridRecyclerViewAdapter(getActivity(),R.layout.section,R.id.section_text,mRecyclerView,mAdapter);
        mSectionedAdapter.setSections(sections.toArray(dummy));

        //Apply this adapter to the RecyclerView
        mRecyclerView.setAdapter(mSectionedAdapter);

You can customize the section layout, changing the layout section.xml and changing the code in the SectionedGridRecyclerViewAdapter.SectionViewHolder class and SectionedGridRecyclerViewAdapter#onBindViewHolder method.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="130dp"
android:gravity="center"
android:layout_margin="4dp"
android:textColor="#999999"
android:textStyle="bold"
android:textSize="22sp"
android:background="@drawable/item_background"/>
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical"
android:paddingLeft="16dp"
android:singleLine="true"
android:textAllCaps="true"
android:background="#90CAF9"
android:textSize="16sp"
android:id="@+id/section_text"
android:textStyle="bold" />
/**
* @author Gabriele Mariotti (gabri.mariotti@gmail.com)
*/
public class SectionedGridRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context mContext;
private static final int SECTION_TYPE = 0;
private boolean mValid = true;
private int mSectionResourceId;
private int mTextResourceId;
private LayoutInflater mLayoutInflater;
private RecyclerView.Adapter mBaseAdapter;
private SparseArray<Section> mSections = new SparseArray<Section>();
private RecyclerView mRecyclerView;
public SectionedGridRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,RecyclerView recyclerView,
RecyclerView.Adapter baseAdapter) {
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSectionResourceId = sectionResourceId;
mTextResourceId = textResourceId;
mBaseAdapter = baseAdapter;
mContext = context;
mRecyclerView = recyclerView;
mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
mValid = mBaseAdapter.getItemCount()>0;
notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mValid = mBaseAdapter.getItemCount()>0;
notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mValid = mBaseAdapter.getItemCount()>0;
notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mValid = mBaseAdapter.getItemCount()>0;
notifyItemRangeRemoved(positionStart, itemCount);
}
});
final GridLayoutManager layoutManager = (GridLayoutManager)(mRecyclerView.getLayoutManager());
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return (isSectionHeaderPosition(position))? layoutManager.getSpanCount() : 1 ;
}
});
}
public static class SectionViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public SectionViewHolder(View view,int mTextResourceid) {
super(view);
title = (TextView) view.findViewById(mTextResourceid);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
if (typeView == SECTION_TYPE) {
final View view = LayoutInflater.from(mContext).inflate(mSectionResourceId, parent, false);
return new SectionViewHolder(view,mTextResourceId);
}else{
return mBaseAdapter.onCreateViewHolder(parent, typeView -1);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
if (isSectionHeaderPosition(position)) {
((SectionViewHolder)sectionViewHolder).title.setText(mSections.get(position).title);
}else{
mBaseAdapter.onBindViewHolder(sectionViewHolder,sectionedPositionToPosition(position));
}
}
@Override
public int getItemViewType(int position) {
return isSectionHeaderPosition(position)
? SECTION_TYPE
: mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)) +1 ;
}
public static class Section {
int firstPosition;
int sectionedPosition;
CharSequence title;
public Section(int firstPosition, CharSequence title) {
this.firstPosition = firstPosition;
this.title = title;
}
public CharSequence getTitle() {
return title;
}
}
public void setSections(Section[] sections) {
mSections.clear();
Arrays.sort(sections, new Comparator<Section>() {
@Override
public int compare(Section o, Section o1) {
return (o.firstPosition == o1.firstPosition)
? 0
: ((o.firstPosition < o1.firstPosition) ? -1 : 1);
}
});
int offset = 0; // offset positions for the headers we're adding
for (Section section : sections) {
section.sectionedPosition = section.firstPosition + offset;
mSections.append(section.sectionedPosition, section);
++offset;
}
notifyDataSetChanged();
}
public int positionToSectionedPosition(int position) {
int offset = 0;
for (int i = 0; i < mSections.size(); i++) {
if (mSections.valueAt(i).firstPosition > position) {
break;
}
++offset;
}
return position + offset;
}
public int sectionedPositionToPosition(int sectionedPosition) {
if (isSectionHeaderPosition(sectionedPosition)) {
return RecyclerView.NO_POSITION;
}
int offset = 0;
for (int i = 0; i < mSections.size(); i++) {
if (mSections.valueAt(i).sectionedPosition > sectionedPosition) {
break;
}
--offset;
}
return sectionedPosition + offset;
}
public boolean isSectionHeaderPosition(int position) {
return mSections.get(position) != null;
}
@Override
public long getItemId(int position) {
return isSectionHeaderPosition(position)
? Integer.MAX_VALUE - mSections.indexOfKey(position)
: mBaseAdapter.getItemId(sectionedPositionToPosition(position));
}
@Override
public int getItemCount() {
return (mValid ? mBaseAdapter.getItemCount() + mSections.size() : 0);
}
}
/**
* @author Gabriele Mariotti (gabri.mariotti@gmail.com)
*/
public class SimpleAdapter extends RecyclerView.Adapter<SimpleAdapter.SimpleViewHolder> {
private static final int COUNT = 100;
private final Context mContext;
private final List<Integer> mItems;
private int mCurrentItemId = 0;
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public final TextView title;
public SimpleViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
}
}
public SimpleAdapter(Context context) {
mContext = context;
mItems = new ArrayList<Integer>(COUNT);
for (int i = 0; i < COUNT; i++) {
addItem(i);
}
}
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
return new SimpleViewHolder(view);
}
@Override
public void onBindViewHolder(SimpleViewHolder holder, final int position) {
holder.title.setText(mItems.get(position).toString());
}
public void addItem(int position) {
final int id = mCurrentItemId++;
mItems.add(position, id);
notifyItemInserted(position);
}
public void removeItem(int position) {
mItems.remove(position);
notifyItemRemoved(position);
}
@Override
public int getItemCount() {
return mItems.size();
}
}
@lawrence615

This comment has been minimized.

Copy link

@lawrence615 lawrence615 commented Jun 15, 2015

How can I increase the width of each grid item or make each row to hold only three items uniformly didtributed?

@juanlabrador

This comment has been minimized.

Copy link

@juanlabrador juanlabrador commented Jul 16, 2015

Very easy, comfortable. Thanks. :) How I can update the content dynamically? I try to add a new group with header in position 0, but not possible.

@ptskyin

This comment has been minimized.

Copy link

@ptskyin ptskyin commented Oct 20, 2015

Great thanks! Saved my life!

@pranaypatel512

This comment has been minimized.

Copy link

@pranaypatel512 pranaypatel512 commented Nov 7, 2015

I got my answer here..great thanks 👍

@Juxtlie

This comment has been minimized.

Copy link

@Juxtlie Juxtlie commented Nov 10, 2015

Great thanks!

@drinfernoo

This comment has been minimized.

Copy link

@drinfernoo drinfernoo commented Dec 23, 2015

How can I retrieve the position of the row in my original adapter? When I use getPosition(), it gives me the position, including the section, so my rows pull values from the wrong place :(

@drinfernoo

This comment has been minimized.

Copy link

@drinfernoo drinfernoo commented Dec 30, 2015

Just in case anyone else is wondering, I figured out to use sectionedPositionToPosition(int position) :)

@AEMLoviji

This comment has been minimized.

Copy link

@AEMLoviji AEMLoviji commented Jan 22, 2016

How we can add click event to items? I have done something, but can not get clickable items.

@mposada

This comment has been minimized.

Copy link

@mposada mposada commented Jan 25, 2016

How to add Click to items of the list but not to the section item

@davideas

This comment has been minimized.

Copy link

@davideas davideas commented Feb 25, 2016

For the latest comments up here, I want to notify the comment I posted here: https://gist.github.com/gabrielemariotti/4c189fb1124df4556058#gistcomment-1706849

@lgarcia95

This comment has been minimized.

Copy link

@lgarcia95 lgarcia95 commented Mar 8, 2016

How do I adapt to the head in an image is also displayed?

@walmyrcarvalho

This comment has been minimized.

Copy link

@walmyrcarvalho walmyrcarvalho commented Mar 16, 2016

Thanks for the gist, Gabriele, pretty useful! 👍

@ppamorim

This comment has been minimized.

@ChristopheVersieux

This comment has been minimized.

Copy link

@ChristopheVersieux ChristopheVersieux commented Jun 29, 2016

Same comment than @lawrence651, The width of each item doesn't seem to match parent, even if I put with="match_parent" in each row layout!

@harshal2711

This comment has been minimized.

Copy link

@harshal2711 harshal2711 commented Jul 23, 2016

How can we perform search in this adapter ?

@asadsatti

This comment has been minimized.

Copy link

@asadsatti asadsatti commented Aug 15, 2016

When i replace TextView with ImageView in item.xml. It automatically adds very large margin at the top and bottom of each element that I can't change. Any clues

@asadsatti

This comment has been minimized.

Copy link

@asadsatti asadsatti commented Aug 15, 2016

Found the solution. Needed to set ImageView's android:adjustViewBounds="true"

@nguyenngan

This comment has been minimized.

Copy link

@nguyenngan nguyenngan commented Aug 26, 2016

how to scrollToPosition(position) with RecyclerView thanks

@robinvanyang

This comment has been minimized.

Copy link

@robinvanyang robinvanyang commented Sep 20, 2016

I think there's something wrong with item decoration

@mghouse-mca

This comment has been minimized.

Copy link

@mghouse-mca mghouse-mca commented Sep 29, 2016

Hi, thanks for helping us ....... !!
With this same code, how to implement using CursorAdapter. Please need help.

@theanilpaudel

This comment has been minimized.

Copy link

@theanilpaudel theanilpaudel commented Mar 31, 2017

How to add a section later on and update the view, like using the notifyDataSetChanged() way

@kikermo

This comment has been minimized.

Copy link

@kikermo kikermo commented May 1, 2017

Nice, thanks a lot.

@milhauscz

This comment has been minimized.

Copy link

@milhauscz milhauscz commented May 19, 2017

I wonder how this can work. In SectionedGridRecyclerViewAdapter's onBindViewHolder (line 87) the ViewHolder is explicitly cast to SectionViewHolder, but how can you be sure it is a SectionViewHolder? Since onCreateViewHolder creates instances of two different classes, onBindViewHolder is also going to get instances of two different classes as a parameter without regard to the position, right?
EDIT: Got it, it is ensured by the getItemViewType() method. Elegant!

@abalta

This comment has been minimized.

Copy link

@abalta abalta commented Jun 2, 2017

When I tried to remove item from SimpleAdapter. I have an java.lang.IndexOutOfBoundsException. How can I remove an item and notify dataset successfully?

@DebdeepG

This comment has been minimized.

Copy link

@DebdeepG DebdeepG commented Jun 6, 2017

How to add more items with new headers ? I did several new sections.add(new SectionedGridRecyclerViewAdapter.Section(0,"Section x")) and also mSectionsAdapter.notifyDataSetChanged() , it didn't work. :(

@peter1408

This comment has been minimized.

Copy link

@peter1408 peter1408 commented Jun 14, 2017

how to change the section size at run time

@VivekRajNei

This comment has been minimized.

Copy link

@VivekRajNei VivekRajNei commented Jun 27, 2017

Thanks...

@SiddarthG

This comment has been minimized.

Copy link

@SiddarthG SiddarthG commented Oct 31, 2017

my recyclerview adds blank space between items on scrolling, what might be the reason?

@khizarhayat

This comment has been minimized.

Copy link

@khizarhayat khizarhayat commented Sep 7, 2018

Its great and simple. Thanks man!

@Mustafax06

This comment has been minimized.

Copy link

@Mustafax06 Mustafax06 commented Oct 2, 2018

How do I make header sticky?

@naveen3186

This comment has been minimized.

Copy link

@naveen3186 naveen3186 commented Aug 4, 2019

how to set onclikitem?

@sinandizdarevic

This comment has been minimized.

Copy link

@sinandizdarevic sinandizdarevic commented Apr 6, 2020

@MathewMobi

This comment has been minimized.

Copy link

@MathewMobi MathewMobi commented May 23, 2020

Thanks! saved my day. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.