android – Recyclerview + Content provider + CursorLoader-ThrowExceptions

Exception or error:

I have some data in SQLite database.I have a content provider which will get the data from the database. Now the problem is how do i implement cursorLoader to work in hand with recyclerview?

Also, anyone can explain why we need to transfer the data from cursor to an object to display in a listview/recyclerview and not straight from a cursor?

For example, In the custom cursorAdapter class,

Person person = new Person(cursor.getString(cursor.getColumnIndexOrThrow(PERSON_NAME)));
textview.setText = person.getName();

OR

textview.setText = cursor.getString(cursor.getColumnIndexOrThrow(PERSON_NAME));

Which one of the above method is better?

In the past, we used to have listview and gridview and it seems now they are combined to become recyclerview. Then, how do i implement a grid based recyclerview?

How to solve:

In general you should try to separate view and data responsibilities. So what you need is to get all your objects from the database in advance then set up an Adapter which looks like the following:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private final List<Person> objectList = new ArrayList<>();

    @Override
    public CustomAdapter.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        return new ViewHolder(layoutInflater.inflate(R.layout.adapter_item, parent, false));
    }

    @Override
    public void onBindViewHolder(final CustomAdapter.ViewHolder holder, final int position) {
        holder.bindItem(objectList.get(position));
    }

    // Set the persons for your adapter
    public void setItems(final List<Person> persons) {
        objectList.addAll(persons);
        notifyDataSetChanged();
    }

    @Override
    public int getItemCount() {
        return objectList.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView mTextViewTitle;
        private Object mObject;

        public ViewHolder(final View itemView) {
            super(itemView);
            mTextViewTitle = (TextView) itemView.findViewById(R.id.view_item_textViewTitle);                
            mTextViewTitle.setText(mObject.getText());
        }

        private void bindItem(@NonNull final Person object) {
            mObject = object;
        }
    }
}

Then you can bind the adapter to the RecyclerView by:

final CustomAdapter adapter = new CustomAdapter();
adapter.setItems(mPersons);
mRecyclerView.setAdapter();

To answer your second question (“In the past, we used to have listview and gridview and it seems now they are combined to become recyclerview. Then, how do i implement a grid based recyclerview?”):

When binding a LayoutManager to the RecyclerView you can decide which one you take:

final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);

or

final GridLayoutManager layoutManager = new GridLayoutManager(this, COLUMN_SPAN_COUNT);
mRecyclerView.setLayoutManager(layoutManager);

There are several LayoutManagers. Find out more here.

UPDATE:
You don’t have to load all items in advance, just rename setItems to addItems and you are good to go

###

There are several Github gists/projects like this and this which show such a use case.

While you would be using a Adapter which is custom-tailored to a cursor adapter,you would use a GridLayoutManager/LinearLayoutManager as usual for the same.

###

I think you can use directly Custom CursorAdapter for RecyclerView so you do not have to convert Cursor to ArrayList:

public class ProductListAdapter extends RecyclerView.Adapter<ProductListAdapter.ViewHolder> {

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

    Activity mContext;
    Random rnd;

    public ProductListAdapter(AppCompatActivity context, Cursor c) {

        mContext = context;
        rnd = new Random();

        mCursorAdapter = new CursorAdapter(mContext, c, 0) {

            @Override
            public View newView(Context context, Cursor cursor, ViewGroup parent) {
                // Inflate the view here
                LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
                return inflater.inflate(R.layout.row_product_layout_grid, parent, false);
            }

            @Override
            public void bindView(View view, Context context, Cursor cursor) {
                String productName = cursor.getString(cursor.getColumnIndex(TProduct.PRODUCT_NAME));

                // Binding operations
                ((TextView) view.findViewById(R.id.sub_product_name_text_view)).setText(productName);



                int color = Color.argb(200, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));

                String url = "http://dummyimage.com/300/" + color + "/ffffff&text=" + (cursor.getPosition() + 1);

                Picasso
                        .with(context)
                        .load(url)
                        .placeholder(R.mipmap.ic_launcher) // can also be a drawable
                        .into((ImageView) view.findViewById(R.id.sub_product_image_view));
            }
        };
    }

    public void reQuery(Cursor c) {
        if (mCursorAdapter != null) {
            mCursorAdapter.changeCursor(c);
            mCursorAdapter.notifyDataSetChanged();
        }
    }

    @Override
    public int getItemCount() {
        return mCursorAdapter.getCount();
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // Passing the binding operation to cursor loader
        mCursorAdapter.getCursor().moveToPosition(position); //EDITED: added this line as suggested in the comments below, thanks :)
        mCursorAdapter.bindView(holder.view, mContext, mCursorAdapter.getCursor());
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Passing the inflater job to the cursor-adapter
        View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
        return new ViewHolder(v);
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        View view;
        public ViewHolder(View itemView) {
            super(itemView);
            view = itemView.findViewById(R.id.product_row_card_view);
        }
    }
}

May it will be useful to you. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *