HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Recycler view with cursor adapter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withviewadapterrecyclercursor

Problem

I am trying to get a recycler viewer with a cursor adapter. I seem to miss important points, so my view gets some of the data repeated instead of all the data at once. Can anyone who knows about this issue review this code and help me find the problem?

```
import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.net.Uri;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.Random;

import amistore.nativeapps.com.amistore.data.ProductContract;

public class RVAdapter extends RecyclerView.Adapter {

private final String LOG_TAG = RVAdapter.class.getName();

// PATCH: Because RecyclerView.Adapter in its current form doesn't natively support
// cursors, we "wrap" a CursorAdapter that will do all teh job
// for us
CursorAdapter mCursorAdapter;

private Cursor mCursor;

protected boolean mDataValid;

private DataSetObserver mDataSetObserver;

private Context mContext;
private ProductViewHolder pvh;
private int mRowIdColumn;
//List products;

public RVAdapter(Context context, Cursor cursor, String locationSetting) {

mContext = context;

// Sort order: Ascending, by date.
String sortOrder = ProductContract.ProductEntry.COLUMN_DATE + " ASC";
Uri productForLocationUri = ProductContract.ProductEntry.buildProductLocationWithStartDate(
locationSetting, System.currentTimeMillis());

// Students: Uncomment the next lines to display what what you stored in the bulkInsert
mCursor = mContext.getContentResolver().query(productForLocationUri,
null, null, null, sortOrder);

Log.d(LOG_TAG, "Const. reached ");
Log.d(LOG_TAG, " mCursor " + Integer.valueOf(mCurso

Solution

First of all CursorAdapter is not designed for use with a RecyclerView. You are trying to hack something in there that would never work right. You can't just call it's methods and expect it to function correctly. See the source.

So first things first. We want to use a cursor.. lets separate off this responsibility and create the RecyclerViewCursorAdapter. It's what it says, a CursorAdapter, for a RecyclerView. The details of this are pretty much the same how CursorAdapter works. See its source to see what is the same and what is not.

Next, we now have your original class RVAdapter to implement the abstract RecyclerViewCursorAdapter which requires us to implement onCreateViewHolder and our new onBindViewHolder which gives us a Cursor parameter to bind against. The details of these views are the same as your original just tidied up a little.

RVAdapter.java

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.Random;

public class RVAdapter extends RecyclerViewCursorAdapter
{
    private static final String TAG = RVAdapter.class.getSimpleName();
    private final Context mContext;
    private final Random mRandom;

    public RVAdapter(Context context, String locationSetting)
    {
        super(null);
        mContext = context;
        mRandom  = new Random(System.currentTimeMillis());

        // Sort order:  Ascending, by date.
        String sortOrder = ProductContract.ProductEntry.COLUMN_DATE + " ASC";
        Uri productForLocationUri = ProductContract.ProductEntry
            .buildProductLocationWithStartDate(locationSetting, System.currentTimeMillis());

        // Students: Uncomment the next lines to display what what you stored in the bulkInsert
        Cursor cursor = mContext.getContentResolver()
            .query(productForLocationUri, null, null, null, sortOrder);

        swapCursor(cursor);
    }

    @Override
    public ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item, parent, false);
        return new ProductViewHolder(view);
    }

    @Override
    protected void onBindViewHolder(ProductViewHolder holder, Cursor cursor)
    {
        String imagePath = cursor.getString(ShopFragment.COL_PRODUCT_IMAGE);
        String price = cursor.getString(ShopFragment.COL_PRODUCT_PRICE);

        holder.productPrice.setText("US $" + price);

        int height = mRandom.nextInt(50) + 500;

        //Download image using picasso library
        Picasso.with(mContext)
            .load(imagePath)
            .resize(500, height)
            .error(R.drawable.placeholder)
            .placeholder(R.drawable.placeholder)
            .into(holder.productPhoto);
    }

    public static class ProductViewHolder extends RecyclerView.ViewHolder
    {
        CardView cv;
        TextView productPrice;
        ImageView productPhoto;

        ProductViewHolder(View itemView)
        {
            super(itemView);
            cv = (CardView) itemView.findViewById(R.id.cv);
            productPrice = (TextView) itemView.findViewById(R.id.product_price);
            productPhoto = (ImageView) itemView.findViewById(R.id.product_photo);
        }
    }
}


RecyclerViewCursorAdapter.java

```
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

/**
* RecyclerView CursorAdapter
*
* Created by Simon on 28/02/2016.
*/
public abstract class RecyclerViewCursorAdapter extends
RecyclerView.Adapter
{
private Cursor mCursor;
private boolean mDataValid;
private int mRowIDColumn;

public RecyclerViewCursorAdapter(Cursor cursor)
{
setHasStableIds(true);
swapCursor(cursor);
}

public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);

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

@Override
public 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);
}

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

@Override
public int getIt

Code Snippets

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.Random;


public class RVAdapter extends RecyclerViewCursorAdapter<RVAdapter.ProductViewHolder>
{
    private static final String TAG = RVAdapter.class.getSimpleName();
    private final Context mContext;
    private final Random mRandom;


    public RVAdapter(Context context, String locationSetting)
    {
        super(null);
        mContext = context;
        mRandom  = new Random(System.currentTimeMillis());

        // Sort order:  Ascending, by date.
        String sortOrder = ProductContract.ProductEntry.COLUMN_DATE + " ASC";
        Uri productForLocationUri = ProductContract.ProductEntry
            .buildProductLocationWithStartDate(locationSetting, System.currentTimeMillis());

        // Students: Uncomment the next lines to display what what you stored in the bulkInsert
        Cursor cursor = mContext.getContentResolver()
            .query(productForLocationUri, null, null, null, sortOrder);

        swapCursor(cursor);
    }

    @Override
    public ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item, parent, false);
        return new ProductViewHolder(view);
    }

    @Override
    protected void onBindViewHolder(ProductViewHolder holder, Cursor cursor)
    {
        String imagePath = cursor.getString(ShopFragment.COL_PRODUCT_IMAGE);
        String price = cursor.getString(ShopFragment.COL_PRODUCT_PRICE);

        holder.productPrice.setText("US $" + price);

        int height = mRandom.nextInt(50) + 500;

        //Download image using picasso library
        Picasso.with(mContext)
            .load(imagePath)
            .resize(500, height)
            .error(R.drawable.placeholder)
            .placeholder(R.drawable.placeholder)
            .into(holder.productPhoto);
    }


    public static class ProductViewHolder extends RecyclerView.ViewHolder
    {
        CardView cv;
        TextView productPrice;
        ImageView productPhoto;

        ProductViewHolder(View itemView)
        {
            super(itemView);
            cv = (CardView) itemView.findViewById(R.id.cv);
            productPrice = (TextView) itemView.findViewById(R.id.product_price);
            productPhoto = (ImageView) itemView.findViewById(R.id.product_photo);
        }
    }
}
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;


/**
 * RecyclerView CursorAdapter
 * <p>
 * Created by Simon on 28/02/2016.
 */
public abstract class RecyclerViewCursorAdapter<VH extends RecyclerView.ViewHolder> extends
    RecyclerView.Adapter<VH>
{
    private Cursor mCursor;
    private boolean mDataValid;
    private int mRowIDColumn;


    public RecyclerViewCursorAdapter(Cursor cursor)
    {
        setHasStableIds(true);
        swapCursor(cursor);
    }

    public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);

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

    @Override
    public 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);
    }

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

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

    protected Cursor getCursor()
    {
        return mCursor;
    }

    public void changeCursor(Cursor cursor)
    {
        Cursor old = swapCursor(cursor);
        if(old != null){
            old.close();
        }
    }

    public Cursor swapCursor(Cursor newCursor)
    {
        if(newCursor == mCursor){
            return null;
        }
        Cursor oldCursor = mCursor;
        if(oldCursor != null){
            if(mDataSetObserver != null){
                oldCursor.unregisterDataSetObserver(mDataSetObserver);
            }
        }
        mCursor = newCursor;
        if(newCursor != null){
            if(mDataSetObserver != null){
                newCursor.registerDataSetObserver(mDataSetObserver);
            }
            mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;
            notifyDataSetChanged();
        }
        else{
            mRowIDColumn = -1;
            mDataValid = false;
            notifyDataSetChanged();
        }
        return oldCursor;
    }


    private DataSetObserver mDataSetObserver = new DataSetObserver()
    {
        @Override
        public void onChanged()
        {
            mDataValid = true;
            notifyDataSetChanged();
        }

        @Override
        public void onInvalidated()
        {
            mDataValid = false;
            notifyDataSetChanged();
        }
    };
}

Context

StackExchange Code Review Q#121353, answer score: 7

Revisions (0)

No revisions yet.