Ich würde gerne wissen, wie man mit dem Navigations-Controller die Rücktastenaktion des Systems richtig handhabt. In meiner App habe ich zwei Fragmente (zB fragment1 und fragment2) und ich habe eine Aktion in fragment1 mit dem Ziel für fragment2. Alles funktioniert gut, bis auf eine Sache: Wenn der Benutzer in fragment2 den System-Zurück-Knopf drückt, möchte ich einen Dialog anzeigen (z. B. mit DialogFragment), um das Schließen zu bestätigen. Wie implementiere ich dieses Verhalten am besten? Wenn ich app:defaultNavHost="true"
in meinem Host-Fragment verwende, werden die Regeln automatisch ignoriert. Und wozu dient diese Komponente zusätzlich?
Soll ich "Pop to" verwenden?
Also habe ich eine Schnittstelle erstellt
public interface OnBackPressedListener {
void onBackPressed();
}
Und es wurde von allen Fragmenten implementiert, die den Zurück-Button bedienen müssen. In der Hauptaktivität überschritt ich die onBackPressed()
-Methode:
@Override
public void onBackPressed() {
final Fragment currentFragment = mNavHostFragment.getChildFragmentManager().getFragments().get(0);
final NavController controller = Navigation.findNavController(this, R.id.nav_Host_fragment);
if (currentFragment instanceof OnBackPressedListener)
((OnBackPressedListener) currentFragment).onBackPressed();
else if (!controller.popBackStack())
finish();
}
Wenn also das oberste Fragment meines Navigationshosts die OnBackPressedListener
-Schnittstelle implementiert, rufe ich seine onBackPressed()
-Methode auf. An anderen Stellen schalte ich einfach den Stack zurück und schließe die Anwendung, wenn der Back-Stack leer ist.
Neuestes UPDATE 25. April 2019
Neue Version androidx.activity ver. 1.0.0-alpha07 bringt einige Änderungen
Weitere Erklärungen in Android offizielle Anleitung: Bieten Sie benutzerdefinierte Navigation zurück
Beispiel:
public class MyFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This callback will only be called when MyFragment is at least Started.
OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
@Override
public boolean handleOnBackPressed() {
// Handle the back button event
}
});
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
// The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}
ALTE UPDATES
UPD: 3. April 2019
Jetzt ist es vereinfacht. Weitere Infos hier
Beispiel:
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), this);
@Override
public boolean handleOnBackPressed() {
//Do your job here
//use next line if you just need navigate up
//NavHostFragment.findNavController(this).navigateUp();
//Log.e(getClass().getSimpleName(), "handleOnBackPressed");
return true;
}
Veraltet (seit Version 1.0.0-alpha06, 3. April 2019):
Da this , kann es einfach mit JetPack der Implementierung OnBackPressedCallback
in Ihrem Fragment implementiert und zur Aktivität hinzugefügt werden: getActivity().addOnBackPressedCallback(getViewLifecycleOwner(),this);
Dein Fragment sollte so aussehen:
public MyFragment extends Fragment implements OnBackPressedCallback {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().addOnBackPressedCallback(getViewLifecycleOwner(),this);
}
@Override
public boolean handleOnBackPressed() {
//Do your job here
//use next line if you just need navigate up
//NavHostFragment.findNavController(this).navigateUp();
//Log.e(getClass().getSimpleName(), "handleOnBackPressed");
return true;
}
@Override
public void onDestroyView() {
super.onDestroyView();
getActivity().removeOnBackPressedCallback(this);
}
}
UPD: Ihre Aktivität sollte AppCompatActivity
oder FragmentActivity
und in Gradle-Datei:
implementation 'androidx.appcompat:appcompat:{lastVersion}'
Der Ansatz empfohlen besteht darin, der Aktivität OnBackPressedCallback
ein OnBackPressedDispatcher
hinzuzufügen.
requireActivity().onBackPressedDispatcher.addCallback {
// handle back event
}
Hier ist eine Lösung, die tun sollte, was Sie wollen, aber ich denke, es ist eine schlechte Lösung, da sie gegen die Android-Navigationskomponenten-Idee (die Android-Navigation die Navigation) zulässt.
Überschreiben Sie "onBackPressed" in Ihrer Aktivität
override fun onBackPressed() {
when(NavHostFragment.findNavController(nav_Host_fragment).currentDestination.id) {
R.id.fragment2-> {
val dialog=AlertDialog.Builder(this).setMessage("Hello").setPositiveButton("Ok", DialogInterface.OnClickListener { dialogInterface, i ->
finish()
}).show()
}
else -> {
super.onBackPressed()
}
}
}
Etwas spät zur Party, aber mit der neuesten Version von Navigation Component 1.0.0-alpha09 haben wir jetzt eine AppBarConfiguration.OnNavigateUpListener.
Weitere Informationen finden Sie unter diesen Links: https://developer.Android.com/reference/androidx/navigation/ui/AppBarConfiguration.OnNavigateUpListenerhttps://developer.Android.com/jetpack/docs/release-notes
Im 2.1.0-alpha06
Wenn Sie nur im aktuellen Fragment mit Backpress umgehen möchten
requireActivity().onBackPressedDispatcher.addCallback([email protected]) {
// handle back event
}
Für die ganze Aktivität
requireActivity().onBackPressedDispatcher.addCallback() {
// handle back event
}
Wenn Sie nur das aktuelle Fragment schließen möchten, das Sie viewLifecycleOwner übergeben müssen, wird abhängig von Ihrer Logik der folgende Code angezeigt:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
requireActivity().finish()
}
})
Wenn Sie jedoch die Anwendung auf backPressed schließen möchten, egal von welchem Fragment (wahrscheinlich möchten Sie das nicht!), Übergeben Sie den viewLifecycleOwner nicht. Wenn Sie die Schaltfläche "Zurück" deaktivieren möchten, führen Sie in handleOnBackPressed () keine Aktionen aus. Siehe unten:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// do nothing it will disable the back button
}
})
Für alle, die eine Kotlin-Implementierung suchen, siehe unten.
Beachten Sie, dass OnBackPressedCallback
anscheinend nur zum Bereitstellen eines benutzerdefinierten Zurückverhaltens für die integrierte Software-/Hardware-Zurückschaltfläche und nicht für die Zurückpfeiltaste/Start als Aufwärtsschaltfläche in der Aktionsleiste/Symbolleiste verwendet wird. Um auch das Verhalten für die Schaltfläche "Zurück" in der Aktionsleiste/Symbolleiste zu überschreiben, stelle ich die Lösung bereit, die für mich funktioniert. Wenn dies ein Fehler ist oder Sie eine bessere Lösung für diesen Fall kennen, kommentieren Sie bitte.
build.gradle
...
implementation "androidx.appcompat:appcompat:1.1.0-rc01"
implementation "androidx.navigation:navigation-fragment-ktx:2.0.0"
implementation "androidx.navigation:navigation-ui-ktx:2.0.0"
...
MainActivity.kt
...
import androidx.appcompat.app.AppCompatActivity
...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
...
val navController = findNavController(R.id.nav_Host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
// This line is only necessary if using the default action bar.
setupActionBarWithNavController(navController, appBarConfiguration)
// This remaining block is only necessary if using a Toolbar from your layout.
val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setupWithNavController(navController, appBarConfiguration)
// This will handle back actions initiated by the the back arrow
// at the start of the toolbar.
toolbar.setNavigationOnClickListener {
// Handle the back button event and return to override
// the default behavior the same way as the OnBackPressedCallback.
// TODO(reason: handle custom back behavior here if desired.)
// If no custom behavior was handled perform the default action.
navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
/**
* If using the default action bar this must be overridden.
* This will handle back actions initiated by the the back arrow
* at the start of the action bar.
*/
override fun onSupportNavigateUp(): Boolean {
// Handle the back button event and return true to override
// the default behavior the same way as the OnBackPressedCallback.
// TODO(reason: handle custom back behavior here if desired.)
// If no custom behavior was handled perform the default action.
val navController = findNavController(R.id.nav_Host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
MyFragment.kt
...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// Handle the back button event
}
}
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback)
}
}
Die offizielle Dokumentation finden Sie unter https://developer.Android.com/guide/navigation/navigation-custom-back
Versuche dies. Ich denke das wird dir helfen.
überschreiben Spaß onBackPressed () { when (mNavController.getCurrentDestination () !!. getId ()) {
R.id.loginFragment -> {
onWarningAlertDialog(this, "Alert", "Do you want to close this application ?")
}
R.id.registerFragment -> {
super.onBackPressed()
}
}
}
privater Spaß onWarningAlertDialog (mainActivity: MainActivity, s: String, s1: String) {
val dialogBuilder = AlertDialog.Builder(this) dialogBuilder.setMessage(/*""*/s1) .setCancelable(false) .setPositiveButton("Proceed", DialogInterface.OnClickListener { dialog, id -> finish() }) .setNegativeButton("Cancel", DialogInterface.OnClickListener { dialog, id -> dialog.cancel() }) // create dialog box val alert = dialogBuilder.create() // set title for alert dialog box alert.setTitle("AlertDialogExample") // show alert dialog alert.show() }
Wenn Sie BaseFragment für Ihre App verwenden, können Sie Ihrem Basisfragment onBackPressedDispatcher hinzufügen.
//Make a BaseFragment for all your fragments
abstract class BaseFragment : Fragment() {
private lateinit var callback: OnBackPressedCallback
/**
* SetBackButtonDispatcher in OnCreate
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setBackButtonDispatcher()
}
/**
* Adding BackButtonDispatcher callback to activity
*/
private fun setBackButtonDispatcher() {
callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
onBackPressed()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
/**
* Override this method into your fragment to handleBackButton
*/
open fun onBackPressed() {
}
}
Überschreiben Sie onBackPressed () in Ihrem Fragment, indem Sie das Basisfragment erweitern
//How to use this into your fragment
class MyFragment() : BaseFragment(){
private lateinit var mView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mView = inflater.inflate(R.layout.fragment_my, container, false)
return mView.rootView
}
override fun onBackPressed() {
//Write your code here on back pressed.
}
}
Hier ist meine Lösung
Verwenden androidx.appcompat.app.AppCompatActivity
für die Aktivität, die das Fragment NavHostFragment
enthält.
Definieren Sie die folgende Schnittstelle und implementieren Sie sie in alle Navigationszielfragmente
interface InterceptionInterface {
fun onNavigationUp(): Boolean
fun onBackPressed(): Boolean
}
Überschreiben Sie in Ihrer Aktivität onSupportNavigateUp
und onBackPressed
:
override fun onSupportNavigateUp(): Boolean {
return getCurrentNavDest().onNavigationUp() || navigation_Host_fragment.findNavController().navigateUp()
}
override fun onBackPressed() {
if (!getCurrentNavDest().onBackPressed()){
super.onBackPressed()
}
}
private fun getCurrentNavDest(): InterceptionInterface {
val currentFragment = navigation_Host_fragment.childFragmentManager.primaryNavigationFragment as InterceptionInterface
return currentFragment
}
Diese Lösung hat den Vorteil, dass sich die Fragmente des Navigationsziels nicht um die Aufhebung der Registrierung ihrer Listener kümmern müssen, sobald sie getrennt werden.
Ich habe Jurij Pitulja-Lösung ausprobiert, aber ich konnte getOnBackPressedDispatcher oder addOnBackPressedCallback nicht finden, auch wenn Kiryl Tkachs Lösung das aktuelle Fragment nicht gefunden hat.
interface OnBackPressedListener {
fun onBackPressed(): Boolean
}
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_Host_fragment)
val currentFragment = navHostFragment?.childFragmentManager!!.fragments[0]
if (currentFragment !is OnBackPressedListener || !(currentFragment as OnBackPressedListener).onBackPressed()) super.onBackPressed()
auf diese Weise können Sie fragmentarisch entscheiden, ob die Aktivität die Kontrolle über den zurückgedrückten Bereich übernehmen soll oder nicht.
Alternativ haben Sie BaseActivity für alle Ihre Aktivitäten, die Sie auf diese Weise implementieren können
override fun onBackPressed() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_Host_fragment)
if (navHostFragment != null){
val currentFragment = navHostFragment.childFragmentManager.fragments[0]
if (currentFragment !is AuthContract.OnBackPressedListener ||
!(currentFragment as AuthContract.OnBackPressedListener).onBackPressed()) super.onBackPressed()
} else {
super.onBackPressed()
}
}
Ich schrieb in Hauptaktivität wie folgt,
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.my_nav_Host_fragment).navigateUp(appBarConfiguration)
}