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(); } } }