it-swarm.com.de

Gibt es eine Möglichkeit, Fragment bei der Verwendung von BottomNavigationView mit dem neuen NavController am Leben zu erhalten?

Ich versuche die neue Navigationskomponente zu verwenden. Ich verwende eine BottomNavigationView mit dem navController: NavigationUI.setupWithNavController (bottomNavigation, navController)

Wenn ich jedoch Fragmente wechsle, werden sie jedes Mal zerstört/erstellt, auch wenn sie zuvor verwendet wurden.

Gibt es eine Möglichkeit, unsere wichtigsten Fragmente mit unserer BottomNavigationView zu verbinden?

23
IdAndro

Versuche dies.

Navigator

Erstellen Sie einen benutzerdefinierten Navigator.

@Navigator.Name("custom_fragment")  // Use as custom tag at navigation.xml
class CustomNavigator(
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            fragment = destination.createFragment(args)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commit()

        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED)
    }
}

NavHostFragment

Erstellen Sie ein benutzerdefiniertes NavHostFragment.

class CustomNavHostFragment: NavHostFragment() {
    override fun createFragmentNavigator(): Navigator<out FragmentNavigator.Destination> {
        return CustomNavigator(requireContext(), childFragmentManager, id)
    }
}

navigation.xml

Verwenden Sie ein benutzerdefiniertes Tag anstelle eines Fragment-Tags. 

<navigation xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto" Android:id="@+id/navigation"
    app:startDestination="@id/navigation_first">

    <custom_fragment
        Android:id="@+id/navigation_first"
        Android:name="com.example.sample.FirstFragment"
        Android:label="FirstFragment" />
    <custom_fragment
        Android:id="@+id/navigation_second"
        Android:name="com.example.sample.SecondFragment"
        Android:label="SecondFragment" />
</navigation>

aktivitätslayout

Verwenden Sie CustomNavHostFragment anstelle von NavHostFragment.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/container"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <fragment
        Android:id="@+id/nav_Host_fragment"
        Android:name="com.example.sample.CustomNavHostFragment"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/navigation" />

    <com.google.Android.material.bottomnavigation.BottomNavigationView
        Android:id="@+id/bottom_navigation"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

Aktualisieren

Ich habe ein Beispielprojekt erstellt. Verknüpfung

Ich erstelle kein benutzerdefiniertes NavHostFragment. Ich benutze navController.navigatorProvider += navigator.

25
STAR_ZERO

Nach vielen Stunden der Recherche habe ich eine Lösung gefunden. Es war die ganze Zeit direkt vor uns :) Es gibt eine Funktion: popBackStack(destination, inclusive), die zum angegebenen Ziel navigiert, wenn sie in backStack gefunden wird. Es wird ein Boolescher Wert zurückgegeben, sodass wir manuell dorthin navigieren können, wenn der Controller das Fragment nicht findet.

if(findNavController().popBackStack(R.id.settingsFragment, false)) {
        Log.d(TAG, "SettingsFragment found in backStack")
    } else {
        Log.d(TAG, "SettingsFragment not found in backStack, navigate manually")
        findNavController().navigate(R.id.settingsFragment)
    }
4
Piotr Prus

Google samples link Kopieren Sie einfach NavigationExtensions in Ihre Anwendung und konfigurieren Sie anhand von Beispielen. Funktioniert super.

3
Zakhar Rodionov

Die von @ piotr-prus bereitgestellte Lösung hat mir geholfen, aber ich musste eine aktuelle Zielüberprüfung hinzufügen:

if (navController.currentDestination?.id == resId) {
    return       //do not navigate
}

ohne diese Prüfung wird das aktuelle Ziel wiederhergestellt, wenn Sie versehentlich dorthin navigieren, da es nicht im Backstack gefunden wird.

0
akhris

Ich habe den von @STAR_ZERO bereitgestellten Link verwendet und er funktioniert einwandfrei. Für diejenigen, die Probleme mit der Zurück-Schaltfläche haben, können Sie diese in der Aktivität/nav Host wie folgt behandeln.

override fun onBackPressed() {
        if(navController.currentDestination!!.id!=R.id.homeFragment){
            navController.navigate(R.id.homeFragment)
        }else{
            super.onBackPressed()
        }
    }

Überprüfen Sie einfach, ob das aktuelle Ziel Ihr Root-/Home-Fragment ist (normalerweise das erste in der unteren Navigationsansicht). Wenn nicht, navigieren Sie einfach zurück zum Fragment. Wenn ja, beenden Sie nur die App oder tun Sie, was Sie wollen.

Übrigens muss diese Lösung mit dem von STAR_ZERO bereitgestellten Lösungslink zusammenarbeiten und keep_state_fragment verwenden.

0
veeyikpong

Ab sofort nicht verfügbar.

Als Problemumgehung könnten Sie alle abgerufenen Daten im Ansichtsmodell speichern und diese Daten beim Wiederherstellen des Fragments zur Verfügung haben. Stellen Sie sicher, dass Sie die Ansicht über den Aktivitätskontext erhalten.

Sie können LiveData verwenden, um Ihren Datenlebenszyklus beobachtbar zu machen

0
Samuel Robert