morristech
5/18/2017 - 6:16 AM

Cursor Adapters for RecyclerView

Cursor Adapters for RecyclerView

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 ARNAUD FRUGIER
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import android.database.Cursor;
import android.support.v7.widget.RecyclerView;

public abstract class CursorRecyclerAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {

	protected boolean mDataValid;
	protected Cursor mCursor;
	protected int mRowIDColumn;

	public CursorRecyclerAdapter(Cursor c) {
		init(c);
	}

	void init(Cursor c) {
		boolean cursorPresent = c != null;
		mCursor = c;
		mDataValid = cursorPresent;
		mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
		setHasStableIds(true);
	}

	@Override
	public final void onBindViewHolder (VH holder, int position) {
		if (!mDataValid) {
			throw new IllegalStateException("this should only be called when the cursor is valid");
		}
		if (!mCursor.moveToPosition(position)) {
			throw new IllegalStateException("couldn't move cursor to position " + position);
		}

		onBindViewHolder(holder, mCursor);
	}

	public abstract void onBindViewHolder(VH holder, Cursor cursor);

	public Cursor getCursor() {
		return mCursor;
	}

	@Override
	public int getItemCount () {
		if (mDataValid && mCursor != null) {
			return mCursor.getCount();
		} else {
			return 0;
		}
	}

	@Override
	public long getItemId (int position) {
		if(hasStableIds() && mDataValid && mCursor != null){
			if (mCursor.moveToPosition(position)) {
				return mCursor.getLong(mRowIDColumn);
			} else {
				return RecyclerView.NO_ID;
			}
		} else {
			return RecyclerView.NO_ID;
		}
	}

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

	/**
	 * Swap in a new Cursor, returning the old Cursor.  Unlike
	 * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
	 * closed.
	 *
	 * @param newCursor The new cursor to be used.
	 * @return Returns the previously set Cursor, or null if there wasa not one.
	 * If the given new Cursor is the same instance is the previously set
	 * Cursor, null is also returned.
	 */
	public Cursor swapCursor(Cursor newCursor) {
		if (newCursor == mCursor) {
			return null;
		}
		Cursor oldCursor = mCursor;
		mCursor = newCursor;
		if (newCursor != null) {
			mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
			mDataValid = true;
			// notify the observers about the new cursor
			notifyDataSetChanged();
		} else {
			mRowIDColumn = -1;
			mDataValid = false;
			// notify the observers about the lack of a data set
			notifyItemRangeRemoved(0, getItemCount());
		}
		return oldCursor;
	}

	/**
	 * <p>Converts the cursor into a CharSequence. Subclasses should override this
	 * method to convert their results. The default implementation returns an
	 * empty String for null values or the default String representation of
	 * the value.</p>
	 *
	 * @param cursor the cursor to convert to a CharSequence
	 * @return a CharSequence representing the value
	 */
	public CharSequence convertToString(Cursor cursor) {
		return cursor == null ? "" : cursor.toString();
	}
}
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 ARNAUD FRUGIER
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

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;

public class SimpleCursorRecyclerAdapter extends CursorRecyclerAdapter<SimpleViewHolder> {

	private int mLayout;
	private int[] mFrom;
	private int[] mTo;
	private String[] mOriginalFrom;

	public SimpleCursorRecyclerAdapter (int layout, Cursor c, String[] from, int[] to) {
		super(c);
		mLayout = layout;
		mTo = to;
		mOriginalFrom = from;
		findColumns(c, from);
	}

	@Override
	public SimpleViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
		View v = LayoutInflater.from(parent.getContext())
				.inflate(mLayout, parent, false);
		return new SimpleViewHolder(v, mTo);
	}

	@Override
	public void onBindViewHolder (SimpleViewHolder holder, Cursor cursor) {
		final int count = mTo.length;
		final int[] from = mFrom;

		for (int i = 0; i < count; i++) {
			holder.views[i].setText(cursor.getString(from[i]));
		}
	}

	/**
	 * Create a map from an array of strings to an array of column-id integers in cursor c.
	 * If c is null, the array will be discarded.
	 *
	 * @param c the cursor to find the columns from
	 * @param from the Strings naming the columns of interest
	 */
	private void findColumns(Cursor c, String[] from) {
		if (c != null) {
			int i;
			int count = from.length;
			if (mFrom == null || mFrom.length != count) {
				mFrom = new int[count];
			}
			for (i = 0; i < count; i++) {
				mFrom[i] = c.getColumnIndexOrThrow(from[i]);
			}
		} else {
			mFrom = null;
		}
	}

	@Override
	public Cursor swapCursor(Cursor c) {
		findColumns(c, mOriginalFrom);
		return super.swapCursor(c);
	}
}

class SimpleViewHolder extends RecyclerView.ViewHolder
{
	public TextView[] views;

	public SimpleViewHolder (View itemView, int[] to)
	{
		super(itemView);
		views = new TextView[to.length];
		for(int i = 0 ; i < to.length ; i++) {
			views[i] = (TextView) itemView.findViewById(to[i]);
		}
	}
}