I have a question. I'm using android navigation architecture components, using bottom navigation view with singl activity. How can I make a fragment open only once? Even if the button that causes this fragment is clicked several times? How to make a fragment to be added to the back stack only once? I created a test project for try it
xml navigation_test
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/test_nav"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="ru.artem_nr.navigation_test.FirstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" >
<action
android:id="@+id/action_firstFragment_to_secondFrag"
app:destination="@id/navigation"
app:launchSingleTop="true"
app:popUpTo="@+id/secondFrag"
app:popUpToInclusive="false" />
</fragment>
<navigation android:id="@+id/navigation"
app:startDestination="@id/secondFrag">
<fragment
android:id="@+id/secondFrag"
android:name="ru.artem_nr.navigation_test.SecondFrag"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
</navigation>
main_activity
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public Toolbar toolbar;
public DrawerLayout drawerLayout;
public NavController navController;
public NavigationView navigationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupNavigation();
}
private void setupNavigation() {
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
drawerLayout = findViewById(R.id.drawer_layout);
navigationView = findViewById(R.id.nav_view);
navController = Navigation.findNavController(this, R.id.garden_nav_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);
NavigationUI.setupWithNavController(navigationView, navController);
navigationView.setNavigationItemSelectedListener((NavigationView.OnNavigationItemSelectedListener) this);
}
@Override
public boolean onSupportNavigateUp() {
return (boolean) NavigationUI.navigateUp(navController, drawerLayout);
}
@Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
menuItem.setChecked(true);
drawerLayout.closeDrawers();
int id = menuItem.getItemId();
switch (id) {
case R.id.nav_home:
navController.navigate(R.id.navigation);
break;
}
return true;
}
}
fragments - just a blank
As per this issue, BottomNavigationView
offers a OnNavigationItemReselectedListener
that takes precedence over the OnNavigationItemSelectedListener
set by NavigationUI
:
bottomNavigationView.setOnNavigationItemReselectedListener(
new BottomNavigationView.OnNavigationReselectedListener() {
@Override
public void onNavigationItemReselected(MenuItem item) {
// By doing nothing, we ignore reselection events
}
});
NavigationView
does not offer the same interface, as per this other issue, so you'd want to copy the default behavior (note that it calls NavigationUI.onNavDestinationSelected()
and not just call navigate()
directly, unlike your code) and add your own check for isChecked()
to guard against reselection.