Skip to content
Advertisement

How to implement “favourite” button feature (like favourite recipe/food) and display on another list in another fragment

I want to have a feature that when the user clicked the button on a certain row, it will add the row to another list which is called favorite list. Currently i have created database that also include favourite status. I already tried to start with creating a button that when its clicked it will change the fav status. Im still new on android studio, just learnt for half a month. So go easy on me. I am currently stuck on this error:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 13778
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.myapplication.data.DatabaseHandler.addRecipe(com.example.myapplication.model.Recipe)' on a null object reference
    at com.example.myapplication.adapter.RecyclerViewAdapter$ViewHolder$1.onClick(RecyclerViewAdapter.java:123)
    at android.view.View.performClick(View.java:7448)
    at android.view.View.performClickInternal(View.java:7425)
    at android.view.View.access$3600(View.java:810)
    at android.view.View$PerformClick.run(View.java:28305)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:223)
    at android.app.ActivityThread.main(ActivityThread.java:7656)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Recipe.java

public class Recipe {
    private int id;
    private String name;
    private String description;
    private String ingredient;
    private int image;
    private String favStatus;
    
    
    public Recipe() {
    }
    
    public Recipe(int id, String name, String description, String ingredient, int image, String favStatus) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.ingredient = ingredient;
        this.image = image;
        this.favStatus = favStatus;
    }
    
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public String getIngredient() {
        return ingredient;
    }
    
    public void setIngredient(String ingredient) {
        this.ingredient = ingredient;
    }
    
    public int getImage() {
        return image;
    }
    
    public void setImage(int image) {
        this.image = image;
    }
    
    public String getFavStatus() {
        return favStatus;
    }
    
    public void setFavStatus(String favStatus) {
        this.favStatus = favStatus;
    }
}

DatabaseHandler.java

public class DatabaseHandler extends SQLiteOpenHelper {

    public DatabaseHandler(Context context) {
        super(context, Util.DATABASE_NAME, null, Util.DATABASE_VERSION);
    }


    //We create our table..
    @Override
    public void onCreate(SQLiteDatabase db) {
        //SQL- Structured Query Language
       /* 
        create table _name(id, name, desc, ingredient, image);
        */
        String CREATE_CONTACT_TABLE = "CREATE TABLE " + Util.TABLE_NAME + "("
                + Util.KEY_ID + " INTEGER PRIMARY KEY," + Util.KEY_NAME + " TEXT,"
                + Util.KEY_DESCRIPTION + " TEXT," + Util.KEY_INGREDIENT + " TEXT,"
                + Util.KEY_IMAGE + " BLOB," + Util.KEY_FAV_STATUS + " TEXT" + ")";
        db.execSQL(CREATE_CONTACT_TABLE); //Creating our table..
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String DROP_TABLE = String.valueOf(R.string.db_drop);
        db.execSQL(DROP_TABLE, new String[]{Util.DATABASE_NAME});

        //Create table again
        onCreate(db);
    }

    //Add Recipe
    public void addRecipe(Recipe recipe) {
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(Util.KEY_NAME, recipe.getName());
        values.put(Util.KEY_DESCRIPTION, recipe.getDescription());
        values.put(Util.KEY_INGREDIENT, recipe.getIngredient());
        values.put(Util.KEY_IMAGE, recipe.getImage());
        values.put(Util.KEY_FAV_STATUS, recipe.getFavStatus());

        //Insert into row..
        db.insert(Util.TABLE_NAME, null, values);
        Log.d("DBHandler", "addRecipe: " + "item added");
        db.close();
    }


    //Get a recipe
    public Recipe getRecipe(int id) {
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(Util.TABLE_NAME,
                new String[] { Util.KEY_ID, Util.KEY_NAME, Util.KEY_DESCRIPTION, Util.KEY_FAV_STATUS,
                        Util.KEY_INGREDIENT, Util.KEY_IMAGE}, Util.KEY_ID +"=?",
                new String[]{String.valueOf(id)},
                null, null, null);

        if (cursor != null)
            cursor.moveToFirst();

        Recipe recipe = new Recipe();
        recipe.setId(Integer.parseInt(cursor.getString(0)));
        recipe.setName(cursor.getString(1));
        recipe.setDescription(cursor.getString(2));
        recipe.setIngredient(cursor.getString(3));
        recipe.setImage(Integer.parseInt(cursor.getString(4)));
        recipe.setFavStatus(cursor.getString(5));

        return recipe;
    }


    //Get all Recipes
    public List<Recipe> getAllRecipes() {
        List<Recipe> recipeList = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        //Select all recipes
        String selectAll = "SELECT * FROM " + Util.TABLE_NAME;
        Cursor cursor = db.rawQuery(selectAll, null);

        //Loop through our data
        if (cursor.moveToFirst()) {
            do {
                Recipe recipe = new Recipe();
                recipe.setId(Integer.parseInt(cursor.getString(0)));
                recipe.setName(cursor.getString(1));
                recipe.setDescription(cursor.getString(2));
                recipe.setIngredient(cursor.getString(3));
                recipe.setImage(Integer.parseInt(cursor.getString(4)));
                recipe.setFavStatus((cursor.getString(5)));

                //add recipe objects to our list
                recipeList.add(recipe);
            }while (cursor.moveToNext());
        }

        return recipeList;
    }


    //Update recipe
    public int updateRecipe (Recipe recipe) {
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(Util.KEY_NAME, recipe.getName());
        values.put(Util.KEY_DESCRIPTION, recipe.getDescription());
        values.put(Util.KEY_INGREDIENT, recipe.getIngredient());
        values.put(Util.KEY_IMAGE, recipe.getImage());
        values.put(Util.KEY_FAV_STATUS, recipe.getFavStatus());

        //Update the row
        return db.update(Util.TABLE_NAME, values, Util.KEY_ID + "=?",
                new String[]{String.valueOf(recipe.getId())});
    }


    //Delete single recipe
    public void deleteRecipe(Recipe recipe) {
        SQLiteDatabase db = this.getWritableDatabase();

        db.delete(Util.TABLE_NAME, Util.KEY_ID + "=?",
                new String[]{String.valueOf(recipe.getId())});
        db.close();
    }

    //Select all favorite list method.
    public List<Recipe> getAllFavRecipes() {
        List<Recipe> recipeList = new ArrayList<>();

        SQLiteDatabase db = this.getReadableDatabase();

        //Select all recipes
        String selectAll = "SELECT * FROM " + Util.TABLE_NAME + " WHERE " + Util.KEY_FAV_STATUS + " ='1'";
        Cursor cursor = db.rawQuery(selectAll, null);

        //Loop through our data
        if (cursor.moveToFirst()) {
            do {
                Recipe recipe = new Recipe();
                recipe.setId(Integer.parseInt(cursor.getString(0)));
                recipe.setName(cursor.getString(1));
                recipe.setDescription(cursor.getString(2));
                recipe.setIngredient(cursor.getString(3));
                recipe.setImage(Integer.parseInt(cursor.getString(4)));
                recipe.setFavStatus((cursor.getString(5)));

                //add recipe objects to our list
                recipeList.add(recipe);
            }while (cursor.moveToNext());
        }

        return recipeList;
    }
}

RecyclerViewAdapter.java

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements Filterable{

    private Context context;
    private List<Recipe> recipeList;
    private List<Recipe> recipeListFull;
    private DatabaseHandler db;


    public RecyclerViewAdapter(Context context, List<Recipe> recipeList) {
        this.context = context;
        this.recipeList = recipeList;
        recipeListFull = new ArrayList<>(recipeList);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.recipe_row, viewGroup, false);

        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
        Recipe recipe = recipeList.get(position); //each recipe object inside of our list

        viewHolder.recipeName.setText(recipe.getName());
        viewHolder.description.setText(recipe.getDescription());
        viewHolder.image.setImageResource(recipe.getImage());
    }

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

    @Override
    public Filter getFilter() {
        return filterRecipe;
    }


    private Filter filterRecipe = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            String searchText = charSequence.toString().toLowerCase();
            List<Recipe> tempList = new ArrayList<>();
            if(searchText.length()==0 | searchText.isEmpty()) {
                tempList.addAll(recipeListFull);
            }else {
                for (Recipe item:recipeListFull) {
                    if (item.getName().toLowerCase().contains(searchText)) {
                        tempList.add(item);
                    }
                }
            }
            FilterResults filterResults = new FilterResults();
            filterResults.values = tempList;

            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults filterResults) {
            recipeList.clear();
            recipeList.addAll((Collection<? extends Recipe>) filterResults.values);
            notifyDataSetChanged();

        }
    };


    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public TextView recipeName;
        public TextView description;
        public ImageView image;
        public ImageView favBtn;

        public ViewHolder(@NonNull  View itemView) {
            super(itemView);

            itemView.setOnClickListener(this);
            recipeName = itemView.findViewById(R.id.name);
            description = itemView.findViewById(R.id.description);
            image = itemView.findViewById(R.id.recipe_imageView);
            favBtn = itemView.findViewById(R.id.fav_image_btn);

            favBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int position = getAdapterPosition();
                    Recipe recipe = recipeList.get(position);

                    if (recipe.getFavStatus().equals("0")) {
                        recipe.setFavStatus("1");
                        db.addRecipe(recipe);
                        favBtn.setImageResource(R.drawable.favourite_star);
                    } else {
                        recipe.setFavStatus("0");
                        db.deleteRecipe(recipe);
                        favBtn.setImageResource(R.drawable.shadow_fav_star);
                    }
                }
            });

        }

        @Override
        public void onClick(View v) {

            int position = getAdapterPosition();
            Recipe recipe = recipeList.get(position);

            Intent intent = new Intent(context, DetailsActivity.class);
            intent.putExtra("name", recipe.getName());
            intent.putExtra("description", recipe.getDescription());
            intent.putExtra("ingredient", recipe.getIngredient());
            intent.putExtra("image", recipe.getImage());

            context.startActivity(intent);

            //Log.d("Clicked", "onClick: " + recipe.getName());

        }
    }

    //Create method to read and check for fav status for every row..
    
}

recipeRow.xml

<?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
             android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:id="@+id/row_cardView"
        android:layout_width="0dp"
        android:layout_height="150dp"
        android:layout_marginStart="1dp"
        android:layout_marginTop="2dp"
        android:layout_marginEnd="1dp"
        app:cardCornerRadius="15dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/white">

            <ImageView
                android:id="@+id/recipe_imageView"
                android:layout_width="200dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="3dp"
                android:scaleType="centerCrop"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:srcCompat="@tools:sample/avatars" />

            <TextView
                android:id="@+id/name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="12dp"
                android:layout_marginTop="7dp"
                android:layout_marginEnd="12dp"
                android:ellipsize="end"
                android:fontFamily="@font/courgette"
                android:maxLines="2"
                android:text="Title Text"
                android:textColor="@color/darker"
                android:textSize="28sp"
                android:textStyle="bold"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.007"
                app:layout_constraintStart_toEndOf="@+id/recipe_imageView"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/description"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:ellipsize="end"
                android:maxLines="3"
                android:text="Desc Text"
                android:textSize="13sp"
                android:textColor="@color/darkGray"
                app:layout_constraintBottom_toTopOf="@+id/fav_image_btn"
                app:layout_constraintEnd_toEndOf="@+id/name"
                app:layout_constraintStart_toStartOf="@+id/name"
                app:layout_constraintTop_toBottomOf="@+id/name" />

            <ImageView
                android:id="@+id/fav_image_btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="25dp"
                android:layout_marginBottom="7dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:srcCompat="@drawable/favourite_star" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

Answer

The following error can occur when you try to use object which is not initialised before using it. means it’s in null state, as you can see your db object in RecyclerViewAdapter is not initialised.

to solve this just initialised the object before using it or check if it’s not null.

in you case just do as

RecyclerViewAdapter.java

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements Filterable{

    ....
    private DatabaseHandler db; // object declared here


    public RecyclerViewAdapter(Context context, List<Recipe> recipeList) {
        this.context = context;
        this.recipeList = recipeList;
        recipeListFull = new ArrayList<>(recipeList);
        db = new DatabaseHandler(context); // db object initialised here
    }
    ....
}
Advertisement