【问题标题】:Why does RecyclerView not get updated from the LiveData in the Viewmodel?为什么 RecyclerView 没有从 Viewmodel 中的 LiveData 获取更新?
【发布时间】:2020-04-12 10:57:22
【问题描述】:

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

【问题讨论】:

    标签: android android-fragments android-livedata android-viewmodel


    【解决方案1】:

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

    【讨论】:

    • 好吧,我修好了,但现在我在这一行得到一个空指针异常。
    • 在 InventoryFragment 中初始化 foodList
    • 您也可以在here查看文章以供参考
    猜你喜欢
    • 2022-01-11
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 2020-01-29
    • 2021-09-26
    • 1970-01-01
    相关资源
    最近更新 更多