Warum funktioniert die Navigation in der Aktivitätsvorlage Navigation Drawer mit Version 2.4.1 nicht?

Lesezeit: 4 Minuten

Benutzer-Avatar
motevalizadeh

(Mit Android Studio 2021.1.1)

Erstellen eines neuen Projekts mit der Aktivität „Navigation Drawer“:

  1. Erstellt eine standardmäßige Android-Anwendung mit der Aktivitätsvorlage „Navigation Drawer“.
  2. Dem Projekt wurde ein Einstellungsfragment hinzugefügt, um das zu testen action_settings Menü- und Konfigurationsmenüpunkte.
  3. Überschrieben onOptionsItemSelected() in MainActivity.java So handhaben Sie das Einstellungsmenü:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Bundle bundle = new Bundle();
        switch (item.getItemId()) {
            case R.id.action_settings:
                Navigation
                    .findNavController(this, R.id.nav_host_fragment_content_main)
                    .navigate(R.id.nav_settings);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

Testen:

Führen Sie das Projekt aus, das Schubladenmenü funktioniert einwandfrei und öffnet Fragmente wie erwartet. Das Problem ist, dass wenn Sie auf das Überlaufmenü klicken, um das Einstellungsfragment zu öffnen, es funktioniert, aber das Schubladenmenü nicht mehr funktioniert, wenn Sie das Home-Fragment öffnen.

Geben Sie hier die Bildbeschreibung ein
Geben Sie hier die Bildbeschreibung ein

Überwachung:

Nach einigen Tests stellte ich fest, dass es an der Abhängigkeitsversion liegt, ein Downgrade auf 2.3.5 von 2.4.1 behebt das Problem.

Stimmt etwas mit meinem Code nicht oder liegt es an einer API-Änderung? Wie gehe ich damit um, ohne downgraden zu müssen?

Zusatzinformation:

In MainActivity‘s onCreate() Methode habe ich folgendes hinzugefügt:

     mAppBarConfiguration = new AppBarConfiguration.Builder(
                R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow, R.id.nav_settings)
                .setOpenableLayout(drawer)
                .build();

app Moduls build.gradle:

plugins {
    id 'com.android.application'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdk 23
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 
            'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    buildFeatures {
        viewBinding true
    }

    buildToolsVersion '32.0.0'
        ndkVersion '23.1.7779620'
    }

    dependencies {
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.5.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
        implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
        implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
        implementation 'androidx.navigation:navigation-fragment:2.4.1'
        implementation 'androidx.navigation:navigation-ui:2.4.1'
        implementation 'androidx.legacy:legacy-support-v4:1.0.0'
        
        testImplementation 'junit:junit:4.13.2'
        androidTestImplementation 'androidx.test.ext:junit:1.1.3'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    }
}

tl/dr: Sie sollten dem folgen Verknüpfen Sie Ziele mit der Dokumentation der Menüpunkte und verwenden NavigationUI.onNavDestinationSelected() um das richtige Verhalten zu bekommen:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    NavController navController = Navigation.findNavController(this,
        R.id.nav_host_fragment_content_main);

    // By calling onNavDestinationSelected(), you always get the right behavior
    return NavigationUI.onNavDestinationSelected(item, navController)
            || super.onOptionsItemSelected(item);
}

Wieso den

Navigation 2.4 verwendet mehrere Backstacks mit jedem Element in Ihrem verknüpft NavigationView gemäß der Fügen Sie eine Navigationsschubladenführung hinzu:

Ab in Navigation 2.4.0-alpha01wird der Status jedes Menüelements gespeichert und wiederhergestellt, wenn Sie setupWithNavController verwenden.

Das bedeutet, dass dem „Home“-Bildschirm ein Backstack zugeordnet ist, der wiederhergestellt wird, wenn Sie auf dieses Symbol tippen, dasselbe gilt für Galerie, Diashow und Einstellungen. So wird der Status für dieses Element gespeichert.

Das bedeutet, dass jedes Tippen auf die Artikel in Ihrer Schublade nicht nur zu diesem Bildschirm navigiert, sondern den gesamten mit diesem Artikel verknüpften Backstack austauscht – alles, zu dem Sie von diesem ersten Bildschirm aus navigiert sind.

Also, wenn Sie anrufen Navigation.findNavController(this, R.id.nav_host_fragment_content_main).navigate(R.id.nav_settings);, tun Sie nicht dasselbe wie das Auswählen des Elements “Einstellungen” in Ihrer Schublade – Sie fügen nur den Bildschirm “Einstellungen” zum hinteren Stapel des Bildschirms “Startseite” hinzu. Aus diesem Grund bewirkt das Antippen des Home-Symbols nichts – Sie befinden sich bereits auf dem Backstack des „Home“-Bildschirms.

Was Sie eigentlich tun möchten, ist, zu dem vollständig separaten Backstack zu wechseln, der dem Element nav_settings zugeordnet ist. Dies würde den nav_settings-Backstack vom Home-Backstack trennen und so sicherstellen, dass Sie durch Antippen des Home-Symbols zum Backstack des Startbildschirms zurückkehren.

Genau das ist die NavigationUI.onNavDestinationSelected() API tut (denn genau das ist es, was die setupWithNavController API verwendet), sodass Sie diese einfach direkt in Ihrer verwenden können onOptionsItemSelected().

Allerdings, wenn Sie manuell anrufen möchten navigate() (was übrigens bedeutet, dass Sie nicht die Überblendungsanimation erhalten, die Sie standardmäßig erhalten, wenn Sie verwenden onNavDestinationSelected), können Sie die hinzufügen Zustandsflaggen speichern zu Ihrer Navigation rufen Sie an Anwenden von NavOptions programmgesteuert:

@Override
 public boolean onOptionsItemSelected(MenuItem item) {
    Bundle bundle =new Bundle();
    switch (item.getItemId()) {
        case R.id.action_settings:
        {
            // Manually build the NavOptions that manually do
            // what NavigationUI.onNavDestinationSelected does for you
            NavOptions navOptions = new NavOptions.Builder()
                .setPopUpTo(R.id.nav_home, false, true)
                .setRestoreState(true)
                .build();

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

            navController.navigate(R.id.nav_settings, navOptions);
            return true;
        }

        default:
            return super.onOptionsItemSelected(item);
    }
}

Notiere dass der setupWithNavController API stützt sich auf die verschachtelter Graph des aktuellen Ziels, um zu bestimmen, welches Element ausgewählt ist – die Erwartung ist, dass alle Ziele auf der Registerkarte „Startseite“ Teil des Navigationsdiagramms „Startseite“ sind. Da Sie also zu den nav_settings gewechselt haben, setupWithNavController davon aus, dass Sie zu diesem Backstack gewechselt haben. Da Sie dies nicht wirklich getan haben, wird Ihr ausgewähltes Element nicht mehr mit dem Backstack synchronisiert, auf dem Sie sich befinden.

1014050cookie-checkWarum funktioniert die Navigation in der Aktivitätsvorlage Navigation Drawer mit Version 2.4.1 nicht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy