Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Question about android navigation architecture component

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

like image 933
Артем Артемов Avatar asked May 21 '26 11:05

Артем Артемов


2 Answers

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.

like image 86
ianhanniballake Avatar answered May 23 '26 12:05

ianhanniballake


You need to declare the fragment as "SingleTop".

There is an option on the menu in the right hand side of the navigation graph editor view to do this, below the animation options.

Alternatively you can set it programmatically through the NavOptions.Builder class, using the setLaunchSingleTop(true) method as detailed in the link below:

https://developer.android.com/reference/kotlin/androidx/navigation/NavOptions.Builder#setLaunchSingleTop(kotlin.Boolean)

So your on select method should look like:

public boolean onNavigationItemSelected(@NonNull MenuItem menuItem){
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true);

NavOptions options = builder.build();
navController.navigate(item.getItemId(), null, options);
like image 42
Izzy Stannett Avatar answered May 23 '26 11:05

Izzy Stannett



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!