En Android 13, el onBackPresionado() el método ha quedado obsoleto y reemplazado por OnBackPressedDispatcher para gestionar la navegación hacia atrás. Este nuevo enfoque Es más flexible y sensible al ciclo de vida, lo que lo hace ideal para aplicaciones con múltiples fragmentos o necesidades de comportamiento personalizado. En esta publicación veremos el uso de OnBackPressedDispatcher en tareas y fragmentos para ayudarlo a administrar la navegación hacia atrás de manera efectiva.
¿Cómo usarlo?
- En su actividad/fragmento, indique una OnBackPressedCallback variable
- Grabar la devolución de llamada en al inicio() usando onBackPressedDispatcher.addCallback(devolución de llamada)
- Cuando el usuario navega hacia atrás, manejarOnBackPressed() Se llamará donde podrá definir su lógica personalizada.
- Una vez que haya terminado de escuchar la navegación hacia atrás, puede permitir la navegación hacia atrás predeterminada configurando esEanble A FALSO.
- Eliminar devolución de llamada entrante al detener() método usando eliminar() función. Esto es opcional porque OnBackPressedCallback es un componente sensible al ciclo de vida, pero en algunos casos es necesario para evitar comportamientos inesperados.
1. Ejemplo: guardar el formulario al presionar Atrás
Considere un ejemplo en el que un usuario ingresó datos en un formulario pero presiona hacia atrás antes de guardarlos. En este caso, podemos omitir la navegación hacia atrás para mostrar un mensaje de confirmación, asegurándonos de no perder ningún dato no guardado.
- Colocar enableOnBackInvokedCallback A REAL En AndroidManifest.xml
-
Agregue algunos campos de texto a su archivo de diseño para simular un formulario. Aquí estamos creando dos campos para nombre y correo electrónico.
- Abra su archivo de diseño de actividad/fragmento y realice los siguientes cambios.
- A OnBackPressedCallback está definido y adjunto al fragmento en al inicio() y eliminado por dentro onDestroyView()
- De forma predeterminada, deshabilitamos el interceptor posterior configurando está habilitado A FALSO para que pueda ocurrir la navegación hacia atrás predeterminada.
- Se agrega una lista de cambios de texto a los campos de texto y cuando el usuario ingresa texto, el interceptor posterior se habilita mediante la configuración está habilitado A REAL. Cuando los campos de texto están vacíos, desactivamos el interceptor posterior. backPressCallback.isEnabled = !binding.name.text.isNullOrBlank() || !binding.email.text.isNullOrBlank()
- En este punto, si el usuario presiona Atrás cuando el formulario contiene algunos datos, manejarOnBackPressed() Se invoca y aparece un cuadro de diálogo de confirmación para guardar los datos.
- Si el usuario opta por cancelar el guardado, volvemos a la pantalla anterior llamando encontrarNavController().popBackStack()
paquete info.androidhive.androidbacknavigation importar android.os.Bundle importar android.text.Editable importar android.text.TextWatcher importar android.view.LayoutInflater importar android.view.View importar android.view.ViewGroup importar androidx.activity.OnBackPressedCallback importar androidx .fragment.app.Importaciones de fragmentos androidx.navigation.fragment.findNavController import com.google.android.material.dialog.MaterialAlertDialogBuilder import info.androidhive.androidbacknavigation.databinding.FragmentProfileBinding clase ProfileFragment: Fragment() { enlace de valor privado diferido { FragmentProfileBinding .inflate(layoutInflater) } valor privado volverPresionarDevolución de llamada: OnBackPressedCallback = objeto: OnBackPressedCallback(true) { anular divertido handleOnBackPressed() { showConfirmationDialog() } } anular divertido onCreateView( inflador: LayoutInflater, contenedor: ViewGroup?, saveInstanceState: Bundle? ): Ver { return bind.root } /** * Mostrar atrás presione confirmar cuando el formulario tenga datos no guardados * */ diversión privada showConfirmationDialog() { contexto?.let { MaterialAlertDialogBuilder(it).setTitle(resources.getString(R.string.title) ) .setMessage(resources.getString(R.string.unsaved_message)) .setNegativeButton(resources.getString(R. cadena.cancelar)) { _, _ -> }.setPositiveButton(resources.getString(R.string.accept)) { _, _ -> findNavController().popBackStack() }.show() } } var privada textChangeListener: TextWatcher = objeto: TextWatcher { anular diversión beforeTextChanged(p0 : CharSequence?, p1: Int, p2: Int, p3: Int ) {} anula la diversión onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { // alternar pulsación de devolución de llamada cuando se cambian los datos del formulario toggleBackPress() } anular diversión afterTextChanged(p0: ¿Editable?) { } } /** * Habilitar la devolución de llamada cuando el formulario contiene datos no guardados * */ diversión privada toggleBackPress() { backPressCallback.isEnabled = !binding.name.text.isNullOrBlank() || !binding.email.text.isNullOrBlank() } anula fun onViewCreated(view: View, saveInstanceState: Bundle?) { super.onViewCreated(view, saveInstanceState) bind.buttonSave.setOnClickListener { findNavController().popBackStack() } actividad?. onBackPressedDispatcher?.addCallback(backPressCallback) // deshabilita la devolución de llamada al presionar de forma predeterminada backPressCallback.isEnabled = false initForm() } diversión privada initForm() { bind.apply { name.addTextChangedListener(textChangeListener) email.addTextChangedListener(textChangeListener) } } sobrescribe fun onDestroyView() { super.onDestroyView() // eliminar la devolución de llamada no siempre es necesario // pero mientras se usa el componente de navegación, el antiguo oyente todavía está conectado // después de la navegación hacia atrás backPressCallback.remove() } }
2. Implementado doble pulsación hacia atrás para salir de la aplicación.
Consideremos otro escenario común en el que el usuario necesita presionar el botón Atrás dos veces para salir de la aplicación. Esta característica generalmente se implementa para evitar salidas accidentales. El siguiente ejemplo muestra cómo utilizar el archivo OnBackPressedCallback cuando tienes el Componente de navegación.
- Al utilizar el oyente de destino del componente de navegación, el interceptor posterior se habilita solo en la pantalla de inicio. De lo contrario, también quedará atrapado en fragmentos secundarios. private val navControllerListener = NavController.OnDestinationChangedListener { _, destino, _ -> // habilitar la devolución de llamada cuando el destino es el fragmento de inicio backPressCallback.isEnabled = destino.id == R.id.HomeFragment }
- Cuando presiona el botón Atrás, aparece un mensaje Toast que le pide que presione Atrás nuevamente inmediatamente. Si el usuario presiona el botón Atrás dentro de 1 segundo, la aplicación se cerrará. De lo contrario, el interceptor trasero se habilitará nuevamente. diversión privada showBackToast() { Toast.makeText(this, R.string.press_back_again, Toast.LENGTH_SHORT).show() backPressCallback.isEnabled = false GlobalScope.launch { delay(1000) // el usuario no presionó hacia atrás en 1 segundo. Habilite la devolución de llamada de retroceso nuevamente backPressCallback.isEnabled = true } }
Aquí está el código completo. paquete info.androidhive.androidbacknavigation importar android.os.Bundle importar android.util.Log importar com.google.android.material.snackbar.Snackbar importar androidx.appcompat.app.AppCompatActivity importar androidx.navigation.findNavController importar androidx.navigation.ui .AppBarConfiguration importar androidx.navigation.ui.navigateUp importar androidx.navigation.ui.setupActionBarWithNavController importar android.view.Menu importar android.view.MenuItem importar android.widget.Toast importar androidx.activity.OnBackPressedCallback importar androidx.navigation.NavController importar información .androidhive.androidbacknavigation.databinding.ActivityMainBinding importar kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch Clase MainActivity: AppCompatActivity() { enlace de valor privado de lazy(LazyThreadSafetyMode.NONE) { ActivityMainBinding.inflate(layoutInflate) } lateinit privado var appBarConfiguration: AppBarConfiguration private lateinit var navController: NavController private val backPressCallback: OnBackPressedCallback = object: OnBackPressedCallback(true) { override fun handleOnBackPressed() { showBackToast() } } /** * Escuche la navegación de destino y habilite la devolución de llamada solo en la pantalla de inicio * */ val privado navControllerListener = NavController.OnDestinationChangedListener { _, destino, _ -> // habilitar la devolución de llamada cuando el destino es el fragmento de inicio backPressCallback.isEnabled = destino.id == R. id.HomeFragment } /** * Mostrar brindis y deshabilitar la devolución de llamada presionando hacia atrás. Si el usuario presiona nuevamente dentro de 1 segundo, * se realizará la navegación hacia atrás; de lo contrario, la devolución de llamada al presionar hacia atrás se habilitará nuevamente * */ @OptIn(DelicateCoroutinesApi::class) private fun showBackToast() { Toast.makeText(this, R .string.press_back_again, Toast.LENGTH_SHORT).show() backPressCallback.isEnabled = false GlobalScope.launch { delay(1000) // el usuario no presionó Atrás en 1 segundo. Habilite la devolución de llamada nuevamente presionando backPressCallback.isEnabled = true } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) setSupportActionBar(binding.toolbar) navController = findNavController(R.id. nav_host_fragment_content_main) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) } anular fun onSupportNavigateUp(): booleano { val navController = findNavController(R.id.nav_host_fragment_content_main) return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } anula fun onStart() { super.onStart() onBackPressedDispatcher.addCallback(backPressCallback) } anula fun onStop() { super.onStop() backPressCallback.remove() } anula fun onResume() { super.onResume () navController.addOnDestinationChangedListener(navControllerListener) } anula fun onPause() { super.onPause() navController.removeOnDestinationChangedListener(navControllerListener) } }
Déjame saber tus preguntas en la sección de comentarios a continuación.
¡Saludos!
Feliz programación 🤗