androiddatabasesqliteandroid-recyclerviewandroid-cursorloader

Blank main activity due to recyclerview with a cursorloader


Right now I am trying to use recyclerview with a cursorloader. I included the cursorloader within my recyclerview adapter based on my research. I do not have the desire to put my SQLite database data into an arraylist. Right now it looks like my code is correct but when I load the app I get a blank screen. Can anyone help me see my mistake in my code?

Here is my adapter:

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

private CursorAdapter mCursorAdapter;
private Context mContext;
private ViewHolder holder;
Cursor prescriptionCursor;




public PrescriptionRecyclerAdapter(Context context, Cursor c) {
    mContext = context;
    prescriptionCursor = c;
    mCursorAdapter = new CursorAdapter(mContext, c, 0) {




        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            // Inflate the view here

                    View v = LayoutInflater.from(context)
                    .inflate(R.layout.recycle_item, parent, false);
                return v;

        }




        @Override
        public void bindView(View view, Context context, Cursor cursor) {



            // Extract data from the current store row and column
            int nameColumnIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry.COLUMN_PRESCRIPTION_NAME);
            int amountColumnIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry.COLUMN_PRESCRIPTION_AMOUNT);
            int durationColumnIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_DURATION);
            final int columnIdIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry._ID);

            //Read the store attritubes from the Cursor for the current stores
            String name = cursor.getString(nameColumnIndex);
            String amount = cursor.getString(amountColumnIndex);
            String duration = cursor.getString(durationColumnIndex);
            String col = cursor.getString(columnIdIndex);

            // Populate fields with extracted properties
            holder.prescriptionName.setText(name);
            holder.prescriptionAmount.setText(amount);
            holder.prescriptionDays.setText(duration);


        }
    };



}

public static class ViewHolder extends RecyclerView.ViewHolder {

    public TextView prescriptionName;
    public TextView prescriptionAmount;
    public TextView prescriptionDays;
    final public Button prescriptionButton;

    public ViewHolder(View itemView) {
        super(itemView);
        // Find fields to populate in inflated template
        prescriptionName = (TextView) itemView.findViewById(R.id.name);
        prescriptionAmount = (TextView) itemView.findViewById(R.id.amountlist);
        prescriptionDays = (TextView) itemView.findViewById(R.id.daysList);
        prescriptionButton = itemView.findViewById(R.id.scheduleButton);

    }
}

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

public Cursor swapCursor(Cursor cursor) {
    if (prescriptionCursor == cursor) {
        return null;
    }
    Cursor oldCursor = prescriptionCursor;
    this.prescriptionCursor = cursor;
    if (cursor != null) {
        this.notifyDataSetChanged();
    }
    return oldCursor;
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    // Passing the binding operation to cursor loader
    mCursorAdapter.getCursor().moveToPosition(position);
    mCursorAdapter.bindView(holder.itemView, 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);
    holder = new ViewHolder(v);
    return holder;
}
}

Here is my display activity.

 public class DisplayActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{
private static final int PRESCRIPTION_LOADER = 0;
PrescriptionRecyclerAdapter mCursorAdapter;
private RecyclerView.LayoutManager mLayoutManager;





@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_display);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(DisplayActivity.this, EditorActivity.class);
            startActivity(intent);
        }
    });

    RecyclerView prescriptionRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    mLayoutManager = new LinearLayoutManager(getApplicationContext());
    prescriptionRecyclerView.setLayoutManager(mLayoutManager);


    mCursorAdapter = new PrescriptionRecyclerAdapter(this, null);
    prescriptionRecyclerView.setAdapter(mCursorAdapter);


    //Kick off the loader
    getLoaderManager().initLoader(PRESCRIPTION_LOADER,null,this);

}



@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_display, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // User clicked on a menu option in the app bar overflow menu
    switch (item.getItemId()) {
        // Respond to a click on the "Delete all entries" menu option
        case R.id.action_delete_all_entries:
            deleteAllPrescriptions();
            return true;

    }

    return super.onOptionsItemSelected(item);
}

/**
 * Helper method to delete all items in the database.
 */
private void deleteAllPrescriptions() {
    int rowsDeleted = getContentResolver().delete(PrescriptionEntry.CONTENT_URI, null, null);
    Log.v("CatalogActivity", rowsDeleted + " rows deleted from prescription database");
}

@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
    // Since the editor shows all store attributes, define a projection that contains
    // all columns from the store table
    String[] projection = {
            PrescriptionEntry._ID,
            PrescriptionEntry.COLUMN_PRESCRIPTION_NAME,
            PrescriptionEntry.COLUMN_PRESCRIPTION_AMOUNT,
            PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_HOURS,
            PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_TIMES,
            PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_DURATION,
            PrescriptionEntry.COLUMN_PRESCRIPTION_REFILL,
            PrescriptionEntry.COLUMN_PRESCRIPTION_EXPIRATION,
            PrescriptionEntry.COLUMN_PRESCRIPTION_PHARMACIST_NAME,
            PrescriptionEntry.COLUMN_PRESCRIPTION_PHARMACIST_NUMBER,
            PrescriptionEntry.COLUMN_PRESCRIPTION_PHYSICIAN_NAME,
            PrescriptionEntry.COLUMN_PRESCRIPTION_PHYSICIAN_NUMBER};

    // This loader will execute the ContentProvider's query method on a background thread
    return new CursorLoader(this,   // Parent activity context
            PrescriptionEntry.CONTENT_URI,         // Query the content URI for the current store
            projection,             // Columns to include in the resulting Cursor
            null,                   // No selection clause
            null,                   // No selection arguments
            null);                  // Default sort order
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    mCursorAdapter.swapCursor(data);
}



@Override
public void onLoaderReset(Loader<Cursor> loader) {
    mCursorAdapter.swapCursor(null);
}



}

Solution

  • I think i got your problem. After getting new Cursor in onLoadFinished method you are calling PrescriptionRecyclerAdapter's swapCursor() method this method is updating prescriptionCursor Cursor reference. Thats OK. But updating prescriptionCursor will not effect your CursorAdapter. You are actually dependent on CursorAdapter. So you have to update theCursor of your CursorAdapter. Because your mCursorAdapter still holding the old reference of the Cursor you have provided in constructor.

    So use this method to update Cursor reference mCursorAdapter.swapCursor(prescriptionCursor).

    public Cursor swapCursor(Cursor cursor) {
      if (prescriptionCursor == cursor) {
        return null;
      }
    
      Cursor oldCursor = prescriptionCursor;
      this.prescriptionCursor = cursor;
    
      if (cursor != null) {
        this.notifyDataSetChanged();
        // update your Cursor for CursorAdapter
        mCursorAdapter.swapCursor(prescriptionCursor);
      }
    
      return oldCursor;
    }
    

    I think you have made it complex by maintaining two Adapter. You can use RecyclerView.Adapter with List or Cursor. There is not need to make it complex.

    Hope it will help you. Let me know it solve your problem.