Unt
Unt

Reputation: 341

Androidx Navigation View - `setNavigationItemSelectedListener` Doesn't Work

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.

Upvotes: 17

Views: 22246

Answers (6)

lathesky
lathesky

Reputation: 1

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.

Upvotes: 0

Ekeuwei
Ekeuwei

Reputation: 281

MainActivity should implement OnNavigationItemSelectedListener, your code should look like

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
...
}

Upvotes: 0

Sujeet
Sujeet

Reputation: 576

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;

}

Upvotes: 0

Šemsudin Tafilović
Šemsudin Tafilović

Reputation: 925

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)

}

Upvotes: 9

Sanlok Lee
Sanlok Lee

Reputation: 3494

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.

Upvotes: 14

Unt
Unt

Reputation: 341

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.

Upvotes: 16

Related Questions