package com.marshalchen.ultimaterecyclerview.gridSection;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by hesk on 5/1/16.
*/
public abstract class SectionedRecyclerViewAdapter<H extends RecyclerView.ViewHolder,
VH extends RecyclerView.ViewHolder,
F extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int TYPE_SECTION_HEADER = -1;
public static final int TYPE_SECTION_FOOTER = -2;
public static final int TYPE_ITEM = -3;
private int[] sectionForPosition = null;
private int[] positionWithinSection = null;
private boolean[] isHeader = null;
private boolean[] isFooter = null;
private int count = 0;
public SectionedRecyclerViewAdapter() {
super();
registerAdapterDataObserver(new SectionDataObserver());
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
setupIndices();
}
/**
* Returns the sum of number of items for each section plus headers and footers if they
* are provided.
*/
@Override
public int getItemCount() {
return count;
}
private void setupIndices() {
count = countItems();
allocateAuxiliaryArrays(count);
precomputeIndices();
}
private int countItems() {
int count = 0;
int sections = getSectionCount();
for (int i = 0; i < sections; i++) {
count += 1 + getItemCountForSection(i) + (hasFooterInSection(i) ? 1 : 0);
}
return count;
}
private void precomputeIndices() {
int sections = getSectionCount();
int index = 0;
for (int i = 0; i < sections; i++) {
setPrecomputedItem(index, true, false, i, 0);
index++;
for (int j = 0; j < getItemCountForSection(i); j++) {
setPrecomputedItem(index, false, false, i, j);
index++;
}
if (hasFooterInSection(i)) {
setPrecomputedItem(index, false, true, i, 0);
index++;
}
}
}
private void allocateAuxiliaryArrays(int count) {
sectionForPosition = new int[count];
positionWithinSection = new int[count];
isHeader = new boolean[count];
isFooter = new boolean[count];
}
private void setPrecomputedItem(int index, boolean isHeader, boolean isFooter, int section, int position) {
this.isHeader[index] = isHeader;
this.isFooter[index] = isFooter;
sectionForPosition[index] = section;
positionWithinSection[index] = position;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;
if (isSectionHeaderViewType(viewType)) {
viewHolder = onCreateSectionHeaderViewHolder(parent, viewType);
} else if (isSectionFooterViewType(viewType)) {
viewHolder = onCreateSectionFooterViewHolder(parent, viewType);
} else {
viewHolder = onCreateItemViewHolder(parent, viewType);
}
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int section = sectionForPosition[position];
int index = positionWithinSection[position];
if (isSectionHeaderPosition(position)) {
onBindSectionHeaderViewHolder((H) holder, section);
} else if (isSectionFooterPosition(position)) {
onBindSectionFooterViewHolder((F) holder, section);
} else {
onBindItemViewHolder((VH) holder, section, index);
}
}
@Override
public int getItemViewType(int position) {
if (sectionForPosition == null) {
setupIndices();
}
int section = sectionForPosition[position];
int index = positionWithinSection[position];
if (isSectionHeaderPosition(position)) {
return getSectionHeaderViewType(section);
} else if (isSectionFooterPosition(position)) {
return getSectionFooterViewType(section);
} else {
return getSectionItemViewType(section, index);
}
}
protected int getSectionHeaderViewType(int section) {
return TYPE_SECTION_HEADER;
}
protected int getSectionFooterViewType(int section) {
return TYPE_SECTION_FOOTER;
}
/**
* @param section section id
* @param position position id
* @return type id
*/
protected int getSectionItemViewType(int section, int position) {
return TYPE_ITEM;
}
/**
* Returns true if the argument position corresponds to a header
*
* @param position raw position
* @return bool
*/
public boolean isSectionHeaderPosition(int position) {
if (isHeader == null) {
setupIndices();
}
return isHeader[position];
}
/**
* Returns true if the argument position corresponds to a footer
*
* @param position raw position
* @return bool
*/
public boolean isSectionFooterPosition(int position) {
if (isFooter == null) {
setupIndices();
}
return isFooter[position];
}
protected boolean isSectionHeaderViewType(int viewType) {
return viewType == TYPE_SECTION_HEADER;
}
protected boolean isSectionFooterViewType(int viewType) {
return viewType == TYPE_SECTION_FOOTER;
}
/**
* Returns the number of sections in the RecyclerView
*
* @return total amount of sections
*/
protected abstract int getSectionCount();
/**
* Returns the number of items for a given section
*
* @param section section index
* @return index
*/
protected abstract int getItemCountForSection(int section);
/**
* Returns true if a given section should have a footer
*
* @param section section index
* @return bool
*/
protected abstract boolean hasFooterInSection(int section);
/**
* Creates a ViewHolder of class H for a Header
* *
*
* @param parent context in parent
* @param viewType the view type
* @return the type in hold
*/
protected abstract H onCreateSectionHeaderViewHolder(ViewGroup parent, int viewType);
/**
* Creates a ViewHolder of class F for a Footer
*
* @param parent context in parent
* @param viewType the view type
* @return the type in hold
*/
protected abstract F onCreateSectionFooterViewHolder(ViewGroup parent, int viewType);
/**
* Creates a ViewHolder of class VH for an Item
*
* @param parent context in parent
* @param viewType the view type
* @return the type in hold
*/
protected abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);
/**
* Binds data to the header view of a given section
*
* @param holder type of holder
* @param section section index
*/
protected abstract void onBindSectionHeaderViewHolder(H holder, int section);
/**
* Binds data to the footer view of a given section
*
* @param holder type of holder
* @param section section index
*/
protected abstract void onBindSectionFooterViewHolder(F holder, int section);
/**
* Binds data to the item view for a given position within a section
*
* @param holder holder type
* @param section the section id
* @param position the raw position
*/
protected abstract void onBindItemViewHolder(VH holder, int section, int position);
class SectionDataObserver extends RecyclerView.AdapterDataObserver {
@Override
public void onChanged() {
setupIndices();
}
}
}