Navigation te permite adjuntar datos a una operación de navegación si defines los argumentos de un destino. Por ejemplo, el destino de un perfil de usuario podría tomar el argumento de ID del usuario para determinar a quién mostrar el contenido.
En general, debes optar por pasar solo la cantidad mínima de datos entre destinos. Por ejemplo, debes pasar una clave para recuperar un objeto en lugar de pasar el objeto en sí, ya que el espacio total para los estados guardados en Android es limitado. Si necesitas pasar grandes cantidades de datos, usa un objeto ViewModel
como se describe en Descripción general de ViewModel.
Cómo definir argumentos de destino
Si deseas pasar datos entre destinos, primero define el argumento agregándolo al destino que lo recibe. Para ello, sigue los pasos que se indican a continuación:
- En el Editor de Navigation, haz clic en el destino que recibe el argumento.
- En el panel Attributes, haz clic en Add (+).
- En la ventana Add Argument Link, ingresa el nombre del argumento, el tipo, si es anulable y un valor predeterminado, si es necesario.
- Haz clic en Add. Observa que el argumento ahora aparece en la lista Arguments del panel Attributes.
- A continuación, haz clic en la acción correspondiente que te lleva al destino. En el panel Attributes, ahora deberías ver el argumento que acabas de agregar en la sección Argument Default Values.
También puedes ver que el argumento se agregó en XML. Haz clic en la pestaña Text para activar o desactivar la vista XML, y observa que tu argumento se agregó al destino que recibe el argumento. A continuación, se muestra un ejemplo:
<fragment android:id="@+id/myFragment" > <argument android:name="myArg" app:argType="integer" android:defaultValue="0" /> </fragment>
Tipos de argumento compatibles
La biblioteca de Navigation admite los siguientes tipos de argumento:
Tipo | Sintaxis app:argType | Compatibilidad con valores predeterminados | Administrado por rutas | Anulable |
---|---|---|---|---|
Entero | app:argType="integer" | Sí | Sí | No |
Flotante | app:argType="float" | Sí | Sí | No |
Largo | app:argType="long" | Sí; los valores predeterminados siempre deben terminar con un sufijo "L" (p. ej., "123L") | Sí | No |
Booleano | app:argType="boolean" | Sí; valores "true" o "false" | Sí | No |
String | app:argType="string" | Sí | Sí | Sí |
Referencia del recurso | app:argType="reference" | Sí; los valores predeterminados deben tener el formato "@resourceType/resourceName" (p. ej., "@style/myCustomStyle") o "0" | Sí | No |
Parcelable personalizado | app:argType="<type>", donde <type> es el nombre de clase completamente calificado de Parcelable |
Admite un valor predeterminado de "@null". No admite otros valores predeterminados. | No | Sí |
Serializable personalizado | app:argType="<type>", donde <type> es el nombre de clase completamente calificado de Serializable |
Admite un valor predeterminado de "@null". No admite otros valores predeterminados. | No | Sí |
Enumeración personalizada | app:argType="<type>", donde <type> es el nombre completamente calificado de la enum. | Sí. Los valores predeterminados deben coincidir con el nombre no calificado (p. ej., "SUCCESS" para coincidir con MyEnum.SUCCESS). | No | No |
Si un tipo de argumento admite valores nulos, puedes declarar un valor predeterminado nulo mediante android:defaultValue="@null"
.
Las rutas, los vínculos directos y los URI con sus argumentos se pueden analizar a partir de cadenas. Eso no es posible con tipos de datos personalizados como Parcelables y Serializables, como se ve en la tabla anterior. Para pasar datos complejos, almacena los datos en otro lugar, como ViewModel o una base de datos, y solo pasa un identificador mientras navegas; luego, recupera los datos en la ubicación nueva una vez que haya finalizado la navegación.
Cuando eliges uno de los tipos predeterminados, aparece el diálogo Select Class y te pide que elijas la clase correspondiente para ese tipo. La pestaña Project te permite elegir una clase de tu proyecto actual.
Puedes seleccionar <inferred type> para que la biblioteca de Navigation determine el tipo en función del valor provisto.
Puedes marcar Array para indicar que el argumento debería ser un array del valor Type seleccionado. Ten en cuenta lo siguiente:
- No se admiten los arrays de enumeraciones y los de referencias a recursos.
- Los arrays admiten valores anulables, independientemente de la compatibilidad con valores anulables del tipo subyacente. Por ejemplo, usar
app:argType="integer[]"
te permite usarapp:nullable="true"
para indicar que se acepta un array nulo. - Los arrays admiten un solo valor predeterminado: "@null". No admiten ningún otro.
Cómo anular un argumento de destino en una acción
Todas las acciones que navegan al destino usan valores predeterminados y argumentos a nivel de destino. Si es necesario, puedes anular el valor predeterminado de un argumento (o definir uno si todavía no existe). Para ello, define un argumento a nivel de la acción. Este argumento debe tener el mismo nombre y tipo que el declarado en el destino.
El siguiente código XML declara una acción con un argumento que anula el argumento a nivel de destino del ejemplo anterior:
<action android:id="@+id/startMyFragment"
app:destination="@+id/myFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>
Cómo usar Safe Args para pasar datos con seguridad de tipos
El componente de Navigation tiene un complemento de Gradle llamado "Safe Args" que genera clases de objetos y compiladores simples que permiten una navegación de tipo seguro y acceso a cualquier argumento asociado. Se recomienda el uso de Safe Args para navegar y pasar datos, ya que garantiza la seguridad de tipos.
Si no usas Gradle, no puedes usar el complemento Safe Args. En esas situaciones, puedes utilizar Bundles para pasar datos de forma directa.
Para agregar Safe Args a tu proyecto, incluye la siguiente classpath
en tu archivo build.gradle
de nivel superior:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.8.0" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.8.0" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
También debes aplicar uno de los dos complementos disponibles.
Para generar código de lenguaje Java adecuado para Java o módulos combinados de Java y Kotlin, agrega esta línea al archivo build.gradle
de tu app o módulo:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Como alternativa, para generar el código de Kotlin adecuado para módulos solo de Kotlin, agrega lo siguiente:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
Tienes que tener el objeto android.useAndroidX=true
en tu archivo gradle.properties
, según se indica en Cómo migrar a AndroidX.
Después de habilitar Safe Args, el código generado contendrá las siguientes clases y métodos seguros para cada acción, además de cada destino de envío y recepción.
Se crea una clase por cada destino en el que se origina una acción. El nombre de esta clase es el nombre del destino de origen, unido a la palabra "Directions". Por ejemplo, si el destino de origen es un fragmento que se llama
SpecifyAmountFragment
, la clase generada se llamaráSpecifyAmountFragmentDirections
.Esa clase tiene un método para cada acción definida en el destino de origen.
Para cada acción que se usa para pasar el argumento, se crea una clase interna cuyo nombre se basa en la acción. Por ejemplo, si la acción se llama
confirmationAction,
, la clase se llamaráConfirmationAction
. Si tu acción contiene argumentos sin undefaultValue
, debes usar la clase de acción asociada para configurar el valor de los argumentos.Se crea una clase para el destino de recepción. El nombre de esta clase es el nombre del destino, unido a la palabra "Args". Por ejemplo, si el fragmento de destino se llama
ConfirmationFragment,
, la clase generada se llamaráConfirmationFragmentArgs
. Usa el métodofromBundle()
de esta clase para recuperar los argumentos.
En el siguiente ejemplo, se muestra cómo utilizar estos métodos para configurar un argumento y pasarlo al método navigate()
:
Kotlin
override fun onClick(v: View) { val amountTv: EditText = view!!.findViewById(R.id.editTextAmount) val amount = amountTv.text.toString().toInt() val action = SpecifyAmountFragmentDirections.confirmationAction(amount) v.findNavController().navigate(action) }
Java
@Override public void onClick(View view) { EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount); int amount = Integer.parseInt(amountTv.getText().toString()); ConfirmationAction action = SpecifyAmountFragmentDirections.confirmationAction(); action.setAmount(amount); Navigation.findNavController(view).navigate(action); }
En el código del destino de recepción, usa el método getArguments()
para recuperar el paquete y usar su contenido. Cuando se usan las dependencias -ktx
, los usuarios de Kotlin también pueden usar el delegado de propiedades by navArgs()
para acceder a los argumentos.
Kotlin
val args: ConfirmationFragmentArgs by navArgs() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tv: TextView = view.findViewById(R.id.textViewAmount) val amount = args.amount tv.text = amount.toString() }
Java
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { TextView tv = view.findViewById(R.id.textViewAmount); int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount(); tv.setText(amount + ""); }
Cómo usar Safe Args con una acción global
Cuando usas Safe Args con una acción global, debes proporcionar un valor android:id
para su elemento raíz <navigation>
, como se muestra en el siguiente ejemplo:
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_nav" app:startDestination="@id/mainFragment"> ... </navigation>
Navigation genera una clase Directions
para el elemento <navigation>
que se basa en el valor android:id
. Por ejemplo, si tienes un elemento <navigation>
con android:id=@+id/main_nav
, la clase generada se llamará MainNavDirections
. Todos los destinos dentro del elemento <navigation>
generaron métodos para acceder a todas las acciones globales asociadas con los mismos métodos que se describieron en la sección anterior.
Cómo pasar datos entre los destinos con objetos Bundle
Si no utilizas Gradle, de todos modos, puedes pasar argumentos entre destinos con objetos Bundle
. Crea un objeto Bundle
y pásalo al destino con navigate()
, como en el siguiente ejemplo:
Kotlin
val bundle = bundleOf("amount" to amount) view.findNavController().navigate(R.id.confirmationAction, bundle)
Java
Bundle bundle = new Bundle(); bundle.putString("amount", amount); Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
En tu código de destino de recepción, usa el método getArguments()
para recuperar Bundle
y usar su contenido:
Kotlin
val tv = view.findViewById<TextView>(R.id.textViewAmount) tv.text = arguments?.getString("amount")
Java
TextView tv = view.findViewById(R.id.textViewAmount); tv.setText(getArguments().getString("amount"));
Cómo pasar datos al destino de inicio
Puedes pasar datos al destino de inicio de tu app. En primer lugar, debes compilar explícitamente un Bundle
que contenga los datos. Luego, recurre a uno de los siguientes enfoques para pasar el Bundle
al destino de inicio:
- Si creas tu
NavHost
de forma programática, llama aNavHostFragment.create(R.navigation.graph, args)
, dondeargs
es elBundle
que contiene tus datos. - De lo contrario, puedes configurar los argumentos del destino de inicio llamando a una de las siguientes sobrecargas de
NavController.setGraph()
:- Usa el ID del gráfico:
navController.setGraph(R.navigation.graph, args)
. - Usa el mismo gráfico:
navController.setGraph(navGraph, args)
.
- Usa el ID del gráfico:
Para recuperar los datos de tu destino de inicio, llama a Fragment.getArguments()
.
Consideraciones de ProGuard
Si quieres reducir tu código, debes evitar la ofuscación de los nombres de clase Parcelable
, Serializable
y Enum
como parte del proceso de reducción. Para eso, puedes usar uno de estos métodos:
- Usa anotaciones @Keep.
- Usa reglas keepnames.
Estos enfoques se describen en las siguientes subsecciones.
Cómo usar anotaciones @Keep
En el siguiente ejemplo, se agregan anotaciones @Keep
a las definiciones de las clases de modelos:
Kotlin
@Keep class ParcelableArg : Parcelable { ... } @Keep class SerializableArg : Serializable { ... } @Keep enum class EnumArg { ... }
Java
@Keep public class ParcelableArg implements Parcelable { ... } @Keep public class SerializableArg implements Serializable { ... } @Keep public enum EnumArg { ... }
Cómo usar reglas keepnames
También puedes agregar reglas keepnames
a tu archivo proguard-rules.pro
, como se muestra en el siguiente ejemplo:
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
Recursos adicionales
Para obtener más información acerca de la navegación, consulta los siguientes recursos adicionales.