Рассмотрим следующий сценарий. У меня есть два фрагмента, FormFragment
и SelectionFragment
, которые размещены на MainActivity
. У меня есть SharedViewModel
, который я планирую использовать для передачи данных между фрагментами.
FormFragment
имеет счетчик, нажатие на которое открывает SelectionFragment
.
SelectionFragment
состоит из RecyclerView, который имеет список Item
объектов.
SharedViewModel
имеет две LiveDatas, LiveData<Item> selectedItem
и LiveData<List<Item>> items
Вариант использования:
- При запуске приложения пользователь приземляется на
FormFragment
, у которого есть счетчик без выбранного элемента - Когда пользователь нажимает на счетчик, мы переходим к
SelectionFragment
SelectionFragment
получает список элементов из SharedViewModel
и заполняет RecyclerView - Когда пользователь нажимает на элемент, мы обновляем
selectedItem
в SharedViewModel
и выталкиваем SelectionFragment
из заднего стека в go обратно к FormFragment
FormFragment
теперь отображается выбранный элемент - Пользователь может выбрать другой элемент, выполнив тот же процесс
Проблема:
После выполнения шагов с 1 по 5 выше, когда мы пытаемся выбрать другой элемент, выполнив Шаг 1 снова, наблюдатель указал e SelectionFragment
немедленно запускается (поскольку selectedItem
не null
) и извлекает фрагмент из заднего стека. Это явно не желаемое поведение, так как мы хотим, чтобы пользователь мог выбирать другой элемент по желанию.
Как правильно наблюдать за selectedItem
внутри SelectionFragment
, чтобы он не вызывал его наблюдателя стрелять сразу?
class FormFragment extends Fragment {
@BindView(R.id.spinner) protected Spinner spinner;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SharedViewModel viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel.class);
viewModel.getSelectedItem().observe(getViewLifecycleOwner(), selectedItem -> {
spinner.setSelected(selectedItem);
});
spinner.setOnClickListener(v -> {
// navigate to SelectionFragment
requireFragmentManager().beginTransaction().replace(R.id.container, new SelectionFragment()).commit();
});
}
}
class SelectionFragment extends Fragment {
@BindView(R.id.recycler_view) protected RecyclerView recyclerView;
private ItemsAdapter adapter = new ItemsAdapter();
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
SharedViewModel viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel.class);
viewModel.getItems().observe(getViewLifecycleOwner(), items -> {
// load items into recycler view
adapter.submitList(new ArrayList<>(items));
});
viewModel.getSelectedItem().observe(getViewLifecycleOwner(), selectedItem -> {
// pop fragment from back stack
requireFragmentManager().popBackStackImmediate();
});
adapter.setOnItemClickedListener(item -> {
// update selected item in view model when user taps on an item in the list
viewModel.setSelectedItem(item);
});
recyclerView.setAdapter(adapter);
}
}
class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selectedItem = new MutableLiveData<>();
private Repository repository;
// repository is injected (unimportant to the problem)
SharedViewModel(Repository repository) {
this.repository = repository;
}
LiveData<List<Item>> getItems() {
return repository.getItems();
}
LiveData<Item> getSelectedItem() {
return selectedItem;
}
void setSelectedItem(Item item) {
selectedItem.setValue(item);
}
}