Skip to content

Instantly share code, notes, and snippets.

@Ashok-Varma
Forked from slidenerd/RecyclerViewAdapter.java
Last active September 2, 2015 06:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ashok-Varma/3fbb51bd745f03d0c3d4 to your computer and use it in GitHub Desktop.
Save Ashok-Varma/3fbb51bd745f03d0c3d4 to your computer and use it in GitHub Desktop.
A single adapter that supports Cursor + an optional header + optional footer
import android.database.Cursor;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
public abstract class RecyclerCursorAdapter<U, V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//The number of headers to be displayed by default if child classes want a header
public static final int HEADER_COUNT = 2;
//The number of footers to be displated by default if child classes want a footer
public static final int FOOTER_COUNT = 2;
//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 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;
public Cursor getCursor() {
return mCursor;
}
/**
* @param view This view will be automatically Visible when cursor is empty and gone when cursor exists
*/
public void setEmptyView(View view) {
this.mEmptyView = view;
}
/**
* @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.
*/
public void setModifiedListener(OnChangedListener<U> onChangedListener) {
this.onChangedListener = onChangedListener;
}
/**
* 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 setDataSource(Cursor cursor) {
if (mCursor == null) {
createCursor(cursor);
} else {
swapCursor(cursor);
}
toggleEmptyView();
}
/**
* 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
*/
private void createCursor(Cursor cursor) {
mCursor = cursor;
isValid = (cursor != null);
indexColumnId = isValid ? this.mCursor.getColumnIndex("_id") : -1;
notifyItemRangeInserted(getPositionForNotifyItemRangeXXX(), getCount() - 1);
}
/**
* 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 void swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return;
}
final Cursor oldCursor = mCursor;
if (newCursor != null) {
indexColumnId = newCursor.getColumnIndexOrThrow("_id");
isValid = true;
mCursor = newCursor;
notifyDataSetChanged();
} else {
indexColumnId = -1;
isValid = false;
notifyItemRangeRemoved(getPositionForNotifyItemRangeXXX(), getCount()-1);
mCursor = newCursor;
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
if (oldCursor != null && !oldCursor.isClosed()) {
oldCursor.close();
}
}
public int getPositionForNotifyItemRangeXXX() {
return hasHeader() ? HEADER_COUNT : 0;
}
public void toggleEmptyView() {
if (mEmptyView != null)
mEmptyView.setVisibility(getCount() == 0 ? View.VISIBLE : View.GONE);
}
public final int getCount() {
return isValid ? mCursor.getCount() : 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 (isItem(position) && isValid && mCursor.moveToPosition(position)) {
return mCursor.getLong(indexColumnId);
}
return RecyclerView.NO_ID;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
@Override
public final int getItemCount() {
int itemCount = 0;
if (hasHeader()) {
itemCount += HEADER_COUNT;
}
if (hasFooter()) {
itemCount += FOOTER_COUNT;
}
itemCount += getCount();
return itemCount;
}
@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 boolean isHeader(int position) {
if (hasHeader()) {
return position < HEADER_COUNT ? true : false;
} else {
return false;
}
}
public boolean isFooter(int position) {
int headerCount = hasHeader() ? HEADER_COUNT : 0;
if (hasFooter()) {
return position >= (getCount() + headerCount) ? true : false;
} else {
return false;
}
}
public boolean isItem(int position) {
if (!isHeader(position) && !isFooter(position)) {
return true;
} else {
return false;
}
}
@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());
}
}
@Override
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));
}
}
@Nullable
public U getObjectAt(int position) {
U object = null;
if (isValid && mCursor.moveToPosition(position)) {
object = extractFromCursor(mCursor);
}
return object;
}
public void onSwipe(int position) {
int headerCount = hasHeader() ? HEADER_COUNT : 0;
U object = getObjectAt(position - headerCount);
if (onChangedListener != null) {
onChangedListener.onRemove(object);
}
}
public abstract boolean hasHeader();
public abstract boolean hasFooter();
public abstract U extractFromCursor(Cursor cursor);
public abstract V onCreate(ViewGroup parent, int viewType);
public abstract void onBind(V holder, U item, int viewType);
public enum Type {
HEADER, ITEM, FOOTER;
}
public interface OnChangedListener<U> {
public void onAdd(U item);
public void onRemove(U item);
}
}
@Ashok-Varma
Copy link
Author

Example App

Here goes the adapter

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

import com.ashokvarma.test.ImageClass;
import com.ashokvarma.test.R;


public class ImagesListAdapter extends RecyclerCursorAdapter {

    private Context context;
    private LayoutInflater inflater;
    OnClickInterface clickListener;


    public ImagesListAdapter(Context c) {
        context = c;
        inflater = LayoutInflater.from(c);
    }

    @Override
    public boolean hasHeader() {
        return true;
    }

    @Override
    public boolean hasFooter() {
        return true;
    }

    @Override
    public Object extractFromCursor(Cursor cursor) {
        ImageClass image = new ImageClass();
        image.id = cursor.getInt(cursor.getColumnIndex(ImageClass.IMAGE_ID));
        image.path = cursor.getString(cursor.getColumnIndex(ImageClass.IMAGE_PATH));
        image.tag = cursor.getString(cursor.getColumnIndex(ImageClass.IMAGE_TAG));
        return image;
    }

    @Override
    public RecyclerView.ViewHolder onCreate(ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder holder = null;
        if (viewType == Type.ITEM.ordinal()) {
            View view = inflater.inflate(R.layout.image_list_single_page, parent, false);
            holder = new MyViewHolder(view);
        } else if (viewType == Type.HEADER.ordinal()) {
            View view = inflater.inflate(R.layout.image_list_single_page, parent, false);
            holder = new FooterHolder(view);
        } else if (viewType == Type.FOOTER.ordinal()) {
            View view = inflater.inflate(R.layout.image_list_single_page, parent, false);
            holder = new FooterHolder(view);
        }
        return holder;
    }

    @Override
    public void onBind(RecyclerView.ViewHolder holder, Object item, int viewType) {
        ImageClass image = (ImageClass) item;

        if (viewType == Type.ITEM.ordinal()) {
            MyViewHolder myViewHolder = (MyViewHolder) holder;
            myViewHolder.Id.setText(Integer.toString(image.id));
            myViewHolder.Tag.setText(image.tag);
            myViewHolder.Path.setText(image.path);
        } else if (viewType == Type.HEADER.ordinal()) {
        } else if (viewType == Type.FOOTER.ordinal()) {
        }

    }

    class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        final TextView Id;
        final TextView Path;
        final TextView Tag;

        public MyViewHolder(View itemView) {
            super(itemView);
            Id = (TextView) itemView.findViewById(R.id.Id);
            Path = (TextView) itemView.findViewById(R.id.Path);
            Tag = (TextView) itemView.findViewById(R.id.Tag);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            int positionClicked = getLayoutPosition() - getPositionForNotifyItemRangeXXX();
            if (clickListener != null) {
                notifyItemRemoved(positionClicked);
                clickListener.adapterItemClicked(getCursor(), positionClicked, v);
            }
        }

    }

    public void setOnClickListener(OnClickInterface clickListener) {
        this.clickListener = clickListener;
    }

    public interface OnClickInterface {
        void adapterItemClicked(Cursor cursor, int positionClicked, View v);
    }

    class FooterHolder extends RecyclerView.ViewHolder {

        public FooterHolder(View itemView) {
            super(itemView);
        }
    }
}

Activity From which i call

package com.ashokvarma.test;

import android.database.Cursor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

import com.ashokvarma.test.adapters.ImagesListAdapter;
import com.ashokvarma.test.database.ImageTableClass;

public class ImagesTest extends AppCompatActivity implements ImagesListAdapter.OnClickInterface{

    private ImagesListAdapter imagesListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_images_test);

        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);

        Cursor cursor = xxx(abc);

        TextView textView = (TextView) findViewById(R.id.emptyTextView);
        imagesListAdapter = new ImagesListAdapter(this);
        imagesListAdapter.setEmptyView(textView);
        imagesListAdapter.setDataSource(cursor);
        imagesListAdapter.setOnClickListener(this);

        recyclerView.setAdapter(imagesListAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
//        recyclerView.setLayoutManager(new GridLayoutManager(this, 3));

    }

    @Override
    public void adapterItemClicked(Cursor cursor, int positionClicked, View v) {
        cursor.moveToPosition(positionClicked);
        int index = cursor.getInt(cursor.getColumnIndex(ImageClass.IMAGE_ID));
        ImageTableClass.deleteImage(index);
        Cursor newCursor = xxx(abc);
        imagesListAdapter.setDataSource(newCursor);
    }
}

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