ViewModel Pattern - фрагмент интерфейса обновляется только после поворота экрана - PullRequest
0 голосов
/ 13 октября 2019

Приложение использует фрагменты в сочетании с шаблоном View Model. Во фрагменте будут отображены два Spinners и RecylclerView. Один из Spinner содержит день недели с соответствующими упражнениями, которые должны быть обновлены при изменении выбора дня Spinner. Поэтому я использую метод Livedata.observe(). В начале приложения будут загружены все упражнения текущего дня. Кроме того, к упражнениям будет добавлен еще один элемент в этот список, чтобы создать новое упражнение (теперь я буду называть этот элемент «Добавить упражнение»).

Когда я теперь изменю день недели на другой день, списокбудет обновляться только после того, как я поверну экран. Кроме того, каждый раз, когда экран поворачивается, добавляется дополнительный пункт «Добавить упражнение». Основываясь на этом поведении, я понял, что метод onItemSelected() в Spinner Listener будет активироваться при каждом повороте экрана, даже если не было взаимодействия с элементом Spinner.

Кроме того, все загруженные данные ивыбор элементов Spinner будет потерян, если я перейду на другой фрагмент (как я понял ViewModel, это именно то, что следует предотвратить с помощью класса ViewModel).

Наконец, вот мои вопросы:

  1. Почему экран обновляется только после поворота экрана?
  2. Почему возникают вызовы onItemSelected () элемента Spinner после поворота экрана, даже если не былокакие-либо взаимодействия с элементом счетчика?
  3. Почему я теряю фрагмент или, точнее, данные ViewModel после переключения на другой фрагмент?

Извлечение из HomeViewModel:

public class HomeViewModel extends ViewModel {

    private static final String TAG = "HomeViewModel";

    private MutableLiveData<List<Exercise>> mExercises;
    private MutableLiveData<List<TrainingPlan>> mTrainingPlans;
    private MutableLiveData<Integer> calendarDay;
    private TrainingPlan selectedTrainingPlan;

    final Exercise addExercise = Exercise.builder().name("ADD EXERCISE").sets(null).build();

    private List<TrainingPlan> trainingPlans;
    private List<Exercise> currentExercises;

    public HomeViewModel() {
        mTrainingPlans = new MutableLiveData<>();
        mExercises = new MutableLiveData<>();

        TrainingsPlanDAO trainingsPlanDAO = new DummyTrainingsPlanDAO();
        trainingPlans = trainingsPlanDAO.searchAll();



        setSelectedTrainingPlanToDefaultTrainingPlan(trainingPlans);

        currentExercises = getExercisesOfTheCurrentDay();

        if(currentExercises.isEmpty()){
            Log.i(TAG, "No Exercises for the current day!");
        }

        mTrainingPlans.setValue(trainingPlans);
        mExercises.setValue(currentExercises);


        //additional Exercise in the list for Add function of new Exercise
        currentExercises.add(addExercise);
    }

    public LiveData<List<Exercise>> getExercises() {
        return mExercises;
    }

    public Integer getCurrentDay(){ return getCalendarDay(); }

    public LiveData<List<TrainingPlan>> getTrainingPlans(){ return mTrainingPlans; }


    public void setmExercisesToDay(int day){
        List<Exercise> exercises = getExercisesOfDay(day);
        exercises.add(addExercise);
        mExercises.setValue(exercises);
    }


    private void setSelectedTrainingPlan(TrainingPlan selectedTrainingPlan){
        this.selectedTrainingPlan = selectedTrainingPlan;
    }...

HomeFragment:

public class HomeFragment extends Fragment {

    private HomeViewModel homeViewModel;

    private ExerciseRecyclerViewAdapter exerciseAdapter;
    private ArrayAdapter<CharSequence> weekdayAdapter;
    private ArrayAdapter<TrainingPlan> trainingPlanAdapter;

    View root;


    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        homeViewModel =
                ViewModelProviders.of(this).get(HomeViewModel.class);
        root = inflater.inflate(R.layout.fragment_home, container, false);

        initElements();

        return root;
    }


    private void initElements(){
        initWeekdaySpinner();
        initTrainingPlanSpinner();
        initRecylerView();
    }


    private void initWeekdaySpinner(){
        final Spinner currentDaySpinner = root.findViewById(R.id.currentDay_spinner);

        currentDaySpinner.setOnItemSelectedListener(countrySelected);

        weekdayAdapter = ArrayAdapter.createFromResource(getActivity(), R.array.weekdays, android.R.layout.simple_spinner_item);
        weekdayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        currentDaySpinner.setAdapter(weekdayAdapter);

        currentDaySpinner.setSelection(mapWeekdayFromCalenderDayToSpinnerPosition(homeViewModel.getCurrentDay()));
    }


    private void initTrainingPlanSpinner(){
        final Spinner trainingPlanSpinner = root.findViewById(R.id.currentPlan_spinner);

        trainingPlanAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item, homeViewModel.getTrainingPlans().getValue());
        trainingPlanAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        trainingPlanSpinner.setAdapter(trainingPlanAdapter);
    }


    private void initRecylerView(){
        final RecyclerView recyclerView = root.findViewById(R.id.exercise_recyclerView);

        homeViewModel.getExercises().observe(this, new Observer<List<Exercise>>() {
            @Override
            public void onChanged(@Nullable List<Exercise> mExercises) {
                exerciseAdapter.notifyDataSetChanged();
            }
        });

        exerciseAdapter = new ExerciseRecyclerViewAdapter(getActivity(), homeViewModel.getExercises().getValue());
        RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(this.getContext());
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(exerciseAdapter);
    }


    AdapterView.OnItemSelectedListener countrySelected = new AdapterView.OnItemSelectedListener(){

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            homeViewModel.setmExercisesToDay(mapWeekdayFromSpinnerPositionToCalenderDay(position));
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    };


    private int mapWeekdayFromCalenderDayToSpinnerPosition(int integer){
        return (integer + 5) % 7;
    }


    private int mapWeekdayFromSpinnerPositionToCalenderDay(int integer){
        return (integer + 2) % 7;
    }
}
...