为什么 RecyclerView 没有从 Viewmodel 中的 LiveData 得到更新?

Why does RecyclerView not get updated from the LiveData in the Viewmodel?

我有一个片段(库存片段)在 RecyclerView 中显示一些 CardView 对象。这些对象在从 ViewModel 接收到数据后从 Adapter 获取数据。在适配器内部,有一些函数可以改变 Livedata。所有这些功能首先打开另一个片段(食物编辑器),并将新数据设置到 ViewModel。

我的问题是,即使在这之后,RecyclerView 也没有得到新对象。我用了notifyDataSetChanged()。我究竟做错了什么?有没有更简单的方法来实现我正在尝试做的事情(添加、删除、修改等)?

库存片段:

package com.coffeetech.kittycatch;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;

public class InventoryFragment extends Fragment {

    //GLOBAL VARIABLES
    RecyclerView recyclerView;
    FoodAdapter foodAdapter;
    FloatingActionButton add_button;
    FrameLayout frameLayout;

    FoodViewModel foodViewModel;

    //FOOD LIST
    private List<Food> foodList;

    public InventoryFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_inventory, container, false);

        //GETTING THE FOOD VIEW MODEL
        foodViewModel = new ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(FoodViewModel.class); //TODO:HERE
        foodList = foodViewModel.getFoods().getValue();
        foodAdapter = new FoodAdapter(foodList);
        recyclerView=v.findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(foodAdapter);

        //SETTING THE FOOD VIEW MODEL
        foodViewModel.getFoods().observe(getActivity(), new Observer<List<Food>>() {
            @Override
            public void onChanged(List<Food> foods) {
                foodAdapter.setFoods(foods);
                foodList=foods;
                foodAdapter.notifyDataSetChanged(); //TODO: MAKE THIS BETTER
            }
        });

       //setting up the frameLayout
        frameLayout = v.findViewById(R.id.food_editor_frame);
        //setting up the Add button
        add_button=v.findViewById(R.id.add_button);
        add_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {  //for new food addition to list
                openFoodEditorFragment(-1);
            }
        });

        foodAdapter.setOnFoodcardClickListener(new FoodAdapter.OnFoodcardClickListener() {
            @Override
            public void deleteFood(int position) { //code that deletes current food
                foodViewModel.delete(foodList.get(position));
                foodAdapter.notifyItemRemoved(position);
            }

            @Override
            public void onEdit(int position) { //code that runs the edit of each food
                openFoodEditorFragment(position);
                foodAdapter.notifyItemChanged(position);
            }


            @Override
            public void decrease(int position) {
                foodList.get(position).decrease();
                foodViewModel.update(foodList.get(position));
                foodAdapter.notifyItemChanged(position);
            }

            @Override
            public void increase(int position) {
                foodList.get(position).increase();
                foodViewModel.update(foodList.get(position));
                foodAdapter.notifyItemChanged(position);
            }

            @Override
            public void setSeekBar(int position,int progress) {
                foodList.get(position).setQuantity(progress);
                foodViewModel.update(foodList.get(position));
                foodAdapter.notifyItemChanged(position);
            }
        });
        return v;
    }

    public void openFoodEditorFragment(int position){ //position = -1 for new, and an integer (position) for edit
        FoodEditorFragment foodEditorFragment;

        switch(position){
            case -1:
                foodEditorFragment = new FoodEditorFragment(foodViewModel);
                break;
            default:
                foodEditorFragment = new FoodEditorFragment(foodList.get(position),foodViewModel);
                break;
        }
        FragmentTransaction transaction=getFragmentManager().beginTransaction();
        transaction.replace(R.id.food_editor_frame,foodEditorFragment);
        transaction.commit();
    }
}

RecyclerView 的适配器:

package com.coffeetech.kittycatch;

import android.app.Application;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.FoodViewHolder> {

    //VARIABLE THAT CONTAINS THE FOOD LIST (array list)
    public List<Food> foods;
    int size;

    private OnFoodcardClickListener onFoodcardClickListener;

    //listener interface
    public interface OnFoodcardClickListener{
        void deleteFood(int position);
        void onEdit(int position);
        void decrease(int position);
        void increase(int position);
        void setSeekBar(int position, int progress);
    }

    public void setOnFoodcardClickListener(OnFoodcardClickListener activity){ //this is called in MainActivity
        onFoodcardClickListener=activity;
    }

    public FoodAdapter(List<Food>foods){
        this.foods=foods;
    }


    public static class FoodViewHolder extends RecyclerView.ViewHolder{
        //VARIABLES FOR EACH WIDGET IN FOODCARD
        TextView name,quantity; //to modify according to current food
        ImageButton decreaseButton,increaseButton; //to hide or show
        SeekBar seekbar;                        //according to current type
        ImageButton deleteButton,editButton;

        public FoodViewHolder(@NonNull View itemView, final OnFoodcardClickListener listener) {//'itemView' is the card
            super(itemView);
            name=itemView.findViewById(R.id.name);
            quantity=itemView.findViewById(R.id.quantity);
            decreaseButton=itemView.findViewById(R.id.decrease_button);
            increaseButton=itemView.findViewById(R.id.increase_button);
            seekbar=itemView.findViewById(R.id.seekbar);
            deleteButton=itemView.findViewById(R.id.delete_button);
            editButton=itemView.findViewById(R.id.edit_button);

            editButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(listener!=null){
                        int position=getAdapterPosition();
                        if (position!= RecyclerView.NO_POSITION){
                            listener.onEdit(position);
                        }
                    }
                }
            });

            deleteButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(listener!=null){
                        int position=getAdapterPosition();
                        if (position!= RecyclerView.NO_POSITION){
                            listener.deleteFood(position);
                        }
                    }
                }
            });

            decreaseButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(listener!=null){
                        int position=getAdapterPosition();
                        if (position!= RecyclerView.NO_POSITION){
                            listener.decrease(position);
                        }
                    }
                }
            });

            increaseButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(listener!=null){
                        int position=getAdapterPosition();
                        if (position!= RecyclerView.NO_POSITION){
                            listener.increase(position);
                        }
                    }
                }
            });

            seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {

                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    if(listener!=null){
                        int position=getAdapterPosition();
                        if (position!= RecyclerView.NO_POSITION){
                            listener.setSeekBar(position,seekBar.getProgress());
                        }
                    }
                }
            });
        }
    }

    @NonNull
    @Override
    public FoodViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.foodcard, parent, false); //inflating Foodcard
        FoodViewHolder vw = new FoodViewHolder(v,onFoodcardClickListener);
        return vw;
    }

    @Override
    public void onBindViewHolder(@NonNull FoodViewHolder holder, int position) { //'holder' is the foodcard here
        Food currentFood=foods.get(position);
        holder.name.setText(currentFood.getName());
        holder.quantity.setText(String.valueOf(currentFood.getQuantity()));
        holder.seekbar.setProgress(currentFood.getQuantity());

        //code to hide or show certain widgets based on food type

        if(currentFood.getType()==0){ //for discrete food
            holder.increaseButton.setVisibility(View.VISIBLE);
            holder.decreaseButton.setVisibility(View.VISIBLE);
            holder.quantity.setVisibility(View.VISIBLE);
            holder.seekbar.setVisibility(View.GONE);
        }else{// for continuous food
            holder.increaseButton.setVisibility(View.GONE);
            holder.decreaseButton.setVisibility(View.GONE);
            holder.quantity.setVisibility(View.GONE);
            holder.seekbar.setVisibility(View.VISIBLE);
        }
    }

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

    //FUCNTION TO GET LIVE DATA HERE
    public void setFoods (List<Food> foods){
        this.foods=foods;
        notifyDataSetChanged();
    }
}

美食编辑片段:

package com.coffeetech.kittycatch;

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;


public class FoodEditorFragment extends Fragment {

    private TextView name,quantity,min_quantity;
    private ImageButton save,cancel;
    private RadioGroup radioGroup;

    protected int t,mode;
    protected Food food;
    FoodViewModel foodViewModel;

    public FoodEditorFragment() {
        // Required empty public constructor
    }

    public FoodEditorFragment (Food food, FoodViewModel foodViewModel){
        this.food=food;
        this.foodViewModel=foodViewModel;
        mode=1;
    }

    public FoodEditorFragment (FoodViewModel foodViewModel){
        this.foodViewModel=foodViewModel;
        this.food = new Food();
        mode=0;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_food_editor, container, false);
        name=view.findViewById(R.id.name_editor);
        quantity=view.findViewById(R.id.quantity_editor);
        min_quantity=view.findViewById(R.id.min_quantity_editor);
        save=view.findViewById(R.id.save_button_editor);
        cancel=view.findViewById(R.id.cancel_button_editor);
        radioGroup=view.findViewById(R.id.radioGroup_editor);

        if (mode==1){ //for editing Food
            //CODE TO SETUP EDITOR ACCORDING TO INITIAL DETAILS

            name.setText(food.getName());
            quantity.setText(String.valueOf(food.getQuantity()));
            min_quantity.setText(String.valueOf(food.getMin_quantity()));
            t=food.getType();

            if(t==0){//for discrete food
                radioGroup.check(R.id.discrete_radioButton);
            }else{//for cont food
                radioGroup.check(R.id.cont_radioButton);
            }

        }
        setButtons();
        return view;
    }


    public void setButtons(){
        save.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {  //USE BELOW 'food' TO PASS NEW DATA TO ACTIVITY
                 try {
                        if ((!name.getText().toString().isEmpty()) && ((radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) || (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton))) {
                            food.setName(name.getText().toString());
                            food.setQuantity(Integer.parseInt(quantity.getText().toString()));
                            food.setMin_quantity(Integer.parseInt(min_quantity.getText().toString()));

                            if (radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) {
                                food.setType(0);
                            } else if (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton) {
                                food.setType(1);
                            }

                            switch (mode){
                                case 0:
                                    foodViewModel.insert(food);
                                    break;
                                case 1:
                                    foodViewModel.update(food);
                                    break;
                            }
                            //CLOSE THE FRAGMENT
                            getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
                        } else {
                            throw new Exception();
                        }
                    }catch (Exception e){
                        Toast.makeText(getContext(),"Please set all details",Toast.LENGTH_SHORT).show();
                    }
            }

        });

        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { //CODE IF USER PRESSES ON CANCEL
                //CLOSE THE FRAGMENT
                getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
            }
        });
    }
}

ViewModel 我正在使用:

package com.coffeetech.kittycatch;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import java.util.ArrayList;
import java.util.List;


public class FoodViewModel extends AndroidViewModel {

    private FoodRepository repository;
    private LiveData<List<Food>> foods;

    public FoodViewModel(@NonNull Application application) {
        super(application);
        repository = new FoodRepository(application);
        foods=repository.getAll();
    }


    public void insert(Food food){
        repository.insert(food);
    }

    public void update(Food food){
        repository.update(food);
    }

    public void delete(Food food){
        repository.delete(food);
    }

    public void deleteAll(){
        repository.deleteAll();
    }

    public LiveData<List<Food>> getFoods(){
        return foods;
    }

    public LiveData<List<Food>> getBuying () {return repository.getBuying();}
}

在 FoodAdapter 的 getItemCount() 方法中,您将 itemcount 设置为静态。您必须将其更改为 foods.size()