首页 > 解决方案 > 如何实现“最喜欢的”按钮功能(如最喜欢的食谱/食物)并显示在另一个片段的另一个列表中

问题描述

我想要一个功能,当用户单击某行上的按钮时,它将将该行添加到另一个称为收藏列表的列表中。目前我已经创建了包含收藏状态的数据库。我已经尝试从创建一个按钮开始,单击该按钮将更改收藏状态。我还是android studio的新手,刚学了半个月。所以对我放轻松。我目前陷入此错误:

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)

食谱.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;
    }
}

数据库处理程序.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..
    
}

食谱行.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>

标签: javaandroidandroid-studioandroid-layoutandroid-fragments

解决方案


当您尝试使用在使用之前未初始化的对象时,可能会发生以下错误。表示它处于空状态,因为您可以看到 RecyclerViewAdapter 中的 db 对象未初始化。

为了解决这个问题,只需在使用它之前初始化对象或检查它是否不为空。

在你的情况下,就像

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
    }
    ....
}

推荐阅读