Почему RecyclerView не обновляется из LiveData в модели просмотра?

У меня есть фрагмент (фрагмент инвентаря), который отображает некоторые объекты CardView в RecyclerView. Эти объекты получают свои данные от адаптера после того, как данные были получены от ViewModel. Внутри адаптера есть определенные функции, которые могут изменять 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();}
}


person Captain Woof    schedule 12.04.2020    source источник


Ответы (1)


В методе getItemCount () FoodAdapter вы устанавливаете статическое значение itemcount. Вы должны изменить его на foods.size ()

person Dhrumil Shah    schedule 12.04.2020
comment
Хорошо, я исправил это, но теперь я получаю исключение нулевого указателя в этой самой строке. - person Captain Woof; 12.04.2020
comment
инициализировать foodList в InventoryFragment - person Dhrumil Shah; 12.04.2020
comment
Вы также можете ознакомиться со статьей по адресу здесь - person Dhrumil Shah; 12.04.2020