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.
Artikelverzeichnis
Grundlegende Verwendung
Projekt erstellen
Sie können es direkt in Android Studio erstellen
und wie folgt ausführen:
Nutzungscode beachten
Dies ist die Verzeichnisstruktur.
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
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
. 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
. Nach dem Hinzufügen:
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.
Sie können auch Animationen hinzufügen, wie in der Abbildung usw.
Nach dem Hinzufügen
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:
Sie können DeepLink verwenden, Sie können Anweisungen verwenden und Sie können auch Parameter übergeben.
Sprungeffekt:
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
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
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
. 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
}
}