Skip to content

Instantly share code, notes, and snippets.

@slidenerd
Last active September 24, 2024 15:14
Show Gist options
  • Save slidenerd/af52fc80c6ca49aa7b1c to your computer and use it in GitHub Desktop.
Save slidenerd/af52fc80c6ca49aa7b1c to your computer and use it in GitHub Desktop.

Revisions

  1. slidenerd revised this gist Sep 17, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion RecyclerViewAdapter.java
    Original file line number Diff line number Diff line change
    @@ -53,7 +53,7 @@ public void setDataSource(Cursor cursor) {
    private void createCursor(Cursor cursor) {
    mCursor = cursor;
    isValid = (cursor != null);
    indexColumnId = isValid ? this.mCursor.getColumnIndex("_id") : -1;
    indexColumnId = isValid ? this.mCursor.getColumnIndex(BaseColumns._ID) : -1;
    notifyItemRangeInserted(getPositionForNotifyItemRangeXXX(), getCount());
    }

  2. slidenerd revised this gist Aug 8, 2015. No changes.
  3. slidenerd revised this gist Aug 8, 2015. No changes.
  4. slidenerd revised this gist Aug 8, 2015. 1 changed file with 127 additions and 176 deletions.
    303 changes: 127 additions & 176 deletions RecyclerViewAdapter.java
    Original file line number Diff line number Diff line change
    @@ -1,115 +1,102 @@
    package slidenerd.vivz.fpam.adapter;

    import android.content.Context;
    import android.database.Cursor;
    import android.support.annotation.Nullable;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;

    import java.util.ArrayList;
    import java.util.List;

    public abstract class RecyclerCursorAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final Context context;
    private final List<ExtraItem> headers;
    private final List<ExtraItem> footers;
    private View emptyView;
    //The cursor object that will contain all the rows that you want to display inside the RecyclerView
    public abstract class RecyclerCursorAdapter<U, V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnSwipeListener {
    //The number of headers to be displayed by default if child classes want a header
    public static final int HEADER_COUNT = 1;
    //The number of footers to be displated by default if child classes want a footer
    public static final int FOOTER_COUNT = 1;
    //A listener that is triggered when items are added or removed to the Recycler View.
    protected OnChangedListener<U> onChangedListener;
    private View mEmptyView;
    //The Cursor object that will contain all the rows that you want to display inside the Recycler View
    private Cursor mCursor;

    //A variable indicating if the data contained in the Cursor above is valid
    private boolean mDataValid;
    private boolean isValid;
    //The index of the column containing _id of an SQLite database table from which you want to load data inside the Cursor above
    private int indexColumnId;

    //the index of the column containing _id of an SQLite database table from which you want to load data inside the Cursor above
    private int mIndexColumnId;

    public RecyclerCursorAdapter(Context context) {
    this.context = context;
    this.headers = new ArrayList<>();
    this.footers = new ArrayList<>();
    public Cursor getCursor() {
    return mCursor;
    }

    /**
    * Check if the data contained by the cursor is valid and try to extract the value of the column index _id
    * To indicate that your RecyclerView needs to refresh what it displays, call notifyDataSetChanged
    *
    * @param cursor the rows from a database table that you want to display inside your RecyclerView
    * @param onChangedListener A class that wishes to get notified when items are added or removed from the Recycler View. When items are added, subclasses must take responsibility of controlling when to fire the 'onAdd' event and when items are swiped to the right in an LTR environment or to the left in an RTL environment, the 'onRemove' event is fired.
    */
    private void createCursor(Cursor cursor) {
    mCursor = cursor;
    mDataValid = cursor != null;
    mIndexColumnId = mDataValid ? mCursor.getColumnIndex("_id") : -1;
    notifyDataSetChanged();
    }

    public Cursor getCursor() {
    return mCursor;
    public void setModifiedListener(OnChangedListener<U> onChangedListener) {
    this.onChangedListener = onChangedListener;
    }

    /**
    * If you are setting the cursor for the first time, create it from scratch, else swap it or change it.
    * If the data source is set for the first time, create a Cursor object from scratch else swap the existing Cursor object with a new cursor object. Depending on whether the Cursor has any rows at all, update the empty view if set.
    *
    * @param cursor containing the rows from your SQLite table that you want to display inside your RecyclerView
    */
    public void setCursor(Cursor cursor) {
    public void setDataSource(Cursor cursor) {
    if (mCursor == null) {
    createCursor(cursor);
    } else {
    changeCursor(cursor);
    swapCursor(cursor);
    }
    toggleEmptyView();
    }

    /**
    * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
    * closed.
    * Check if the data contained by the mCursor is valid and try to extract the value of the column index _id
    * To indicate that your RecyclerView needs to refresh what it displays, call notifyDataSetChanged
    *
    * @param cursor the rows from a database table that you want to display inside your RecyclerView
    */
    public void changeCursor(Cursor cursor) {
    Cursor old = swapCursor(cursor);
    if (old != null) {
    old.close();
    }
    private void createCursor(Cursor cursor) {
    mCursor = cursor;
    isValid = (cursor != null);
    indexColumnId = isValid ? this.mCursor.getColumnIndex("_id") : -1;
    notifyItemRangeInserted(getPositionForNotifyItemRangeXXX(), getCount());
    }

    /**
    * If the new and old cursor are same, do nothing, otherwise store the old and new cursors respectively. If the new cursor is not null, notify that data has changed and mark data as valid
    * Swap in a new Cursor, returning the old Cursor. Unlike
    * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
    * closed.
    * Change the underlying mCursor to a new mCursor. If there is an existing mCursor it will be
    * closed. If the new and old mCursor are same, do nothing, otherwise store the old and new cursors respectively. If the new mCursor is not null, notify that data has changed and mark data as valid. Swap in a new Cursor, returning the old Cursor. Unlike {@link #swapCursor(Cursor)}, the returned old Cursor is <em>not</em> closed.
    */
    public Cursor swapCursor(Cursor newCursor) {
    public void swapCursor(Cursor newCursor) {
    if (newCursor == mCursor) {
    return null;
    return;
    }
    final Cursor oldCursor = mCursor;
    mCursor = newCursor;
    if (mCursor != null) {
    mIndexColumnId = newCursor.getColumnIndexOrThrow("_id");
    mDataValid = true;
    indexColumnId = newCursor.getColumnIndexOrThrow("_id");
    isValid = true;
    notifyDataSetChanged();
    } else {
    mIndexColumnId = -1;
    mDataValid = false;
    notifyDataSetChanged();
    indexColumnId = -1;
    isValid = false;
    notifyItemRangeRemoved(getPositionForNotifyItemRangeXXX(), getCount());
    //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
    }
    return oldCursor;
    if (oldCursor != null) {
    oldCursor.close();
    }
    }

    public Context getContext() {
    return context;
    public int getPositionForNotifyItemRangeXXX() {
    return hasHeader() ? HEADER_COUNT : 0;
    }

    public void setEmptyView(View emptyView) {
    this.emptyView = emptyView;
    emptyView.setVisibility(getCount() == 0 ? View.VISIBLE : View.GONE);
    this.mEmptyView = emptyView;
    }

    public void toggleEmptyView() {
    if (mEmptyView != null)
    mEmptyView.setVisibility(getCount() == 0 ? View.VISIBLE : View.GONE);
    }

    public final int getCount() {
    if (mDataValid && mCursor != null) {
    //The number of data items in your RecyclerView
    return mCursor.getCount();
    }
    return 0;
    return isValid ? mCursor.getCount() : 0;
    }

    /**
    @@ -118,10 +105,10 @@ public final int getCount() {
    */
    @Override
    public long getItemId(int position) {
    if (isItem(position) && mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
    return mCursor.getLong(mIndexColumnId);
    if (isItem(position) && isValid && mCursor.moveToPosition(position)) {
    return mCursor.getLong(indexColumnId);
    }
    return 0;
    return RecyclerView.NO_ID;
    }

    @Override
    @@ -131,146 +118,110 @@ public void setHasStableIds(boolean hasStableIds) {

    @Override
    public final int getItemCount() {
    int count = headers.size();
    count += footers.size();
    count += getCount();
    if (emptyView != null)
    emptyView.setVisibility(getCount() == 0 ? View.VISIBLE : View.GONE);

    return count;
    }

    public ExtraItem addHeaderView(int type, RecyclerView.ViewHolder headerView) {
    ExtraItem item = new ExtraItem(type, headerView);
    addHeaderView(item);
    return item;
    }

    public void addHeaderView(ExtraItem headerView) {
    headers.add(headerView);
    notifyItemInserted(headers.size());
    }

    public void removeHeaderView(int type) {
    List<ExtraItem> indexesToRemove = new ArrayList<>();
    for (int i = 0; i < headers.size(); i++) {
    ExtraItem item = headers.get(i);
    if (item.type == type)
    indexesToRemove.add(item);
    int itemCount = 0;
    if (hasHeader()) {
    itemCount += HEADER_COUNT;
    }

    for (ExtraItem item : indexesToRemove) {
    int index = headers.indexOf(item);
    headers.remove(item);
    notifyItemRemoved(index);
    if (hasFooter()) {
    itemCount += FOOTER_COUNT;
    }
    itemCount += getCount();
    return itemCount;
    }

    public void removeHeaderView(ExtraItem headerView) {
    int indexToRemove = headers.indexOf(headerView);
    if (indexToRemove >= 0) {
    headers.remove(indexToRemove);
    notifyItemRemoved(indexToRemove);
    }
    }

    public ExtraItem addFooterView(int type, RecyclerView.ViewHolder footerView) {
    ExtraItem item = new ExtraItem(type, footerView);
    addFooterView(item);
    return item;
    @Override
    public final int getItemViewType(int position) {
    if (isHeader(position)) {
    return Type.HEADER.ordinal();
    } else if (isFooter(position)) {
    return Type.FOOTER.ordinal();
    } else {
    return Type.ITEM.ordinal();
    }
    }

    public void addFooterView(ExtraItem footerView) {
    footers.add(footerView);
    notifyItemInserted(getItemCount());
    public boolean isHeader(int position) {
    if (hasHeader()) {
    return position == 0 ? true : false;
    } else {
    return false;
    }
    }

    public void removeFooterView(int type) {
    List<ExtraItem> indexesToRemove = new ArrayList<>();
    for (int i = 0; i < footers.size(); i++) {
    ExtraItem item = footers.get(i);
    if (item.type == type)
    indexesToRemove.add(item);
    public boolean isFooter(int position) {
    int headerCount = hasHeader() ? HEADER_COUNT : 0;
    if (hasFooter()) {
    return position > (getCount() + headerCount) ? true : false;
    } else {
    return false;
    }
    }

    for (ExtraItem item : indexesToRemove) {
    int index = footers.indexOf(item);
    footers.remove(item);
    notifyItemRemoved(headers.size() + getCount() + index);
    public boolean isItem(int position) {
    if (!isHeader(position) && !isFooter(position)) {
    return true;
    } else {
    return false;
    }
    }

    public void removeFooterView(ExtraItem footerView) {
    int indexToRemove = footers.indexOf(footerView);
    if (indexToRemove >= 0) {
    footers.remove(indexToRemove);
    notifyItemRemoved(headers.size() + getCount() + indexToRemove);
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == Type.HEADER.ordinal()) {
    return onCreate(parent, Type.HEADER.ordinal());
    } else if (viewType == Type.FOOTER.ordinal()) {
    return onCreate(parent, Type.FOOTER.ordinal());
    } else {
    return onCreate(parent, Type.ITEM.ordinal());
    }
    }

    public int getViewType(int position) {
    return super.getItemViewType(position);
    }

    @Override
    public final int getItemViewType(int position) {
    if (isHeader(position))
    return headers.get(position).type;
    if (isFooter(position))
    return footers.get(position - (headers.size() + getCount())).type;
    return getViewType(position - headers.size());
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (isItem(position)) {
    int headerCount = hasHeader() ? HEADER_COUNT : 0;
    U object = getObjectAt(position - headerCount);
    onBind((V) holder, object, getItemViewType(position));
    }
    }

    public boolean isHeader(int position) {
    return position < headers.size() ? true : false;
    @Nullable
    public U getObjectAt(int position) {
    U object = null;
    if (isValid && mCursor.moveToPosition(position)) {
    object = extractFromCursor(mCursor);
    }
    return object;
    }

    public boolean isFooter(int position) {
    return position >= headers.size() + getCount() ? true : false;
    @Override
    public void onSwipe(int position) {
    int headerCount = hasHeader() ? HEADER_COUNT : 0;
    U object = getObjectAt(position - headerCount);
    if (onChangedListener != null) {
    onChangedListener.onRemove(object);
    }
    }

    public boolean isItem(int position) {
    return position >= headers.size() && position - headers.size() < getCount() ? true : false;
    }
    public abstract boolean hasHeader();

    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    for (ExtraItem item : headers)
    if (viewType == item.type)
    return item.view;
    public abstract boolean hasFooter();

    for (ExtraItem item : footers)
    if (viewType == item.type)
    return item.view;
    public abstract U extractFromCursor(Cursor cursor);

    return onCreateView(parent, viewType);
    }
    public abstract V onCreate(ViewGroup parent, int viewType);

    public abstract V onCreateView(ViewGroup parent, int viewType);
    public abstract void onBind(V holder, U item, int type);

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (isItem(position) && mDataValid && mCursor != null && mCursor.moveToPosition(position))
    //noinspection unchecked
    onBindView((V) holder, mCursor, position - headers.size());
    public enum Type {
    HEADER, ITEM, FOOTER;
    }

    public abstract void onBindView(V view, Cursor cursor, int position);

    public static class ExtraItem {
    public final int type;
    public final RecyclerView.ViewHolder view;
    public interface OnChangedListener<U> {
    public void onAdd(U item);

    public ExtraItem(int type, RecyclerView.ViewHolder view) {
    this.type = type;
    this.view = view;
    }

    public ExtraItem(int type, View view) {
    this.type = type;
    this.view = new RecyclerView.ViewHolder(view) {
    };
    }
    public void onRemove(U item);
    }

    }
  5. slidenerd revised this gist Aug 4, 2015. 1 changed file with 21 additions and 10 deletions.
    31 changes: 21 additions & 10 deletions RecyclerViewAdapter.java
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@
    import java.util.ArrayList;
    import java.util.List;

    public abstract class RecyclerViewAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    public abstract class RecyclerCursorAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final Context context;
    private final List<ExtraItem> headers;
    private final List<ExtraItem> footers;
    @@ -23,7 +23,7 @@ public abstract class RecyclerViewAdapter<V extends RecyclerView.ViewHolder> ext
    //the index of the column containing _id of an SQLite database table from which you want to load data inside the Cursor above
    private int mIndexColumnId;

    public RecyclerViewAdapter(Context context) {
    public RecyclerCursorAdapter(Context context) {
    this.context = context;
    this.headers = new ArrayList<>();
    this.footers = new ArrayList<>();
    @@ -107,8 +107,7 @@ public void setEmptyView(View emptyView) {
    public final int getCount() {
    if (mDataValid && mCursor != null) {
    //The number of data items in your RecyclerView
    int itemCount = mCursor.getCount();
    return itemCount;
    return mCursor.getCount();
    }
    return 0;
    }
    @@ -119,7 +118,7 @@ public final int getCount() {
    */
    @Override
    public long getItemId(int position) {
    if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
    if (isItem(position) && mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
    return mCursor.getLong(mIndexColumnId);
    }
    return 0;
    @@ -215,13 +214,25 @@ public int getViewType(int position) {

    @Override
    public final int getItemViewType(int position) {
    if (position < headers.size())
    if (isHeader(position))
    return headers.get(position).type;
    if (position >= headers.size() + getCount())
    if (isFooter(position))
    return footers.get(position - (headers.size() + getCount())).type;
    return getViewType(position - headers.size());
    }

    public boolean isHeader(int position) {
    return position < headers.size() ? true : false;
    }

    public boolean isFooter(int position) {
    return position >= headers.size() + getCount() ? true : false;
    }

    public boolean isItem(int position) {
    return position >= headers.size() && position - headers.size() < getCount() ? true : false;
    }

    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    for (ExtraItem item : headers)
    @@ -239,12 +250,12 @@ public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int vi

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (position >= headers.size() && (position - headers.size()) < getCount())
    if (isItem(position) && mDataValid && mCursor != null && mCursor.moveToPosition(position))
    //noinspection unchecked
    onBindView((V) holder, position - headers.size());
    onBindView((V) holder, mCursor, position - headers.size());
    }

    public abstract void onBindView(V view, int position);
    public abstract void onBindView(V view, Cursor cursor, int position);

    public static class ExtraItem {
    public final int type;
  6. slidenerd created this gist Aug 4, 2015.
    265 changes: 265 additions & 0 deletions RecyclerViewAdapter.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,265 @@
    package slidenerd.vivz.fpam.adapter;

    import android.content.Context;
    import android.database.Cursor;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;

    import java.util.ArrayList;
    import java.util.List;

    public abstract class RecyclerViewAdapter<V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final Context context;
    private final List<ExtraItem> headers;
    private final List<ExtraItem> footers;
    private View emptyView;
    //The cursor object that will contain all the rows that you want to display inside the RecyclerView
    private Cursor mCursor;

    //A variable indicating if the data contained in the Cursor above is valid
    private boolean mDataValid;

    //the index of the column containing _id of an SQLite database table from which you want to load data inside the Cursor above
    private int mIndexColumnId;

    public RecyclerViewAdapter(Context context) {
    this.context = context;
    this.headers = new ArrayList<>();
    this.footers = new ArrayList<>();
    }

    /**
    * Check if the data contained by the cursor is valid and try to extract the value of the column index _id
    * To indicate that your RecyclerView needs to refresh what it displays, call notifyDataSetChanged
    *
    * @param cursor the rows from a database table that you want to display inside your RecyclerView
    */
    private void createCursor(Cursor cursor) {
    mCursor = cursor;
    mDataValid = cursor != null;
    mIndexColumnId = mDataValid ? mCursor.getColumnIndex("_id") : -1;
    notifyDataSetChanged();
    }

    public Cursor getCursor() {
    return mCursor;
    }

    /**
    * If you are setting the cursor for the first time, create it from scratch, else swap it or change it.
    *
    * @param cursor containing the rows from your SQLite table that you want to display inside your RecyclerView
    */
    public void setCursor(Cursor cursor) {
    if (mCursor == null) {
    createCursor(cursor);
    } else {
    changeCursor(cursor);
    }
    }

    /**
    * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
    * closed.
    */
    public void changeCursor(Cursor cursor) {
    Cursor old = swapCursor(cursor);
    if (old != null) {
    old.close();
    }
    }

    /**
    * If the new and old cursor are same, do nothing, otherwise store the old and new cursors respectively. If the new cursor is not null, notify that data has changed and mark data as valid
    * Swap in a new Cursor, returning the old Cursor. Unlike
    * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
    * closed.
    */
    public Cursor swapCursor(Cursor newCursor) {
    if (newCursor == mCursor) {
    return null;
    }
    final Cursor oldCursor = mCursor;
    mCursor = newCursor;
    if (mCursor != null) {
    mIndexColumnId = newCursor.getColumnIndexOrThrow("_id");
    mDataValid = true;
    notifyDataSetChanged();
    } else {
    mIndexColumnId = -1;
    mDataValid = false;
    notifyDataSetChanged();
    //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
    }
    return oldCursor;
    }

    public Context getContext() {
    return context;
    }

    public void setEmptyView(View emptyView) {
    this.emptyView = emptyView;
    emptyView.setVisibility(getCount() == 0 ? View.VISIBLE : View.GONE);
    }

    public final int getCount() {
    if (mDataValid && mCursor != null) {
    //The number of data items in your RecyclerView
    int itemCount = mCursor.getCount();
    return itemCount;
    }
    return 0;
    }

    /**
    * @param position of the current item within the RecyclerView whose item id we need to specify.
    * @return the index of the column _id from the SQLite database table whose rows you are trying to display inside the RecyclerView, 0 if you dont have valid data in your Cursor
    */
    @Override
    public long getItemId(int position) {
    if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
    return mCursor.getLong(mIndexColumnId);
    }
    return 0;
    }

    @Override
    public void setHasStableIds(boolean hasStableIds) {
    super.setHasStableIds(true);
    }

    @Override
    public final int getItemCount() {
    int count = headers.size();
    count += footers.size();
    count += getCount();
    if (emptyView != null)
    emptyView.setVisibility(getCount() == 0 ? View.VISIBLE : View.GONE);

    return count;
    }

    public ExtraItem addHeaderView(int type, RecyclerView.ViewHolder headerView) {
    ExtraItem item = new ExtraItem(type, headerView);
    addHeaderView(item);
    return item;
    }

    public void addHeaderView(ExtraItem headerView) {
    headers.add(headerView);
    notifyItemInserted(headers.size());
    }

    public void removeHeaderView(int type) {
    List<ExtraItem> indexesToRemove = new ArrayList<>();
    for (int i = 0; i < headers.size(); i++) {
    ExtraItem item = headers.get(i);
    if (item.type == type)
    indexesToRemove.add(item);
    }

    for (ExtraItem item : indexesToRemove) {
    int index = headers.indexOf(item);
    headers.remove(item);
    notifyItemRemoved(index);
    }
    }

    public void removeHeaderView(ExtraItem headerView) {
    int indexToRemove = headers.indexOf(headerView);
    if (indexToRemove >= 0) {
    headers.remove(indexToRemove);
    notifyItemRemoved(indexToRemove);
    }
    }

    public ExtraItem addFooterView(int type, RecyclerView.ViewHolder footerView) {
    ExtraItem item = new ExtraItem(type, footerView);
    addFooterView(item);
    return item;
    }

    public void addFooterView(ExtraItem footerView) {
    footers.add(footerView);
    notifyItemInserted(getItemCount());
    }

    public void removeFooterView(int type) {
    List<ExtraItem> indexesToRemove = new ArrayList<>();
    for (int i = 0; i < footers.size(); i++) {
    ExtraItem item = footers.get(i);
    if (item.type == type)
    indexesToRemove.add(item);
    }

    for (ExtraItem item : indexesToRemove) {
    int index = footers.indexOf(item);
    footers.remove(item);
    notifyItemRemoved(headers.size() + getCount() + index);
    }
    }

    public void removeFooterView(ExtraItem footerView) {
    int indexToRemove = footers.indexOf(footerView);
    if (indexToRemove >= 0) {
    footers.remove(indexToRemove);
    notifyItemRemoved(headers.size() + getCount() + indexToRemove);
    }
    }

    public int getViewType(int position) {
    return super.getItemViewType(position);
    }

    @Override
    public final int getItemViewType(int position) {
    if (position < headers.size())
    return headers.get(position).type;
    if (position >= headers.size() + getCount())
    return footers.get(position - (headers.size() + getCount())).type;
    return getViewType(position - headers.size());
    }

    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    for (ExtraItem item : headers)
    if (viewType == item.type)
    return item.view;

    for (ExtraItem item : footers)
    if (viewType == item.type)
    return item.view;

    return onCreateView(parent, viewType);
    }

    public abstract V onCreateView(ViewGroup parent, int viewType);

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (position >= headers.size() && (position - headers.size()) < getCount())
    //noinspection unchecked
    onBindView((V) holder, position - headers.size());
    }

    public abstract void onBindView(V view, int position);

    public static class ExtraItem {
    public final int type;
    public final RecyclerView.ViewHolder view;

    public ExtraItem(int type, RecyclerView.ViewHolder view) {
    this.type = type;
    this.view = view;
    }

    public ExtraItem(int type, View view) {
    this.type = type;
    this.view = new RecyclerView.ViewHolder(view) {
    };
    }
    }

    }