Wenn Sie die Navigation für Ihre App entwerfen, möchten Sie möglicherweise zu einem Ziel basierend auf bedingter Logik mit einem anderen Ziel vergleichen. Beispiel: Ein Nutzer Sie folgen einem Deeplink zu einem Ziel, bei dem der Nutzer angemeldet sein muss. Oder ihr habt verschiedene Ziele in einem Spiel, wenn der Spieler gewinnt oder verliert.
Nutzeranmeldung
In diesem Beispiel versucht eine nutzende Person, zu einem Profilbildschirm zu navigieren, auf dem Authentifizierung. Da für diese Aktion eine Authentifizierung erforderlich ist, sollte der Nutzer werden zu einem Anmeldebildschirm weitergeleitet, sofern sie nicht bereits authentifiziert sind.
Die Navigationsgrafik für dieses Beispiel könnte etwa so aussehen:
<ph type="x-smartling-placeholder">Zur Authentifizierung muss die App die login_fragment
aufrufen, wobei der Nutzer
kann zur Authentifizierung einen Nutzernamen und ein Passwort eingeben. Wenn sie akzeptiert wird, ist der Nutzer
an den profile_fragment
-Bildschirm zurückgesendet. Wenn sie nicht akzeptiert wird, ist der Nutzer
mit einem Hinweis, dass ihre Anmeldedaten ungültig sind,
Snackbar
Wenn Nutzende ohne Anmeldung zum Profilbildschirm zurückkehren,
an den main_fragment
-Bildschirm gesendet.
Hier ist das Navigationsdiagramm für diese App:
<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/nav_graph"
app:startDestination="@id/main_fragment">
<fragment
android:id="@+id/main_fragment"
android:name="com.google.android.conditionalnav.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main">
<action
android:id="@+id/navigate_to_profile_fragment"
app:destination="@id/profile_fragment"/>
</fragment>
<fragment
android:id="@+id/login_fragment"
android:name="com.google.android.conditionalnav.LoginFragment"
android:label="login_fragment"
tools:layout="@layout/login_fragment"/>
<fragment
android:id="@+id/profile_fragment"
android:name="com.google.android.conditionalnav.ProfileFragment"
android:label="fragment_profile"
tools:layout="@layout/fragment_profile"/>
</navigation>
MainFragment
enthält eine Schaltfläche, auf die der Nutzer klicken kann, um sein Profil aufzurufen.
Wenn der Nutzer den Profilbildschirm sehen möchte, muss er sich zuerst authentifizieren. Dieses
Interaktion wird anhand von zwei separaten Fragmenten modelliert, hängt aber von gemeinsamen
Nutzerstatus. Für diese Statusinformationen ist weder die Verantwortung
diese beiden Fragmente und ist passender in einem gemeinsamen UserViewModel
enthalten.
Dieser ViewModel
wird von den Fragmenten gemeinsam genutzt, indem er zur Aktivität gehört.
mit dem ViewModelStoreOwner
implementiert wird. Im folgenden Beispiel
requireActivity()
wird in MainActivity
aufgelöst, da MainActivity
Hosts
ProfileFragment
:
Kotlin
class ProfileFragment : Fragment() { private val userViewModel: UserViewModel by activityViewModels() ... }
Java
public class ProfileFragment extends Fragment { private UserViewModel userViewModel; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); ... } ... }
Die Nutzerdaten in UserViewModel
werden über LiveData
offengelegt. Um zu entscheiden, wo
sollten Sie diese Daten im Auge behalten. Wenn Sie nach dem Aufrufen
ProfileFragment
, die App zeigt eine Willkommensnachricht an, wenn die Nutzerdaten
vorhanden ist. Wenn es sich bei den Nutzerdaten um null
handelt, gehen Sie zu LoginFragment
,
da sich der Nutzer authentifizieren muss, bevor er sein Profil sehen kann. Definieren Sie die
Entscheidungslogik in ProfileFragment
, wie im folgenden Beispiel gezeigt:
Kotlin
class ProfileFragment : Fragment() { private val userViewModel: UserViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val navController = findNavController() userViewModel.user.observe(viewLifecycleOwner, Observer { user -> if (user != null) { showWelcomeMessage() } else { navController.navigate(R.id.login_fragment) } }) } private fun showWelcomeMessage() { ... } }
Java
public class ProfileFragment extends Fragment { private UserViewModel userViewModel; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); final NavController navController = Navigation.findNavController(view); userViewModel.user.observe(getViewLifecycleOwner(), (Observer<User>) user -> { if (user != null) { showWelcomeMessage(); } else { navController.navigate(R.id.login_fragment); } }); } private void showWelcomeMessage() { ... } }
Wenn die Nutzerdaten null
sind, wenn sie ProfileFragment
erreichen, sind sie
an LoginFragment
weitergeleitet.
Sie können
NavController.getPreviousBackStackEntry()
zum Abrufen von NavBackStackEntry
für das vorherige Ziel, das die NavController
-spezifischen
Bundesland für das Ziel. LoginFragment
verwendet die
SavedStateHandle
von
vorherige NavBackStackEntry
, um einen Anfangswert festzulegen, der angibt, ob die
Nutzer hat sich erfolgreich angemeldet. Diesen Bundesstaat würden wir zurückgeben,
sofort auf die Zurück-Taste des Systems zu drücken. Festlegen dieses Status
Mit SavedStateHandle
wird sichergestellt, dass der Status bis zum Ende des Prozesses bestehen bleibt.
Kotlin
class LoginFragment : Fragment() { companion object { const val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL" } private val userViewModel: UserViewModel by activityViewModels() private lateinit var savedStateHandle: SavedStateHandle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { savedStateHandle = findNavController().previousBackStackEntry!!.savedStateHandle savedStateHandle.set(LOGIN_SUCCESSFUL, false) } }
Java
public class LoginFragment extends Fragment { public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL" private UserViewModel userViewModel; private SavedStateHandle savedStateHandle; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); savedStateHandle = Navigation.findNavController(view) .getPreviousBackStackEntry() .getSavedStateHandle(); savedStateHandle.set(LOGIN_SUCCESSFUL, false); } }
Sobald der Nutzer einen Nutzernamen und ein Passwort eingegeben hat, werden diese
UserViewModel
für die Authentifizierung. Wenn die Authentifizierung erfolgreich war,
UserViewModel
speichert die Nutzerdaten. LoginFragment
aktualisiert dann die
LOGIN_SUCCESSFUL
-Wert für SavedStateHandle
und hebt sich von
Back Stacks.
Kotlin
class LoginFragment : Fragment() { companion object { const val LOGIN_SUCCESSFUL: String = "LOGIN_SUCCESSFUL" } private val userViewModel: UserViewModel by activityViewModels() private lateinit var savedStateHandle: SavedStateHandle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { savedStateHandle = findNavController().previousBackStackEntry!!.savedStateHandle savedStateHandle.set(LOGIN_SUCCESSFUL, false) val usernameEditText = view.findViewById(R.id.username_edit_text) val passwordEditText = view.findViewById(R.id.password_edit_text) val loginButton = view.findViewById(R.id.login_button) loginButton.setOnClickListener { val username = usernameEditText.text.toString() val password = passwordEditText.text.toString() login(username, password) } } fun login(username: String, password: String) { userViewModel.login(username, password).observe(viewLifecycleOwner, Observer { result -> if (result.success) { savedStateHandle.set(LOGIN_SUCCESSFUL, true) findNavController().popBackStack() } else { showErrorMessage() } }) } fun showErrorMessage() { // Display a snackbar error message } }
Java
public class LoginFragment extends Fragment { public static String LOGIN_SUCCESSFUL = "LOGIN_SUCCESSFUL" private UserViewModel userViewModel; private SavedStateHandle savedStateHandle; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { userViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class); savedStateHandle = Navigation.findNavController(view) .getPreviousBackStackEntry() .getSavedStateHandle(); savedStateHandle.set(LOGIN_SUCCESSFUL, false); EditText usernameEditText = view.findViewById(R.id.username_edit_text); EditText passwordEditText = view.findViewById(R.id.password_edit_text); Button loginButton = view.findViewById(R.id.login_button); loginButton.setOnClickListener(v -> { String username = usernameEditText.getText().toString(); String password = passwordEditText.getText().toString(); login(username, password); }); } private void login(String username, String password) { userViewModel.login(username, password).observe(viewLifecycleOwner, (Observer<LoginResult>) result -> { if (result.success) { savedStateHandle.set(LOGIN_SUCCESSFUL, true); NavHostFragment.findNavController(this).popBackStack(); } else { showErrorMessage(); } }); } private void showErrorMessage() { // Display a snackbar error message } }
Die gesamte Logik für die Authentifizierung befindet sich
UserViewModel
Das ist wichtig, da es nicht in der Verantwortung der
LoginFragment
oder ProfileFragment
, um zu ermitteln, wie Nutzer
authentifiziert. Durch die Kapselung der Logik in einem ViewModel
ist es nicht nur möglich,
einfacher zu teilen und gleichzeitig zu testen. Wenn Ihre Navigationslogik komplex ist,
sollten Sie diese Logik
besonders durch Tests überprüfen. Weitere Informationen finden Sie in der
Leitfaden zur Anwendungsarchitektur mit weiteren Informationen zu
Strukturieren der App-Architektur um testbare Komponenten.
Im ProfileFragment
ist der LOGIN_SUCCESSFUL
-Wert, der in der
SavedStateHandle
kann in der
onCreate()
. Wenn der Nutzer zur ProfileFragment
zurückkehrt, wird der LOGIN_SUCCESSFUL
wird geprüft. Wenn der Wert false
ist, kann der Nutzer zurückgeleitet werden.
zu MainFragment
.
Kotlin
class ProfileFragment : Fragment() { ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val navController = findNavController() val currentBackStackEntry = navController.currentBackStackEntry!! val savedStateHandle = currentBackStackEntry.savedStateHandle savedStateHandle.getLiveData<Boolean>(LoginFragment.LOGIN_SUCCESSFUL) .observe(currentBackStackEntry, Observer { success -> if (!success) { val startDestination = navController.graph.startDestination val navOptions = NavOptions.Builder() .setPopUpTo(startDestination, true) .build() navController.navigate(startDestination, null, navOptions) } }) } ... }
Java
public class ProfileFragment extends Fragment { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); NavController navController = NavHostFragment.findNavController(this); NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry(); SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle(); savedStateHandle.getLiveData(LoginFragment.LOGIN_SUCCESSFUL) .observe(navBackStackEntry, (Observer<Boolean>) success -> { if (!success) { int startDestination = navController.getGraph().getStartDestination(); NavOptions navOptions = new NavOptions.Builder() .setPopUpTo(startDestination, true) .build(); navController.navigate(startDestination, null, navOptions); } }); } ... }
Wenn der Nutzer sich erfolgreich angemeldet hat, zeigt das ProfileFragment
eine
Willkommensnachricht.
Mit der hier verwendeten Technik können Sie zwischen zwei verschiedenen Fällen:
- Im ersten Fall, in dem der Nutzer nicht angemeldet ist und aufgefordert werden sollte, Log-in.
- Der Nutzer ist nicht angemeldet, weil er sich nicht angemeldet hat (aus
false
.
Wenn Sie diese Anwendungsfälle voneinander unterscheiden, vermeiden Sie es, wiederholt nach Fragen zu fragen. um sich anzumelden. Die Geschäftslogik für den Umgang mit Fehlern bleibt Ihnen überlassen und könnte die Anzeige eines Overlays beinhalten, das erklärt, warum die Nutzenden sich anmelden, die gesamte Aktivität abschließen oder den Nutzer an ein Ziel weiterleiten. für die keine Anmeldung erforderlich ist, wie es im vorherigen Codebeispiel der Fall war.