O componente Navigation permite que você anexe dados a uma operação de navegação definindo argumentos para um destino. Por exemplo, um destino de perfil de usuário pode usar um argumento de ID do usuário para determinar qual usuário mostrar.
Em geral, recomendamos que você dê preferência para a transmissão da quantidade mínima de dados
entre destinos. Por exemplo, transmita uma chave para extrair um objeto
em vez de transmitir o próprio objeto, já que o espaço total para todos os estados salvos
é limitado no Android. Se você precisar transmitir grandes quantidades de dados, use
um ViewModel
, conforme descrito na
Visão geral do ViewModel.
Definir argumentos de destino
Para transmitir dados entre destinos, primeiro defina o argumento adicionando-o ao destino que o recebe seguindo estas etapas:
- No Navigation Editor, clique no destino que recebe o argumento.
- No painel Attributes, clique em Add (+).
- Na janela Add Argument Link que é exibida, insira o nome do argumento, o tipo dele, se ele é anulável e um valor padrão, se necessário.
- Clique em Adicionar. O argumento aparecerá na lista Arguments no painel Attributes.
- Clique na ação correspondente que leva você a esse destino. No painel Attributes, seu argumento recém-adicionado aparecerá na seção Argument Default Values.
Veja que o argumento foi adicionado em XML. Clique na guia Text para alternar para a visualização XML e observe que seu argumento foi adicionado ao destino que o recebe. Confira um exemplo abaixo:
<fragment android:id="@+id/myFragment" > <argument android:name="myArg" app:argType="integer" android:defaultValue="0" /> </fragment>
Tipos de argumentos com suporte
A biblioteca "Navigation" é compatível com os seguintes tipos de argumento:
Tipo | Sintaxe app:argType | Compatibilidade com valores padrão | Processado por rotas | Nullable |
---|---|---|---|---|
Número inteiro | app:argType="integer" | Sim | Sim | Não |
Flutuante | app:argType="float" | Sim | Sim | Não |
Longo | app:argType="long" | Sim: os valores padrão precisam sempre terminar com um sufixo "L" (por exemplo, "123L"). | Sim | Não |
Booleano | app:argType="boolean" | Sim: "verdadeiro" ou "falso" | Sim | Não |
String | app:argType="string" | Sim | Sim | Sim |
Referência de recursos | app:argType="reference" | Sim: os valores padrão precisam estar no formato "@resourceType/resourceName" (por exemplo, "@style/myCustomStyle") ou "0" | Sim | Não |
Parcelable personalizado | app:argType="<type>", em que <type> é o nome da classe totalmente qualificado do Parcelable |
Compatível com um valor padrão de "@null". Não é compatível com outros valores padrão. | Não | Sim |
Serializável personalizado | app:argType="<type>", em que <type> é o nome da classe totalmente qualificado do Serializable |
Compatível com um valor padrão de "@null". Não é compatível com outros valores padrão. | Não | Sim |
Enumeração personalizada | app:argType="<type>", em que <type> é o nome totalmente qualificado do tipo enumerado | Sim: os valores padrão precisam corresponder ao nome não qualificado (por exemplo, "SUCCESS" para corresponder a MyEnum.SUCCESS). | Não | Não |
Se um tipo de argumento for compatível com valores nulos, você poderá declarar um valor padrão de
null usando android:defaultValue="@null"
.
Rotas, links diretos e URIs com argumentos podem ser analisados usando strings. Não é possível fazer isso usando tipos de dados personalizados, como Parcelables e Serializables, conforme mostrado na tabela anterior. Para transmitir dados complexos personalizados, armazene os dados em outro lugar, como um ViewModel ou banco de dados, e transmita apenas um identificador durante a navegação. Em seguida, extraia os dados no novo local após a conclusão da navegação.
Quando você escolhe um dos tipos personalizados, a caixa de diálogo Select Class é exibida e solicita que você escolha a classe correspondente para esse tipo. A guia Project permite escolher uma classe do projeto atual.
Escolha o <inferred type> para fazer com que a biblioteca "Navigation" determine o tipo com base no valor fornecido.
Marque Array para indicar que o argumento precisa ser uma matriz do valor de Type selecionado. Observe o seguinte:
- Matrizes de enumerações e matrizes de referências de recursos não são compatíveis.
- As matrizes oferecem suporte a valores anuláveis, independente do suporte a valores
anuláveis do tipo em questão. Por exemplo, o uso de
app:argType="integer[]"
permite usarapp:nullable="true"
para indicar que transmitir uma matriz nula é aceitável. - As matrizes oferecem suporte a um único valor padrão, "@null". As matrizes não oferecem suporte a nenhum outro valor padrão.
Substituir um argumento de destino em uma ação
Argumentos e valores padrão no nível de destino são usados por todas as ações que navegam até o destino. Se necessário, modifique o valor padrão de um argumento (ou defina um, se ele ainda não existir), definindo um argumento no nível da ação. Esse argumento precisa ter o mesmo nome e tipo que o argumento declarado no destino.
O XML abaixo declara uma ação com um argumento que substitui o do nível de destino do exemplo anterior:
<action android:id="@+id/startMyFragment"
app:destination="@+id/myFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>
Usar o Safe Args para transmitir dados com a segurança de tipo
O componente Navigation tem um plug-in para Gradle chamado Safe Args que gera classes simples de objeto e builder para navegação com segurança de tipos e acesso a qualquer argumento associado. O Safe Args é altamente recomendado para a navegação e a transmissão de dados, porque garante a segurança de tipo.
Se você não estiver usando o Gradle, não será possível usar o plug-in Safe Args. Nessas situações, você pode usar Bundles para transmitir dados diretamente.
Para adicionar Safe Args
ao seu projeto, inclua o seguinte classpath
no seu arquivo build.gradle
de nível superior:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.8.4" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.8.4" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
Você também precisa aplicar um dos dois plug-ins disponíveis.
Para gerar um código de linguagem Java adequado para módulos Java ou Java e Kotlin mistos, adicione
esta linha ao arquivo build.gradle
do seu app ou módulo:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Como alternativa, para gerar o código Kotlin adequado para módulos somente Kotlin, adicione:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
Você precisa ter android.useAndroidX=true
no
arquivo gradle.properties
, como mostrado em
Migrar para o AndroidX.
Depois de ativar o Safe Args, o código gerado vai conter as classes e métodos com segurança de tipos abaixo para cada ação, bem como para cada destino de envio e recebimento.
Uma classe é criada para cada destino no qual uma ação se origina. O nome dessa classe é o nome do destino de origem, anexado com a palavra "Directions". Por exemplo, se o destino de origem for um fragmento com o nome
SpecifyAmountFragment
, a classe gerada será chamada deSpecifyAmountFragmentDirections
.Essa classe tem um método para cada ação definida no destino de origem.
Para cada ação usada para transmitir o argumento, é criada uma classe interna cujo nome vem da ação. Por exemplo, se a ação for chamada de
confirmationAction,
, a classe será denominadaConfirmationAction
. Se a ação contiver argumentos semdefaultValue
, use a classe de ação associada para definir o valor dos argumentos.Uma classe é criada para o destino de recebimento. O nome dessa classe é o nome do destino, anexado com a palavra "Args". Por exemplo, se o fragmento de destino tiver o nome
ConfirmationFragment,
, a classe gerada seráConfirmationFragmentArgs
. Use o métodofromBundle()
dessa classe para extrair os argumentos.
O exemplo abaixo mostra como usar esses métodos para definir um argumento e
transmiti-lo ao 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); }
No código do destino de recebimento, use o método getArguments()
para extrair o pacote e usar o conteúdo dele. Ao usar as dependências -ktx
,
os usuários do Kotlin também podem usar a delegação de propriedade by navArgs()
para acessar 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 + ""); }
Usar o Safe Args com uma ação global
Ao usar o Safe Args com uma
ação global,
é necessário fornecer um valor android:id
para o elemento raiz <navigation>
, como
mostrado no exemplo a seguir:
<?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>
O componente de navegação gera uma classe Directions
para o elemento <navigation>
com base no valor android:id
. Por exemplo, se você tiver um elemento <navigation>
com android:id=@+id/main_nav
, a classe gerada terá o nome
MainNavDirections
. Todos os destinos no elemento <navigation>
têm
métodos gerados para acessar todas as ações globais associadas usando
os mesmos métodos descritos na seção anterior.
Transmitir dados entre destinos com objetos Bundle
Se você não estiver usando o Gradle, ainda poderá transmitir argumentos entre destinos
usando objetos Bundle
. Crie um objeto Bundle
e o transmita para o destino
usando navigate()
, como no exemplo abaixo:
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);
No código do destino de recebimento, use o método getArguments()
para
extrair o Bundle
e usar o conteúdo:
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"));
Transmitir dados para o destino inicial
Você pode transmitir dados para o destino inicial do seu app. Primeiro, construa explicitamente
um Bundle
que armazene os dados. Em seguida,
use uma das abordagens abaixo para transmitir o Bundle
para o destino inicial:
- Se você estiver criando seu
NavHost
programaticamente, chameNavHostFragment.create(R.navigation.graph, args)
, em queargs
é oBundle
que contém seus dados. - Caso contrário, você pode definir argumentos de destino inicial chamando uma das
sobrecargas de
NavController.setGraph()
abaixo:- Para usar o ID do gráfico:
navController.setGraph(R.navigation.graph, args)
- Para usar o próprio gráfico:
navController.setGraph(navGraph, args)
- Para usar o ID do gráfico:
Para extrair os dados no destino inicial, chame
Fragment.getArguments()
.
Considerações sobre o ProGuard
Se você estiver reduzindo seu código,
precisará impedir que os nomes de classes Parcelable
, Serializable
e Enum
sejam ofuscados como parte do processo de minificação. Isso pode ser feito de duas maneiras:
- Usando anotações @Keep.
- Usando regras keepnames.
As subseções abaixo apresentam essas abordagens.
Usar anotações @Keep
O exemplo abaixo adiciona as anotações @Keep
a definições de classe 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 { ... }
Usar regras keepnames
Você também pode adicionar regras keepnames
ao seu arquivo proguard-rules.pro
, como mostrado
no exemplo a seguir:
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
Outros recursos
Para saber mais sobre navegação, consulte os recursos adicionais a seguir.