The Android Developer Challenge is back! Submit your idea before December 2.

Cómo pasar datos entre destinos

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 de un 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, considera usar un ViewModel como se describe en Cómo compartir datos entre fragmentos.

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:

  1. En el editor de Navigation, haz clic en el destino que recibe el argumento.
  2. En el panel Attributes, haz clic en Add (+).
  3. En la ventana Add Argument Link, ingresa el nombre del argumento, el tipo, si es anulable y un valor predeterminado, si es necesario.
  4. Haz clic en Add. Observa que el argumento ahora aparece en la lista Arguments, en el panel Attributes.
  5. 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.
  6. También puedes ver que se agregó el argumento en XML. Haz clic en la pestaña Text para cambiar a la vista de XML y observa que se agregó tu argumento al destino que lo recibe. 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 ¿Admite valores predeterminados? ¿Admite valores nulos?
Entero app:argType="integer" No
Flotante app:argType="float" No
Largo app:argType="long" Sí; los valores predeterminados siempre deben terminar con un sufijo "L" (p. ej., "123L") No
Booleano app:argType="boolean" Si; valores "true" o "false" No
String app:argType="string"
Referencia del recurso app:argType="reference" Sí; los valores predeterminados deben tener el formato "@resourceType/resourceName" (p. ej., "@style/myCustomStyle") o "0" 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.
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.
Enumeración personalizada app:argType="<type>", donde <type> es el nombre completamente calificado de la enumeración. Sí. Los valores predeterminados deben coincidir con el nombre sin calificar (p. ej., "SUCCESS" de coincidir con MyEnum.SUCCESS). No

Si un tipo de argumento admite valores nulos, puedes declarar un valor predeterminado nulo mediante android:defaultValue="@null".

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 arreglo del valor Type seleccionado. Ten en cuenta que no se admiten los arreglos de enumeraciones y los de referencias a recursos. Los arreglos siempre son anulables, independientemente de la nulidad del tipo subyacente. Los arreglos admiten un solo valor predeterminado: "@null". No admiten ningún otro valor predeterminado.

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 código XML que se muestra a continuación 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 tipo

El componente de la arquitectura de Navigation tiene un componente de Gradle llamado Safe Args que genera clases simples de objeto y creador para acceder con seguridad de tipo a los argumentos especificados para destinos y acciones. Esta es la manera preferida de pasar datos cuando usas la navegación, ya que garantiza la seguridad de tipo.

En algunos casos, por ejemplo, si no usas Gradle, no puedes usar el complemento Safe Args. En estas situaciones, puedes utilizar Bundles para pasar datos directamente.

Después de habilitar Safe Args, tu código generado contendrá métodos de seguridad de tipo para cada acción, además de los destinos de envío y recepción. Estas clases se describen a continuació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 llamado SpecifyAmountFragment, la clase generada se llamará SpecifyAmountFragmentDirections.

    Esta clase tiene un método para cada acción definida en el destino de origen.

  • Para cada acción que se usa a fin de pasar el argumento, se crea una clase interna cuyo nombre está basado en la acción. Por ejemplo, si la acción se llama confirmationAction,, el nombre de la clase es ConfirmationAction. Si tu acción contiene argumentos sin un defaultValue, debes usar la clase de acción asociada para definir 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,, el nombre de la clase generada es ConfirmationFragmentArgs. Usa el método fromBundle() de esta clase para recuperar los argumentos.

En el siguiente ejemplo, se muestra cómo usar estos métodos para definir un argumento y pasárselo 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 de tu destino de recepción, usa el método getArguments() para recuperar el paquete y usar su contenido. Cuando usas las dependencias -ktx, los usuarios de Kotlin también pueden usar el delegado de la propiedad 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 tu elemento <navigation> raíz, 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 está basada 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 del elemento <navigation> tienen métodos generados para acceder a todas las acciones globales asociadas usando los mismos métodos que se describen en la sección anterior.

Cómo pasar datos entre los destinos mediante objetos Bundle

Si no usas Gradle, de todas maneras, puedes pasar argumentos entre los destinos mediante objetos Bundle. Crea un objeto Bundle y pásalo al destino mediante navigate(), como se muestra a continuación:

Kotlin

    var 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 el código de tu destino de recepción, usa el método getArguments() para recuperar el 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 construir de manera explícita un Bundle que contenga los datos. A continuación, usa uno de los siguientes métodos para pasar el Bundle al destino de inicio:

Para recuperar los datos en tu destino de inicio, llama a Fragment.getArguments().

Consideraciones de ProGuard

Si quieres reducir el tamaño de 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 ello, puedes usar uno de estos métodos:

Cómo usar anotaciones @Keep

En el siguiente ejemplo, se agregan anotaciones @Keep a las definiciones de clase de modelo:

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.

Ejemplos

Codelabs

Videos