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

Posted

技术标签:

【中文标题】如何实现“最喜欢的”按钮功能(如最喜欢的食谱/食物)并显示在另一个片段的另一个列表中【英文标题】:How to implement "favourite" button feature (like favourite recipe/food) and display on another list in another fragment 【发布时间】:2021-10-19 14:05:29 【问题描述】:

我想要一个功能,当用户单击某一行上的按钮时,它将将该行添加到另一个列表中,该列表称为收藏列表。目前我已经创建了包含收藏状态的数据库。我已经尝试从创建一个按钮开始,单击该按钮将更改收藏状态。 我还是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;
    

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_
             android:layout_>

    <androidx.cardview.widget.CardView
        android:id="@+id/row_cardView"
        android:layout_
        android:layout_
        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_
            android:layout_
            android:background="@color/white">

            <ImageView
                android:id="@+id/recipe_imageView"
                android:layout_
                android:layout_
                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_
                android:layout_
                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_
                android:layout_
                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_
                android:layout_
                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>

【问题讨论】:

【参考方案1】:

当您尝试使用在使用前未初始化的对象时,可能会出现以下错误。表示它处于空状态,因为您可以看到 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
    
    ....

【讨论】:

以上是关于如何实现“最喜欢的”按钮功能(如最喜欢的食谱/食物)并显示在另一个片段的另一个列表中的主要内容,如果未能解决你的问题,请参考以下文章

使用序列模型训练对话

我正在创建一个最喜欢的 react 列表。我已经能够添加、获取和删除本地存储中的项目。我如何让心形按钮持续存在

POJ 3281 Dining

从表连接中获取唯一的数据对

少吃食品

前端开发最喜欢的30个工具