Consider the following scenario. I have two fragments, FormFragment
and SelectionFragment
that are hosted by MainActivity
. I have a SharedViewModel
that I plan on using for data communication between the fragments.
FormFragment
has a spinner, tapping on which opens up the SelectionFragment
.
SelectionFragment
consists of a RecyclerView that has a list of Item
objects.
SharedViewModel
has two LiveDatas, LiveData<Item> selectedItem
and LiveData<List<Item>> items
Use case:
- On app launch, user lands on the
FormFragment
which has a spinner with no selected item - When user taps on the spinner, we navigate to
SelectionFragment
SelectionFragment
gets the list of items from theSharedViewModel
and populates the RecyclerView- When user taps on an item, we update the
selectedItem
in theSharedViewModel
and pop theSelectionFragment
from the back stack to go back to theFormFragment
FormFragment
spinner now shows the selected item- User can select a different item by performing the same process
Problem:
After completing steps 1 to 5 above, when we try to choose another item by performing Step 1 again, the observer inside SelectionFragment
immediately fires (since selectedItem
is not null
), and pops the fragment from the back stack. This is obviously not the desired behaviour since we want the user to be able to select a different item at will.
How can I properly observe the selectedItem
inside SelectionFragment
so it doesn't cause its observer to fire immediately?
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);
}
}