I am trying to move my bottom sheet navigation state to a viewmodel. This will enable me to set the tab programmatically and also save the tab state on rotation.
In the viewmodel: enum class NavTab { TAB_1, TAB_2, TAB_3 }
val navigationTab: LiveData<NavigationTab> = savedStateHandle.getLiveData("nav_tab")
fun setNavigationTab(tab: NavigationTab) {
savedStateHandle.set("nav_tab", tab)
}
In the activity:
bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.tab_1:
viewModel.setNavigationTab(NavTab.TAB_1);
break;
case R.id.tab_2:
viewModel.setNavigationTab(NavTab.TAB_2);
break;
case R.id.tab_3:
viewModel.setNavigationTab(NavTab.TAB_3);
break;
}
return true;
});
viewModel.getNavigationTab().observe(this, this::onNavigationTab);
private void onNavigationTab(NavTab tab) {
switch (tab) {
case TAB_1:
bottomNavigationView.setSelectedItemId(R.id.tab_1);
break;
case TAB_2:
bottomNavigationView.setSelectedItemId(R.id.tab_2);
break;
case TAB_3:
bottomNavigationView.setSelectedItemId(R.id.tab_3);
break;
}
}
However the problem is when setting the tab programmatically by calling setNavigationTab only the fragment is switching the tab is not property selected. I can call
bottomNavigationView.setSelectedItemId(R.id.tab_2);
However if I do this, the view model will see the change and fire the observer, causing an infinite loop.
In the case of programmatically changing the tab I need to switch the fragment and the UI tab. In case of user clicking the tab, I only need to set the fragment as the tab UI is changed appropriately.
You can mark tabs as checked without setting the selected item and therefore not calling the selected item listener as follows:
bottomNavigationView.getMenu().getItem(0).setChecked(true);