Navigation der Android Jetpack-Komponente

Unabhängig davon, wie Sie die Benutzeroberfläche Ihrer App erstellen (mit Fragmenten, Aktivitäten oder anderen Komponenten), entwerfen Sie Ihre App für die Navigation zwischen Bildschirmen. Dann können Sie Naviagtion ausprobieren.

Grundlegende Verwendung

Projekt erstellen

Sie können es direkt in Android Studio erstellen
Fügen Sie hier eine Bildbeschreibung ein
und wie folgt ausführen:
Fügen Sie hier eine Bildbeschreibung ein

Nutzungscode beachten

Dies ist die Verzeichnisstruktur.
Fügen Sie hier eine Bildbeschreibung ein
Eine Hauptaktivität, drei Fragmente
der Hauptaktivität, Layoutcodes main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingTop="?attr/actionBarSize">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />

    <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:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>
Navigationssteuerung unten

Dieser Teil: Es ist unser unterer Umschalt-Tab, BottomNavigationView und unser unteres Navigationssteuerelement

  <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />

Wo wird der angezeigte Inhalt kontrolliert?

app:menu=“@menu/bottom_nav_menu“

Hier gibt es ein Menüattribut, und der angezeigte Inhalt wird hier gesteuert. Im Bild oben sehen wir beispielsweise „Startseite“, „Dashboard“ und „Benachrichtigungen“.
Öffnen wir diese Datei und werfen einen Blick darauf:

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

    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home" />

    <item
        android:id="@+id/navigation_dashboard"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="@string/title_dashboard" />

    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_notifications" />

</menu>

Sobald Sie hier angekommen sind, ist es in Ordnung, unten ein paar Registerkarten anzuzeigen. Aber wie verknüpft man es mit dem Fragment oben?

Fragmentcode
<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:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

Hier gibt es tatsächlich ein Fragment, aber wie zeigt man mehrere Fragmente an und wechselt zwischen ihnen?
Tatsächlich handelt es sich bei diesem Fragment um ein verwaltetes Fragment, bei dem es sich um einen Container handelt.
Über die Konfigurationsdatei füllen wir andere Konfigurationsdateien in diesen Container. Wenn Sie genau hinschauen, werden Sie das finden

app:navGraph=“@navigation/mobile_navigation“

Dies ist eine weitere Konfigurationsdatei. Diese Konfigurationsdatei dient zum Konfigurieren des Fragments, das von diesem HostFragment verwaltet werden soll. Der Code lautet wie folgt:

<?xml version="1.0" encoding="utf-8"?>
<navigation 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/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.example.navigationdemo.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/navigation_dashboard"
        android:name="com.example.navigationdemo.ui.dashboard.DashboardFragment"
        android:label="@string/title_dashboard"
        tools:layout="@layout/fragment_dashboard" />

    <fragment
        android:id="@+id/navigation_notifications"
        android:name="com.example.navigationdemo.ui.notifications.NotificationsFragment"
        android:label="@string/title_notifications"
        tools:layout="@layout/fragment_notifications" />
</navigation>

Es sind drei Fragmente darin, nehmen wir das erste

   <fragment
        android:id="@+id/navigation_home"
        android:name="com.sunofbeaches.navigationdemo.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

Die Attribute „id“ und „name“ verweisen auf unser Fragment, „label“ ist das Etikett und „layout“ das Layout.

app:startDestination="@+id/navigation_home"

Gibt die standardmäßig anzuzeigende Seite an, die auf navigation_home verweist. Wenn wir sie also starten, wird standardmäßig das HomeFragment angezeigt.

Gehen Sie zurück und sehen Sie sich den Code für das Homepage-Layout an

<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:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

Hier ist eine

 app:defaultNavHost="true"

Dadurch wird das Return-Key-Ereignis tatsächlich zur Verarbeitung an NavHostFragment übertragen. Steuert die Rückgabe verwalteter Fragmente/Aktivitäten/Dailogs.
Wie verbinden sich die beiden?
Durch Klicken auf die Registerkarte unten wird das Fragment oben gewechselt

Verknüpfung zwischen unterer Navigationsansicht und oberem Fragment

Unser objektorientiertes Denken, das einfachste Denken, besteht darin, die Auswahländerung von BottomNavigationView zu überwachen und dann das obere Fragment zu wechseln.
Da es sich um offizielle Pakete für Android handelt, möchte Google auch, dass wir sie gemeinsam nutzen.
Da sie möchten, dass wir es in Kombination verwenden, haben sie diese Umschaltaktion bereits implementiert und verknüpfen sie einfach.

        //找到底部的导航控件
        val navView: BottomNavigationView = findViewById(R.id.nav_view)
        //找到hostFragment
        val navController = findNavController(R.id.nav_host_fragment)
        //关联起来
        navView.setupWithNavController(navController)

Auf diese Weise sind sie miteinander verbunden. Die Registerkarten unten werden vertauscht und die Fragmente darüber werden vertauscht.

Editor für Navigationsansichten

Öffnen Sie unsere Datei /res/navigation/mobile_navigation.xml
und wechseln Sie dann zur Design-Ansichtsoberfläche. In der oberen rechten Ecke
Fügen Sie hier eine Bildbeschreibung ein
Fügen Sie hier eine Bildbeschreibung ein
sehen Sie die Struktur der linken Ansicht, und
wie wir vor dem Einstieg sagten, kann unsere Navigation nicht nur verwaltet werden Fragmente, haben aber auch Aktivitäten.
Erstellen wir eine Anmeldeaktivität für
Fügen Sie hier eine Bildbeschreibung ein
. Denken Sie daran, sie in der Manifestdatei zu registrieren.
Klicken Sie dann auf die Schaltfläche „Hinzufügen“ in der oberen linken Ecke der Ansicht
Fügen Sie hier eine Bildbeschreibung ein
. Nach dem Hinzufügen:
Fügen Sie hier eine Bildbeschreibung ein
Die entsprechende XML-Datei verfügt auch über eine zusätzliche Aktivität
. Wie springe ich also?

Seitensprung

Zum Beispiel möchten wir zur Aktivität „homepage-jump-login“ gehen

Wir wählen das Home-Fragment aus und klicken dann auf das Symbol, um eine Aktion hinzuzufügen.
Fügen Sie hier eine Bildbeschreibung ein
Sie können auch Animationen hinzufügen, wie in der Abbildung usw.
Nach dem Hinzufügen
Fügen Sie hier eine Bildbeschreibung ein

Die entsprechende XML-Datei enthält:

<fragment
        android:id="@+id/navigation_home"
        android:name="com.sunofbeaches.navigationdemo.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/to_login_activity"
            app:destination="@id/loginActivity"
            app:enterAnim="@anim/fragment_fade_enter"
            app:exitAnim="@anim/fragment_close_exit" />
    </fragment>

Zu diesem Zeitpunkt funktioniert es noch nicht. Wir haben gerade die Sprungbeziehung deklariert. Wenn wir wirklich springen möchten, müssen wir einfachen Code hinzufügen.
Fügen Sie unserem HomeFragment eine Sprungschaltfläche hinzu

     val root = inflater.inflate(R.layout.fragment_home, container, false)

        val loginBtn = root.findViewById<Button>(R.id.toLoginPage)
        loginBtn.setOnClickListener {
    
    
            Navigation.findNavController(root).navigate(R.id.to_login_activity)
        }

Es gibt hauptsächlich eine Navigation.findNavController(root).navigate-Methode.
Diese Methode besteht darin, zu einer bestimmten Stelle zu springen. Es gibt mehrere überladene Methoden:
Fügen Sie hier eine Bildbeschreibung ein
Sie können DeepLink verwenden, Sie können Anweisungen verwenden und Sie können auch Parameter übergeben.
Sprungeffekt:
Fügen Sie hier eine Bildbeschreibung ein

Springe zur Übergabe von Parametern

Für unsere LoginActivity können Sie beispielsweise eine Telefonnummer übergeben.
Beim Springen übertragen wir Parameter.

loginBtn.setOnClickListener {
    
    
            val userInfo = Bundle()
            userInfo.putString("phoneNumber", "15353979727")
            Navigation.findNavController(root).navigate(R.id.to_login_activity, userInfo)
        }

Wie bekommen wir es also in LoginActivity?

  val phoneNum = intent.extras!!.get("phoneNumber")
        println(phoneNum)

Analyse des Navigationsquellcodes

Wie Konfigurationsdateien geladen werden

app:navGraph=“@navigation/mobile_navigation“

Wir sehen die onCreate-Methode von NavHostFragment

  @CallSuper
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        final Context context = requireContext();

        mNavController = new NavHostController(context);
        mNavController.setLifecycleOwner(this);
        mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
        // Set the default state - this will be updated whenever
        // onPrimaryNavigationFragmentChanged() is called
        mNavController.enableOnBackPressed(
                mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
        mIsPrimaryBeforeOnCreate = null;
        mNavController.setViewModelStore(getViewModelStore());
        onCreateNavController(mNavController);

        Bundle navState = null;
        if (savedInstanceState != null) {
    
    
            navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
            if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
    
    
                mDefaultNavHost = true;
                getParentFragmentManager().beginTransaction()
                        .setPrimaryNavigationFragment(this)
                        .commit();
            }
            mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
        }

        if (navState != null) {
    
    
            // Navigation controller state overrides arguments
            mNavController.restoreState(navState);
        }
        if (mGraphId != 0) {
    
    
            // Set from onInflate()
            mNavController.setGraph(mGraphId);
        } else {
    
    
            // See if it was set by NavHostFragment.create()
            final Bundle args = getArguments();
            final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
            final Bundle startDestinationArgs = args != null
                    ? args.getBundle(KEY_START_DESTINATION_ARGS)
                    : null;
            if (graphId != 0) {
    
    
                mNavController.setGraph(graphId, startDestinationArgs);
            }
        }

        // We purposefully run this last as this will trigger the onCreate() of
        // child fragments, which may be relying on having the NavController already
        // created and having its state restored by that point.
        super.onCreate(savedInstanceState);
    }

wir können finden
Fügen Sie hier eine Bildbeschreibung ein

mNavController.setGraph(mGraphId);

Wenn mGraphId in diesem Code nicht 0 ist, wird es direkt verarbeitet. Wenn mGraphId 0 ist, wird es über andere Methoden abgerufen.
Die über die Ressourcen-ID aufgerufene Methode wird hier angezeigt.

    @CallSuper
    public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
    
    
        setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
    }

Schau dir das an

getNavInflater().inflate(graphResId)

Der Aufruf dieser Methode besteht darin, XML in eine Bean-Klasse zu konvertieren

 /**
     * Inflate a NavGraph from the given XML resource id.
     *
     * @param graphResId
     * @return
     */
    @SuppressLint("ResourceType")
    @NonNull
    public NavGraph inflate(@NavigationRes int graphResId) {
    
    
        Resources res = mContext.getResources();
        XmlResourceParser parser = res.getXml(graphResId);
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        try {
    
    
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG
                    && type != XmlPullParser.END_DOCUMENT) {
    
    
                // Empty loop
            }
            if (type != XmlPullParser.START_TAG) {
    
    
                throw new XmlPullParserException("No start tag found");
            }

            String rootElement = parser.getName();
            NavDestination destination = inflate(res, parser, attrs, graphResId);
            if (!(destination instanceof NavGraph)) {
    
    
                throw new IllegalArgumentException("Root element <" + rootElement + ">"
                        + " did not inflate into a NavGraph");
            }
            return (NavGraph) destination;
        } catch (Exception e) {
    
    
            throw new RuntimeException("Exception inflating "
                    + res.getResourceName(graphResId) + " line "
                    + parser.getLineNumber(), e);
        } finally {
    
    
            parser.close();
        }
    }

Das heißt, das Objekt NavGraph wird zurückgegeben. Wem wird dieses übergeben?

    public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) {
    
    
        if (mGraph != null) {
    
    
            // Pop everything from the old graph off the back stack
            popBackStackInternal(mGraph.getId(), true);
        }
        mGraph = graph;
        onGraphCreated(startDestinationArgs);
    }

Direkt im NavController.

So wechseln Sie Fragmente

Haben wir die Fragmente früher ausgetauscht? Wenn wir unten auf die Registerkarte klicken, wird das obige Fragment umgeschaltet.
Das ist unser Eingang.

       //找到底部的导航控件
        val navView: BottomNavigationView = findViewById(R.id.nav_view)
        //找到hostFragment
        val navController = findNavController(R.id.nav_host_fragment)
        navView.setupWithNavController(navController)

Dies ist eine Erweiterungsfunktion

fun BottomNavigationView.setupWithNavController(navController: NavController) {
    
    
    NavigationUI.setupWithNavController(this, navController)
}

Nachverfolgen

 public static void setupWithNavController(
            @NonNull final BottomNavigationView bottomNavigationView,
            @NonNull final NavController navController) {
    
    
        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
    
    
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    
    
                        return onNavDestinationSelected(item, navController);
                    }
                });
        final WeakReference<BottomNavigationView> weakReference =
                new WeakReference<>(bottomNavigationView);
        navController.addOnDestinationChangedListener(
                new NavController.OnDestinationChangedListener() {
    
    
                    @Override
                    public void onDestinationChanged(@NonNull NavController controller,
                            @NonNull NavDestination destination, @Nullable Bundle arguments) {
    
    
                        BottomNavigationView view = weakReference.get();
                        if (view == null) {
    
    
                            navController.removeOnDestinationChangedListener(this);
                            return;
                        }
                        Menu menu = view.getMenu();
                        for (int h = 0, size = menu.size(); h < size; h++) {
    
    
                            MenuItem item = menu.getItem(h);
                            if (matchDestination(destination, item.getItemId())) {
    
    
                                item.setChecked(true);
                            }
                        }
                    }
                });
    }

Überwachen Sie die Auswahl von NavigationItem in bottomNavigationView und geben Sie die Methode onNavDestinationSelected zurück

  public static boolean onNavDestinationSelected(@NonNull MenuItem item,
            @NonNull NavController navController) {
    
    
        NavOptions.Builder builder = new NavOptions.Builder()
                .setLaunchSingleTop(true);
        if (navController.getCurrentDestination().getParent().findNode(item.getItemId())
                instanceof ActivityNavigator.Destination) {
    
    
            builder.setEnterAnim(R.anim.nav_default_enter_anim)
                    .setExitAnim(R.anim.nav_default_exit_anim)
                    .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
                    .setPopExitAnim(R.anim.nav_default_pop_exit_anim);

        } else {
    
    
            builder.setEnterAnim(R.animator.nav_default_enter_anim)
                    .setExitAnim(R.animator.nav_default_exit_anim)
                    .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
                    .setPopExitAnim(R.animator.nav_default_pop_exit_anim);
        }
        if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
    
    
            builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
        }
        NavOptions options = builder.build();
        try {
    
    
            //TODO provide proper API instead of using Exceptions as Control-Flow.
            navController.navigate(item.getItemId(), null, options);
            return true;
        } catch (IllegalArgumentException e) {
    
    
            return false;
        }
    }

Es gibt eine solche Linie

  navController.navigate(item.getItemId(), null, options);

Gehen Sie weiter, bis Sie hier ankommen

Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                node.getNavigatorName());
        Bundle finalArgs = node.addInDefaultArgs(args);
        NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);

Die Art des Navigators hängt davon ab, was herausgenommen wird. Was wir hier untersuchen, ist Fragment. Was wir also herausnehmen, sollte
Fügen Sie hier eine Bildbeschreibung ein
Folgendes sein: Wenn wir navigieren, rufen wir tatsächlich die Navigationsmethode in FragmentNavitor auf, nämlich diese

  @Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
    
    
        if (mFragmentManager.isStateSaved()) {
    
    
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                    + " saved its state");
            return null;
        }
        String className = destination.getClassName();
        if (className.charAt(0) == '.') {
    
    
            className = mContext.getPackageName() + className;
        }
        final Fragment frag = instantiateFragment(mContext, mFragmentManager,
                className, args);
        frag.setArguments(args);
        final FragmentTransaction ft = mFragmentManager.beginTransaction();

        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
    
    
            enterAnim = enterAnim != -1 ? enterAnim : 0;
            exitAnim = exitAnim != -1 ? exitAnim : 0;
            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
        }

        ft.replace(mContainerId, frag);
        ft.setPrimaryNavigationFragment(frag);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;

        boolean isAdded;
        if (initialNavigation) {
    
    
            isAdded = true;
        } else if (isSingleTopReplacement) {
    
    
            // Single Top means we only want one instance on the back stack
            if (mBackStack.size() > 1) {
    
    
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                mFragmentManager.popBackStack(
                        generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
                ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
            }
            isAdded = false;
        } else {
    
    
            ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
            isAdded = true;
        }
        if (navigatorExtras instanceof Extras) {
    
    
            Extras extras = (Extras) navigatorExtras;
            for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
    
    
                ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
            }
        }
        ft.setReorderingAllowed(true);
        ft.commit();
        // The commit succeeded, update our view of the world
        if (isAdded) {
    
    
            mBackStack.add(destId);
            return destination;
        } else {
    
    
            return null;
        }
    }

Wie Sie sehen können, wird die Ersetzungsmethode zum Wechseln verwendet
Fügen Sie hier eine Bildbeschreibung ein
. Daher werden beim Wechseln von Fragmenten häufig Lebenszyklusänderungen zerstört und erstellt.

Ändern Sie die Art und Weise, wie NavFragmentHost Fragmente wechselt

Aus der vorherigen Quellcode-Analyse wissen wir, dass FragmentNavigator für den Wechsel verantwortlich ist.
Wenn wir diese Klasse direkt erben und die Navigationsmethode überschreiben, werden einige private Eigenschaften nicht verwendet.
Was sollen wir dann tun?
Sollten wir nicht auch unseren eigenen FragmentNavigator schreiben, da sie alle von Navigator geerbt sind?
Kopieren Sie den anderen Code von FragmentNavigator und ändern Sie ihn dann.
Nach der Modifikation sieht es so aus

/**
     * {@inheritDoc}
     * <p>
     * This method should always call
     * {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}
     * so that the Fragment associated with the new destination can be retrieved with
     * {@link FragmentManager#getPrimaryNavigationFragment()}.
     * <p>
     * Note that the default implementation commits the new Fragment
     * asynchronously, so the new Fragment is not instantly available
     * after this call completes.
     */
    @SuppressWarnings("deprecation") /* Using instantiateFragment for forward compatibility */
    @Nullable
    @Override
    public NavDestination navigate(@NonNull FragmentNavigator.Destination destination, @Nullable Bundle args,
                                   @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
    
    
        if (mFragmentManager.isStateSaved()) {
    
    
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                    + " saved its state");
            return null;
        }
        String className = destination.getClassName();
        if (className.charAt(0) == '.') {
    
    
            className = mContext.getPackageName() + className;
        }
        String tag = className.substring(className.lastIndexOf(".") + 1);
        Fragment frag = mFragmentManager.findFragmentByTag(tag);
        //判断是否有添加,如果没有添加,则添加,并且显示
        //如果已经添加了,直接显示
        if (frag == null) {
    
    
            System.out.println(" create new fragment..." + tag);
            frag = instantiateFragment(mContext, mFragmentManager,
                    className, args);
        }
        frag.setArguments(args);
        final FragmentTransaction ft = mFragmentManager.beginTransaction();

        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
    
    
            enterAnim = enterAnim != -1 ? enterAnim : 0;
            exitAnim = exitAnim != -1 ? exitAnim : 0;
            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
        }

        //隐藏上一个显示的内容
        for (Fragment fragment : mFragmentManager.getFragments()) {
    
    
            System.out.println("hide fragment -- > " + fragment.getClass().getName());
            ft.hide(fragment);
        }

        if (!frag.isAdded()) {
    
    
            System.out.println("add fragment ... " + tag);
            ft.add(mContainerId, frag, tag);
        }

        ft.show(frag);
        //ft.replace(mContainerId, frag);
        ft.setPrimaryNavigationFragment(frag);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;

        boolean isAdded;
        if (initialNavigation) {
    
    
            isAdded = true;
        } else if (isSingleTopReplacement) {
    
    
            // Single Top means we only want one instance on the back stack
            if (mBackStack.size() > 1) {
    
    
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                mFragmentManager.popBackStack(
                        generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
                ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
            }
            isAdded = false;
        } else {
    
    
            ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
            isAdded = true;
        }
        if (navigatorExtras instanceof HideSwitchFragmentNavigator.Extras) {
    
    
            HideSwitchFragmentNavigator.Extras extras = (HideSwitchFragmentNavigator.Extras) navigatorExtras;
            for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
    
    
                ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
            }
        }
        ft.setReorderingAllowed(true);
        ft.commit();
        // The commit succeeded, update our view of the world
        if (isAdded) {
    
    
            mBackStack.add(destId);
            return destination;
        } else {
    
    
            return null;
        }
    }

Hier ist der Hauptcode. Ersetzen Sie ersetzen durch hide
. Dann schreiben wir eine Klasse, um NavHostFragment zu erben und die darin enthaltene Methode zu überschreiben.
NavHostFragment erstellt FragmentNavigator in der folgenden Methode.

 protected void onCreateNavController(@NonNull NavController navController) {
    
    
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }

Daher überschreiben wir die Methode createFragmentNavigator

class CustomNavHostFragment : NavHostFragment() {
    
    

    /**
     * Create the FragmentNavigator that this NavHostFragment will use. By default, this uses
     * [FragmentNavigator], which replaces the entire contents of the NavHostFragment.
     *
     *
     * This is only called once in [.onCreate] and should not be called directly by
     * subclasses.
     * @return a new instance of a FragmentNavigator
     */
    @Deprecated("Use {@link #onCreateNavController(NavController)}")
    override fun createFragmentNavigator(): Navigator<out FragmentNavigator.Destination?> {
    
    
        return HideSwitchFragmentNavigator(
            requireContext(), childFragmentManager,
            getContainerId()
        )
    }

    private fun getContainerId(): Int {
    
    
        val id = id
        return if (id != 0 && id != View.NO_ID) {
    
    
            id
        } else R.id.nav_host_fragment_container
        // Fallback to using our own ID if this Fragment wasn't added via
        // add(containerViewId, Fragment)
    }
}

Ändern Sie es wie folgt

 <fragment
        android:id="@+id/nav_host_fragment"
        android:name="com.example.navigationdemo.view.CustomNavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
 override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //找到底部的导航控件
        val navView: BottomNavigationView = findViewById(R.id.nav_view)
        //找到hostFragment
        val navController = findNavController(R.id.nav_host_fragment)
        navView.setOnNavigationItemSelectedListener {
    
    
            when (it.itemId) {
    
    
                R.id.navigation_home -> {
    
    
                    navController.navigate(R.id.navigation_home)
                }
                R.id.navigation_dashboard -> {
    
    
                    navController.navigate(R.id.navigation_dashboard)
                }

                R.id.navigation_notifications -> {
    
    
                    navController.navigate(R.id.navigation_notifications)
                }
            }
            true
        }
    }

Ich denke du magst

Origin blog.csdn.net/ChenYiRan123456/article/details/130855077
Empfohlen
Rangfolge