Androidx Navigation View - `setNavigationItemSelectedListener` Doesn't Work
Asked Answered
U

6

17

What am I doing?

I have been trying to work with Androidx Navigation Drawer(<com.google.android.material.navigation.NavigationView>). I've read the documentation Here, which says that for handling item selections we can use setNavigationItemSelectedListener.

Note: I am using JetPack's Navigation Component as well.

Below is: main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark" />

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

    </LinearLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:menu="@menu/drawer_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

Here is: MainActivity.java

import android.os.Bundle;
import android.view.MenuItem;
import android.widget.Toast;

import com.google.android.material.navigation.NavigationView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;

public class MainActivity extends AppCompatActivity {

    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();

    }

    // Setting Up One Time Navigation
    private void setupNavigation() {

        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        drawerLayout = findViewById(R.id.drawer_layout);

        navigationView = findViewById(R.id.navigationView);
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        navController = Navigation.findNavController(this, R.id.nav_host_fragment);

        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

        NavigationUI.setupWithNavController(navigationView, navController);

    }

    @Override
    public boolean onSupportNavigateUp() {
        return NavigationUI.navigateUp(drawerLayout, Navigation.findNavController(this, R.id.nav_host_fragment));
    }

    @Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

}

Situation:

Everything displays fine, I get the Drawer at runtime, I also get the Hamburger, It works fine to display the NavigationView with the menu items.

Below is: drawer_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">

        <item
            android:id="@+id/first"
            android:icon="@mipmap/ic_launcher"
            android:title="First" />

        <item
            android:id="@+id/second"
            android:icon="@mipmap/ic_launcher"
            android:title="Second" />

        <item
            android:id="@+id/third"
            android:icon="@mipmap/ic_launcher"
            android:title="Third" />

    </group>

</menu>

Problem:

On tapping the menu items, it does not respond to my click events, a.k.a onNavigationItemSelected. As you can see my MainActivity.java, the Toast does not appear neither any of the menu ids work inside switch.

I've been trying many examples and different ways to get this done.

Is there any way to make menu items respond to my select events?

If you need any more details on this, please do comment below.

Thank You so much for the Help.

Ulu answered 3/11, 2018 at 13:3 Comment(0)
U
16

I Figured it out guys.

Just in case if someone needs it, I'm posting it here.

Instead of this:

navigationView = findViewById(R.id.navigationView);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
        Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();
        return false;
    }
});

navController = Navigation.findNavController(this, R.id.nav_host_fragment);

NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

NavigationUI.setupWithNavController(navigationView, navController);

I changed to:

navigationView = findViewById(R.id.navigationView);

navController = Navigation.findNavController(this, R.id.nav_host_fragment);

NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

NavigationUI.setupWithNavController(navigationView, navController);

navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
        Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();
        return false;
    }
});

And it worked, may be we need to configure everything before attaching the onNavigationSelector.

Ulu answered 3/11, 2018 at 13:54 Comment(7)
means you doesn't have any use of following line. NavigationUI.setupWithNavController(navigationView, navController); you can remove this.Specs
thanks for posting this, it solved my problem! - it took for me some time to find good exampleMarelya
Literally stupid thing i was doing (i didn't think about this for 2days :( ).. but we are setting the listener to navigationView. Ideally it should work. The Kotlin code (sample from code labs) has the same way.. it didn't follow this suggested order.. Not sure why this behaviour.. Thanks for your answerDopey
Thanks for this, another fool joins the ship!Nebula
As @Sanlok Lee mentioned in other answer, this won't work, you can't have multiple listeners attached to navigationViewIndult
@MahdiJavaheri Where is multiple listener?Rabb
@Ümañgßürmån NavigationUI.setupWithNavController(navigationView, navController); set the listener inside functionIndult
G
14

You might not have understood the full side effect of switching the order.

NavigationUI.setupWithNavController(navigationView, navController); // Line 1
navigationView.setNavigationItemSelectedListener({...}) // Line 2

The NavigationUI internally attaches NavigationView.OnNavigationItemSelectedListener to the NavigationView at Line1. You are overriding that listener with your custom listener in Line 2.

This means navController won't work and you have to handle all of the navigation actions manually in your custom listener. So the full solution might be something along the lines of:

NavigationUI.setupWithNavController(navigationView, navController);

navigationView.setNavigationItemSelectedListener(
        new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {

        // TODO: do stuff
        Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();

        // You need this line to handle the navigation
        boolean handled = NavigationUI.onNavDestinationSelected(menuItem, navController);
        if (handled) {
            ViewParent parent = navigationView.getParent();
            if (parent instanceof DrawerLayout) {
                ((DrawerLayout) parent).closeDrawer(navigationView);
            }
        }

        return handled;
    }
});

Note: You still might want to call setupWithNavController before attaching your custom listener because it does things other than attaching navigation item click listener.

Gulick answered 6/1, 2019 at 22:4 Comment(0)
L
9

In case that someone still looking for answer how to have both: NavigationController to handle NavigationView items, but also to have some specific action inside of the NavigationView.

In menu.xml file setup menu items as usual:

   <menu>
        <item
                android:id="@+id/fragment_settings"
                android:icon="@drawable/ic_settings"
                android:orderInCategory="3"
                android:title="@string/settings" />

        <item
                android:id="@+id/share_app"
                android:icon="@drawable/ic_share"
                android:orderInCategory="4"
                android:onClick="shareApp"
                android:title="@string/share_to_friends" />

        <item
                android:id="@+id/fragment_about"
                android:icon="@drawable/ic_info"
                android:orderInCategory="5"
                android:title="@string/about" />
    </menu>

First define a onClick method "shareApp" for the "share_app" menu item. Then in your activity create method like this:

fun shareApp(item:MenuItem) {
    logD("share app clicked!")
    val intent = Intent(Intent.ACTION_SEND).apply {
        putExtra(Intent.EXTRA_TEXT, "some text to send...")
        type = "text/*"
    }
    startActivity(Intent.createChooser(intent, "Share app..."))
}

Don't forget to attach a Toolbar, NavigationView and DrawerLayout to NavController in the override fun onCreate(savedInstanceState: Bundle?) method of activity (or where ever you want)

override fun onCreate(savedInstanceState: Bundle?) {
    setTheme(R.style.AppTheme)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    setupActionBarWithNavController(navController, drawer_layout)
    setupWithNavController(nav_view, navController)
    setupWithNavController(toolbar, navController, drawer_layout)

}
Leannaleanne answered 9/9, 2019 at 0:27 Comment(4)
I had put private modifier, now it works af! ThanksHardily
Thank you, that's what I was looking for : simply have a link in the navigationview !Compensate
What a timesaver! Although, setupWithNavController should really accept custom listeners as argsUrticaceous
I don't think this solution works. It seems that setupWithNavController(nav_view, navController) and android:onClick cannot coexist. If I set android:onClick, the action which should be associated with the menuItem in NavGraph won't be triggered.Rosalia
S
0

Try this, this one is perfect solution I hope. Simply write below code to navigate to your fragments.

@Override
protected void onCreate(Bundle savedInstanceState) {
    ..........
    ..........
    navigationView.setNavigationItemSelectedListener(this);    
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {

    menuItem.setChecked(true);

    drawerLayout.closeDrawers();

    int id = menuItem.getItemId();

    switch (id) {

        case R.id.first:
            navController.navigate(R.id.firstFragment);// **firstFragment** is the id that is written the file under **res/navigation/mobile_navigation.xml** 
            break;

        case R.id.second:
            navController.navigate(R.id.secondFragment);
            break;

        case R.id.third:
            navController.navigate(R.id.thirdFragment);
            break;

    }
    return true;

}
Sedda answered 15/7, 2020 at 14:17 Comment(0)
U
0

MainActivity should implement OnNavigationItemSelectedListener, your code should look like

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
...
}
Underwrite answered 17/12, 2020 at 7:51 Comment(0)
P
0

The correct order

navView.setupWithNavController(navController)
navView.setNavigationItemSelectedListener {
    Log.e(TAG, "onCreate: ", )
        true
}

If you don't execute navView.setupWithNavController first, NavView.setNavigationItemSelectedListener will not take effect.

Peng answered 17/6, 2021 at 9:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.