Представление навигации Androidx - `setNavigationItemSelectedListener` не работает

Что я делаю?

Я пытался работать с Androidx Navigation Drawer (<com.google.android.material.navigation.NavigationView>). Я прочитал документацию здесь, в которой говорится, что для обработки выбора элементов мы можно использовать setNavigationItemSelectedListener.

Примечание. Я также использую компонент навигации JetPack.

Ниже приведен файл 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>

Вот: 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();
        }
    }

}

Ситуация:

Все отображается нормально, я получаю Drawer во время выполнения, я также получаю Hamburger, он отлично работает, чтобы отображать NavigationView с элементами меню.

Ниже приведен файл 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>

Проблема:

При нажатии на элементы меню он не реагирует на мои события кликов, также известные как onNavigationItemSelected. Как вы можете видеть мой MainActivity.java, Toast не отображается, и ни один из идентификаторов меню не работает внутри переключателя.

Я пробовал много примеров и разных способов сделать это.

Есть ли способ заставить элементы меню реагировать на выбранные мной события?

Если вам нужна дополнительная информация об этом, пожалуйста, оставьте комментарий ниже.

Большое спасибо за помощь.


person Unt    schedule 03.11.2018    source источник


Ответы (6)


Я разобрался, ребята.

На всякий случай, если это кому-то нужно, я размещаю это здесь.

Вместо этого:

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

Я изменил на:

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

И это сработало, может быть, нам нужно все настроить, прежде чем прикреплять файл onNavigationSelector.

person Unt    schedule 03.11.2018
comment
означает, что вы не используете следующую строку. NavigationUI.setupWithNavController(navigationView, navController); вы можете удалить это. - person DjP; 31.12.2018
comment
спасибо за публикацию, это решило мою проблему! - мне потребовалось некоторое время, чтобы найти хороший пример - person Ivan; 01.02.2019
comment
Буквально глупо, что я делал (я не думал об этом в течение 2 дней :( ).. но мы устанавливаем слушателя для навигации. В идеале это должно работать. Код Kotlin (пример из лаборатории кода) имеет то же самое.. он не следовал этому предложенному порядку. Не знаю, почему такое поведение. Спасибо за ваш ответ. - person Nikesh K; 14.04.2019
comment
Спасибо за это, еще один дурак присоединяется к кораблю! - person ZShock; 21.04.2019
comment
Как упоминал @Sanlok Lee в другом ответе, это не сработает, вы не можете подключить несколько слушателей к navigationView. - person Mahdi Javaheri; 24.07.2019
comment
@MahdiJavaheri Где несколько слушателей? - person Ümañg ßürmån; 25.07.2019
comment
@Ümañgßürmån NavigationUI.setupWithNavController(navigationView, navController); установить слушателя внутри функции - person Mahdi Javaheri; 25.07.2019

Возможно, вы не поняли всего побочного эффекта переключения порядка.

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

NavigationUI внутренне прикрепляет NavigationView.OnNavigationItemSelectedListener к NavigationView в Line1. Вы переопределяете этот слушатель своим пользовательским слушателем в Line 2.

Это означает, что navController не будет работать, и вам придется вручную обрабатывать все действия навигации в своем пользовательском прослушивателе. Таким образом, полное решение может быть чем-то вроде:

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

Примечание. Возможно, вы все же захотите вызвать setupWithNavController перед присоединением вашего пользовательского прослушивателя, потому что он делает что-то еще, кроме прикрепления прослушивателя щелчка элемента навигации.

person Sanlok Lee    schedule 06.01.2019

В случае, если кто-то все еще ищет ответ, как иметь оба: NavigationController для обработки элементов NavigationView, а также для выполнения определенных действий внутри NavigationView.

В меню настроек файла menu.xml как обычно:

   <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>

Сначала определите метод onClick "shareApp" для пункта меню "share_app". Затем в вашей деятельности создайте такой метод:

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..."))
}

Не забудьте прикрепить Toolbar, NavigationView и DrawerLayout к NavController в методе override fun onCreate(savedInstanceState: Bundle?) (или где угодно)

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)

}
person Šemsudin Tafilović    schedule 09.09.2019
comment
Я поставил приватный модификатор, теперь он работает! Спасибо - person Joseph Wambura; 14.09.2019
comment
Спасибо, это то, что я искал: просто есть ссылка в навигационном представлении! - person Stephane Mathis; 21.09.2019
comment
Какая экономия времени! Хотя setupWithNavController действительно должен принимать пользовательских слушателей в качестве аргументов. - person Vas; 27.09.2019
comment
Я не думаю, что это решение работает. Кажется, что setupWithNavController(nav_view, navController) и android:onClick не могут сосуществовать. Если я установлю android:onClick, действие, которое должно быть связано с элементом меню в NavGraph, не будет запущено. - person hata; 05.02.2021

Попробуйте это, надеюсь, это идеальное решение. Просто напишите ниже код, чтобы перейти к вашим фрагментам.

@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;

}
person Sujeet    schedule 15.07.2020

MainActivity должен реализовать OnNavigationItemSelectedListener, ваш код должен выглядеть так

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
...
}
person Ekeuwei    schedule 17.12.2020

Правильный порядок

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

Если вы не выполните сначала navView.setupWithNavController, NavView.setNavigationItemSelectedListener не вступит в силу.

person lathesky    schedule 17.06.2021