Create a gist now

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.

Show comment
Hide comment
@lawrence615

lawrence615 Jun 15, 2015

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

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.

Show comment
Hide comment
@juanlabrador

juanlabrador 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.

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.

Show comment
Hide comment
@ptskyin

ptskyin Oct 20, 2015

Great thanks! Saved my life!

ptskyin commented Oct 20, 2015

Great thanks! Saved my life!

@pranaypatel512

This comment has been minimized.

Show comment
Hide comment
@pranaypatel512

pranaypatel512 Nov 7, 2015

I got my answer here..great thanks 👍

I got my answer here..great thanks 👍

@Juxtlie

This comment has been minimized.

Show comment
Hide comment
@Juxtlie

Juxtlie Nov 10, 2015

Great thanks!

Juxtlie commented Nov 10, 2015

Great thanks!

@drinfernoo

This comment has been minimized.

Show comment
Hide comment
@drinfernoo

drinfernoo 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 :(

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.

Show comment
Hide comment
@drinfernoo

drinfernoo Dec 30, 2015

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

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

@AEMLoviji

This comment has been minimized.

Show comment
Hide comment
@AEMLoviji

AEMLoviji Jan 22, 2016

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

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

@mposada

This comment has been minimized.

Show comment
Hide comment
@mposada

mposada Jan 25, 2016

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

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.

Show comment
Hide comment
@davideas

davideas 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

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.

Show comment
Hide comment
@lgarcia95

lgarcia95 Mar 8, 2016

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

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

@walmyrcarvalho

This comment has been minimized.

Show comment
Hide comment
@walmyrcarvalho

walmyrcarvalho Mar 16, 2016

Thanks for the gist, Gabriele, pretty useful! 👍

Thanks for the gist, Gabriele, pretty useful! 👍

@ChristopheVersieux

This comment has been minimized.

Show comment
Hide comment
@ChristopheVersieux

ChristopheVersieux 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!

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.

Show comment
Hide comment
@harshal2711

harshal2711 Jul 23, 2016

How can we perform search in this adapter ?

How can we perform search in this adapter ?

@asadsatti

This comment has been minimized.

Show comment
Hide comment
@asadsatti

asadsatti 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

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.

Show comment
Hide comment
@asadsatti

asadsatti Aug 15, 2016

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

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

@nguyenngan

This comment has been minimized.

Show comment
Hide comment
@nguyenngan

nguyenngan Aug 26, 2016

how to scrollToPosition(position) with RecyclerView thanks

how to scrollToPosition(position) with RecyclerView thanks

@robinvanyang

This comment has been minimized.

Show comment
Hide comment
@robinvanyang

robinvanyang Sep 20, 2016

I think there's something wrong with item decoration

I think there's something wrong with item decoration

@mghouse-mca

This comment has been minimized.

Show comment
Hide comment
@mghouse-mca

mghouse-mca Sep 29, 2016

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

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

@theanilpaudel

This comment has been minimized.

Show comment
Hide comment
@theanilpaudel

theanilpaudel Mar 31, 2017

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

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

@kikermo

This comment has been minimized.

Show comment
Hide comment
@kikermo

kikermo May 1, 2017

Nice, thanks a lot.

kikermo commented May 1, 2017

Nice, thanks a lot.

@milhauscz

This comment has been minimized.

Show comment
Hide comment
@milhauscz

milhauscz 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!

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.

Show comment
Hide comment
@abalta

abalta 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?

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?

@Debdeep009

This comment has been minimized.

Show comment
Hide comment
@Debdeep009

Debdeep009 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. :(

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.

Show comment
Hide comment
@peter1408

peter1408 Jun 14, 2017

how to change the section size at run time

how to change the section size at run time

@VivekRajNei

This comment has been minimized.

Show comment
Hide comment

Thanks...

@SiddarthG

This comment has been minimized.

Show comment
Hide comment
@SiddarthG

SiddarthG Oct 31, 2017

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment