Search code examples
javaandroidandroid-recyclerviewsimplecursoradapter

How to use RecylerView with Database?


I have a problem that I am sitting on for 3 days and I can't think of anything. Namely, I am writing an app for a restaurant where I have a SaladMeterialFragment activity that displays a CardView in a RecyclerView. Everything works as it should when I use the Salad class and extract data from it. However, I need to rewrite my SaladMaterialFragment to work on data retrieved from DatabaseHelper. The whole problem is that I do not quite know how these downloaded data attach to my adapter that was prepared under RecyclerView. How can I retrieve data from cursors and save them in a String [] array because I need it for just the RecyclerView adapter?

SaladMaterialFragment activates the SaladDetailFragment activity, where I was able to fire up the database and everything works as it should. But I have no idea what it should look like with RecyclerView views? I am a beginner developer and just learning databases, so please be patient and explain the matter. Thank you in advance for your help.

RecyclerAdapter

//I must have a STRING[] and INT[], but cursor return String or int, so how to get data from database in this format: String[] ; int[] ? // Must I change anything in my adapter class, in here? If I want to use a Database?

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;

class RecyclerAdapter extends RecyclerView.Adapter<CaptionedImagesAdapter.ViewHolder> {


    private String[] captions;
    private int[] imageIds;

    private Listener listener;


    public static interface Listener {
        public void onClick(int position);
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        private CardView cardView;
        public ViewHolder(CardView v) {
            super(v);
            cardView=v;
        }
    }



    public RecyclerAdapter(String[] captions, int[] imageIds){
        this.captions = captions;
        this.imageIds = imageIds;
    }

    public void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       CardView cv = (CardView) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.card_captioned_image, parent, false);
        return new ViewHolder(cv);

    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
            CardView cardView = holder.cardView;
            ImageView imageView = (ImageView)cardView.findViewById(R.id.info_image);

            Drawable drawable = cardView.getResources().getDrawable(imageIds[position]);
            imageView.setImageDrawable(drawable);
            imageView.setContentDescription(captions[position]);

            TextView textView = (TextView)cardView.findViewById(R.id.info_text);
            textView.setText(captions[position]);


        cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(listener != null) {
                    listener.onClick(position);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return captions.length;
    }
}

DatabaseHelper

public class DatabaseHelper extends SQLiteOpenHelper {

private static final String DB_NAME = "miodzio";
private static final int DB_VERSION = 1;

public MiodzioDatabaseHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {

    db.execSQL("CREATE TABLE SALAD (_id INTEGER PRIMARY KEY AUTOINCREMENT, "
            + "NAME TEXT, "
            + "IMAGE_RESOURCE_ID INTEGER, "
            + "FAVORITE INTEGER);");

    insertSalad(db, "Sałatka grecka", R.drawable.salatka_grecka, 0);
    insertSalatki(db, "Sałatka z grillowanym kurczakiem", R.drawable.salatka_z_kurczakiem, 0);

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

private static void insertSalad(SQLiteDatabase db, String name, int resourceId, int favorite){

    ContentValues saladValues = new ContentValues();
    saladValues.put("NAME", name);
    saladValues.put("IMAGE_RESOURCE_ID", resourceId);
    saladValues.put("Favorite", favorite);
    db.insert("SALAD", null, saladValues);
}

}

Salad.java

// Currently, data for the SaladMaterialFragment is retrieved from this class and everything works // public class Salad {

    private String name;
    private int imageResourceId;


    public static final Salads[] salad = {
            new Salatki("Sałatka grecka", R.drawable.salatka_grecka),
            new Salatki("Sałatka z grillowanym kurczakiem", R.drawable.salatka_z_kurczakiem)
    };

    private Salatki(String name, int imageResourceId) {
        this.name = name;
        this.imageResourceId = imageResourceId;
    }

    public String getName() {
        return name;
    }

    public int getImageResourceId() {
        return imageResourceId;
    }
}

SaladDeteilActivity

    import android.content.ContentValues;
    import android.content.Intent;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteException;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.os.Bundle;
    import android.support.v4.view.MenuItemCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.ShareActionProvider;
    import android.support.v7.widget.Toolbar;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.CheckBox;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;


    public class SaladDetailActivity extends AppCompatActivity {

        public static final String EXTRA_SALATKI = "salatki";

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



            int salatki = (Integer) getIntent().getExtras().get(EXTRA_SALATKI); 
/*

The old way of retrieving data is class Salad
*/

        /*    String salatkiName = Salatki.salatka[salatki].getName();
            TextView textView = (TextView) findViewById(R.id.salatki_text);
            textView.setText(salatkiName);

            int salatkiImage = Salatki.salatka[salatki].getImageResourceId();
            ImageView imageView = (ImageView) findViewById(R.id.salatki_image);
            imageView.setImageDrawable(getResources().getDrawable(salatkiImage));
            imageView.setContentDescription(salatkiName);*/


        //We retrieve data from the database, everything works in this class!

            try {

                SQLiteOpenHelper databaseHelper = new DatabaseHelper(this);
                SQLiteDatabase db = databaseHelper.getReadableDatabase();

                Cursor cursor = db.query("SALAD",
                        new String[]{"NAME", "IMAGE_RESOURCE_ID", "FAVORITE"},
                        "_id = ?",
                        new String[]{Integer.toString(salatki)},
                        null, null, null);

                if(cursor.moveToNext()) {

                    String nameText = cursor.getString(0);
                    int photoId = cursor.getInt(1);


                    boolean isFavorite = (cursor.getInt(2) == 1);

                    //Display name salad
                    TextView name = (TextView) findViewById(R.id.salatki_text);
                    name.setText(nameText);


                    //Display photo salad

                    ImageView photo = (ImageView) findViewById(R.id.salatki_image);
                    photo.setImageResource(photoId);
                    photo.setContentDescription(nameText);

                    //Check it for the favorite
                    CheckBox favorite = (CheckBox) findViewById(R.id.favorite);
                    favorite.setChecked(isFavorite);
                }

                cursor.close();
                db.close();

            }catch (SQLiteException e){
                Toast.makeText(this, "Baza danych niedostępna!", Toast.LENGTH_SHORT).show();
            }


            // getSupportActionBar().setDisplayHomeAsUpEnabled(true); //przycisk w górę!

            Toolbar myChildToolbarZupy = (Toolbar) findViewById(R.id.my_child_toolbar_salatki_detail);
            setSupportActionBar(myChildToolbarZupy);

            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    }

        public void onFavoriteClicked(View view) {

            int salatki = (Integer) getIntent().getExtras().get(EXTRA_SALATKI);

            CheckBox favorite = (CheckBox) findViewById(R.id.favorite);

            ContentValues salatkiValues = new ContentValues();


            salatkiValues.put("FAVORITE", favorite.isChecked());

            SQLiteOpenHelper databaseHelper = new DatabaseHelper(this);

            SQLiteDatabase db = databaseHelper.getWritableDatabase();
            db.update("SALAD", salatkiValues,
                    "_id = ?", new String[]{Integer.toString(salatki)});
            db.close();
        }
    }

SaladMaterialFragment

* In this class, there is a problem and my question is how to extract data from the database to display a name and a photo if I am using an adapter and a RecyclerView view. Does it matter? I need to convert data from the cursor to String [] and int [], but the cursor does not return these types.*

     import android.app.Fragment;
        import android.content.Intent;
        import android.os.Bundle;
        import android.support.v7.widget.LinearLayoutManager;
        import android.support.v7.widget.RecyclerView;
        import android.view.LayoutInflater;
        import android.view.View;
        import android.view.ViewGroup;


        public class SalatkiMaterialFragment extends Fragment {


            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {


                RecyclerView salatkaRecyler = (RecyclerView) inflater.inflate(R.layout.fragment_salatki_material, container, false);

    /*

    At the moment data is retrieved from the Salad class, I care to be retrieved from DatabaseHelper.
    */

                String[] salatkaName = new String[Salatki.salatka.length];

                for(int i=0; i<salatkaName.length; i++) {
                    salatkaName[i] = Salatki.salatka[i].getName();
                }

                int[] salatkaImage = new int[Salatki.salatka.length];

                for(int i=0; i<salatkaImage.length; i++) {
                    salatkaImage[i] = Salatki.salatka[i].getImageResourceId();
                }


    //This adapter adopts in the constructor type: String [] and int []

                RecyclerAdapter adapter = new RecyclerAdapter(salatkaName, salatkaImage);
                salatkaRecyler.setAdapter(adapter);

                LinearLayoutManager manager = new LinearLayoutManager(inflater.getContext(), LinearLayoutManager.VERTICAL, false);
                salatkaRecyler.setLayoutManager(manager);

                adapter.setListener(new CaptionedImagesAdapter.Listener() {
                    @Override
                    public void onClick(int position) {
                        Intent intent = new Intent(getActivity(), SalatkiDetailActivity.class);
                        intent.putExtra(SalatkiDetailActivity.EXTRA_SALATKI, position);
                        getActivity().startActivity(intent);


/*

Here I will write my code, which I tried to prick. Of course he does not work, but maybe it will serve to better focus on what exactly I mean.
*/

/*

try {

SQLiteOpenHelper databaseHelper = new DatabaseHelper(inflater.getContext());
SQLiteDatabase db = databaseHelper.getRedableDatabase();

Cursor cursor = db.query("SALAD", 
new String[] {"NAME", "IMAGE_RESOURCE_ID"},
null, null, null, null, null);

if(cursor.moveToNext()) {

String name = cursor.getString(0); //But I must have a String[] for RecyclerAdapter?

int photo = cursor.getString(0); ////But I must have a int[] for RecyclerAdapter?
}

db.close();
cursor.close();

}catch(SQLiteException e){
Toast.makeText(inflater.getContext(), "Database does not work!", Toast.LENGTHS_SHORT).show();
}

RecyclerAdapter adapter = new RecyclerAdapter(name, photo); //Here I must have String[] - name AND int[] - resource photo id?

                salatkaRecyler.setAdapter(adapter);

                LinearLayoutManager manager = new LinearLayoutManager(inflater.getContext(), LinearLayoutManager.VERTICAL, false);
                salatkaRecyler.setLayoutManager(manager);

                adapter.setListener(new CaptionedImagesAdapter.Listener() {
                    @Override
                    public void onClick(int position) {
                        Intent intent = new Intent(getActivity(), SalatkiDetailActivity.class);
                        intent.putExtra(SalatkiDetailActivity.EXTRA_SALATKI, position);
                        getActivity().startActivity(intent);


*/
                    }
                });

                return salatkaRecyler;




            }
        }

Solution

  • first of all it's better to create a Salad class to store your salad data in so the rest of your application won't have to deal with raw string and int data

    second you are getting Int and String and not array of those two because the database cursor is reading one line at a time. this is where i suggest u create salad objects and return an array of em

    List<Salad> salads = new ArrayList<>();
    
    while(cursor.moveToNext()){
      String name = cursor.getString(0);
      int photo = cursor.getInt(1);
      boolean isFavorite = (cursor.getInt(2) == 1);
    
      Salad salad = new Salad(name,photo,isFavorite);
      salads.add(salad);
    }
    

    and the recyclerview is ought to be displaying an array of salad rather than an array of strings and ints this way u won't have to rework it should you need to load salad data from other sources