wake-up-neo.net

Rücktaste in der Android-Navigationskomponente

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?

 enter image description here

Soll ich "Pop to" verwenden?

9
Kiryl Tkach

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.

9
Kiryl Tkach

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 AppCompatActivityoder FragmentActivity und in Gradle-Datei:

 implementation 'androidx.appcompat:appcompat:{lastVersion}'
34
Jurij Pitulja

Der Ansatz empfohlen besteht darin, der Aktivität OnBackPressedCallback ein OnBackPressedDispatcher hinzuzufügen.

requireActivity().onBackPressedDispatcher.addCallback { 
    // handle back event
}
5
user11566289

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()
        }
    }
} 
3
Alex

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

3
Onur D.

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
}
2
solaza

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
        }
    })
0
Junia Montana

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

0
Abtin Gramian

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()
}
0
Anandharaj R

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.
}

}

0
Happy Singh

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.

0
Nantoka

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()
    }
}
0

Ich schrieb in Hauptaktivität wie folgt,

override fun onSupportNavigateUp(): Boolean {
        return findNavController(R.id.my_nav_Host_fragment).navigateUp(appBarConfiguration)
    }   
0