A Navigation component é uma biblioteca que pode gerenciar navegação complexa, animação de transição, links diretos e argumento verificado em tempo de compilação passando entre as telas do app.
Este documento serve como um guia de propósito geral para migração de um app existente para o uso da Navigation component.
Em geral, a migração envolve as seguintes etapas:
Remover a lógica da IU específica da tela das atividades: retire a lógica da IU do app das atividades, garantindo que cada atividade possua apenas a própria lógica dos componentes da IU de navegação global, como uma
Toolbar
, enquanto delega a implementação de cada tela a um fragmento ou destino personalizado.Integrar a Navigation component: para cada atividade, crie um gráfico de navegação que contenha um ou mais fragmentos gerenciados por essa atividade. Substitua transações de fragmento por operações da Navigation component.
Adicionar destinos de atividades: substitua chamadas
startActivity()
por ações que usam destinos de atividades.Combinar atividades: combine gráficos de navegação nos casos em que várias atividades compartilham um mesmo layout.
Pré-requisitos
Este guia presume que você já tenha migrado o app para usar as bibliotecas AndroidX. Caso não tenha feito isso, migre seu projeto para usar o AndroidX antes de continuar.
Remover a lógica da IU específica da tela das atividades
Atividades são componentes em nível de sistema que facilitam uma interação gráfica entre o app e o Android. As atividades são registradas no manifesto do app para que o Android saiba quais delas estão disponíveis para iniciar. A classe da atividade permite que o app também reaja às mudanças no Android, como quando a IU do app está entrando ou saindo do primeiro plano, está em rotação e assim por diante. A atividade também pode servir como um lugar para compartilhar o estado entre telas.
No contexto do app, as atividades precisam servir como um host para navegação e manter a lógica e o conhecimento de como fazer a transição entre telas, transmitir dados e assim por diante. No entanto, é melhor deixar o gerenciamento dos detalhes da IU para uma parte menor e reutilizável. A implementação recomendada para esse padrão é a que usa fragmentos. Veja Atividade única: por que, quando e como para saber mais sobre as vantagens de usar fragmentos (link em inglês). A navegação é compatível com fragmentos por meio da dependência navegação-fragmento. A navegação é também compatível com tipos de destino personalizados.
Se o app não estiver usando fragmentos, a primeira coisa a fazer é migrar cada tela para usar um fragmento. Você não está removendo a atividade neste momento. Em vez disso, está criando um fragmento para representar a tela e separar a lógica da IU por responsabilidade.
Introdução de fragmentos
Para ilustrar o processo de introdução de fragmentos, vamos começar com o exemplo de um app que consiste em duas telas: uma tela de lista de produtos e uma tela de detalhes do produto. Clicar em um produto na tela de lista leva o usuário a uma tela de detalhes para saber mais sobre o produto.
Neste exemplo, as telas de lista e de detalhes são atividades separadas.
Criar um novo layout para hospedar a IU
Para introduzir um fragmento, comece pela criação de um novo arquivo de layout para a atividade para hospedar o fragmento. Isso substitui o layout atual de visualização de conteúdo da atividade.
Para uma visualização simples, você pode usar um FrameLayout
, como mostrado no
exemplo a seguir product_list_host
:
<FrameLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent" />
O atributo id
refere-se à seção de conteúdo em que depois adicionamos o
fragmento.
Em seguida, na função onCreate()
da atividade, modifique a referência do arquivo de layout
na função onCreate para apontar para esse novo arquivo de layout:
Kotlin
class ProductListActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { ... // Replace setContentView(R.layout.product_list) with the line below setContentView(R.layout.product_list_host) ... } }
Java
public class ProductListActivity extends AppCompatActivity { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { ... // Replace setContentView(R.layout.product_list); with the line below setContentView(R.layout.product_list_host); ... } }
O layout existente (product_list
, neste exemplo) é usado como a visualização raiz
do fragmento que você está prestes a criar.
Criar um fragmento
Crie um novo fragmento para gerenciar a IU da tela. É uma prática recomendada
ser consistente com o nome do host da sua atividade. O snippet abaixo usa
ProductListFragment
, por exemplo:
Kotlin
class ProductListFragment : Fragment() { // Leave empty for now. }
Java
public class ProductListFragment extends Fragment { // Leave empty for now. }
Mover a lógica de atividade para um fragmento
Depois de estabelecida a definição do fragmento, a próxima etapa é mover a lógica da IU
dessa tela da atividade para esse novo fragmento. Se você estiver vindo de uma
arquitetura baseada em atividades, provavelmente tem muita lógica de criação de visualizações
acontecendo na função onCreate()
da atividade.
Veja um exemplo de tela baseada em atividades com lógica de IU que precisamos mover:
Kotlin
class ProductListActivity : AppCompatActivity() { // Views and/or ViewDataBinding references, Adapters... private lateinit var productAdapter: ProductAdapter private lateinit var binding: ProductListActivityBinding ... // ViewModels, System Services, other Dependencies... private val viewModel: ProductListViewModel by viewModels() ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // View initialization logic DataBindingUtil.setContentView(this, R.layout.product_list_activity) // Post view initialization logic // Connect adapters productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener {...} // Subscribe to state viewModel.products.observe(this, Observer { myProducts -> ... }) // ...and so on } ... }
Java
public class ProductListActivity extends AppCompatActivity { // Views and/or ViewDataBinding references, adapters... private ProductAdapter productAdapter; private ProductListActivityBinding binding; ... // ViewModels, system services, other dependencies... private ProductListViewModel viewModel; ... @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // View initialization logic DataBindingUtil.setContentView(this, R.layout.product_list_activity); // Post view initialization logic // Connect adapters productAdapter = new ProductAdapter(productClickCallback); binding.productsList.setAdapter(productAdapter); // Initialize ViewModels and other dependencies ProductListViewModel viewModel = new ViewModelProvider(this).get(ProductListViewModel.java); // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener(v -> { ... }); // Subscribe to state viewModel.getProducts().observe(this, myProducts -> ... ); // ...and so on }
Sua atividade também pode estar controlando quando e como o usuário navega para a próxima tela, como mostrado no exemplo a seguir:
Kotlin
// Provided to ProductAdapter in ProductListActivity snippet. private val productClickCallback = ProductClickCallback { product -> show(product) } fun show(product: Product) { val intent = Intent(this, ProductActivity::class.java) intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id) startActivity(intent) }
Java
// Provided to ProductAdapter in ProductListActivity snippet. private ProductClickCallback productClickCallback = this::show; private void show(Product product) { Intent intent = new Intent(this, ProductActivity.class); intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId()); startActivity(intent); }
Dentro do seu fragmento, você distribui este trabalho entre
onCreateView()
e
onViewCreated()
,
com apenas a lógica de navegação restante na atividade:
Kotlin
class ProductListFragment : Fragment() { private lateinit var binding: ProductListFragmentBinding private val viewModel: ProductListViewModel by viewModels() // View initialization logic override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = DataBindingUtil.inflate( inflater, R.layout.product_list, container, false ) return binding.root } // Post view initialization logic override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // Connect adapters productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener {...} // Subscribe to state viewModel.products.observe(this, Observer { myProducts -> ... }) // ...and so on } // Provided to ProductAdapter private val productClickCallback = ProductClickCallback { product -> if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { (requireActivity() as ProductListActivity).show(product) } } ... }
Java
public class ProductListFragment extends Fragment { private ProductAdapter productAdapter; private ProductListFragmentBinding binding; // View initialization logic @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = DataBindingUtil.inflate( inflater, R.layout.product_list_fragment, container, false); return binding.getRoot(); } // Post view initialization logic @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // Connect adapters binding.productsList.setAdapter(productAdapter); // Initialize ViewModels and other dependencies ProductListViewModel viewModel = new ViewModelProvider(this) .get(ProductListViewModel.class); // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener(...) // Subscribe to state viewModel.getProducts().observe(this, myProducts -> { ... }); // ...and so on // Provided to ProductAdapter private ProductClickCallback productClickCallback = new ProductClickCallback() { @Override public void onClick(Product product) { if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { ((ProductListActivity) requireActivity()).show(product); } } }; ... }
Em ProductListFragment
, observe que não há chamada para
setContentView()
para aumentar e conectar o layout. Em um fragmento, onCreateView()
inicializa a
visualização raiz. onCreateView()
usa uma instância de um
LayoutInflater
que pode ser usada para
aumentar a visualização raiz com base em um arquivo de recurso de layout. Este exemplo reutiliza o
layout product_list
existente que foi usado pela atividade porque nada
precisa mudar no layout propriamente dito.
Se você tiver alguma lógica de IU residente nas funções onStart()
, onResume()
,
onPause()
ou onStop()
da atividade que não estejam relacionadas à navegação,
mova-as para funções correspondentes de mesmo nome no fragmento.
Inicializar o fragmento na atividade do host
Depois de mover toda a lógica da IU para o fragmento, apenas a lógica de navegação permanecerá na atividade.
Kotlin
class ProductListActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.product_list_host) } fun show(product: Product) { val intent = Intent(this, ProductActivity::class.java) intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id) startActivity(intent) } }
Java
public class ProductListActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.product_list_host); } public void show(Product product) { Intent intent = new Intent(this, ProductActivity.class); intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId()); startActivity(intent); } }
A última etapa é criar uma instância do fragmento em onCreate()
, logo
após configurar a visualização de conteúdo:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.product_list_host) if (savedInstanceState == null) { val fragment = ProductListFragment() supportFragmentManager .beginTransaction() .add(R.id.main_content, fragment) .commit() } }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.product_list_host); if (savedInstanceState == null) { ProductListFragment fragment = new ProductListFragment(); getSupportFragmentManager() .beginTransaction() .add(R.id.main_content, fragment) .commit(); } }
Como mostrado neste exemplo, FragmentManager
salva e restaura
automaticamente fragmentos nas mudanças de configuração. Portanto, você só precisa adicionar o fragmento
se savedInstanceState
for nulo.
Passar extras de intent para o fragmento
Se a atividade receber Extras
por meio de um intent, você poderá passá-la para o
fragmento diretamente como argumento.
Neste exemplo, o ProductDetailsFragment
recebe os argumentos diretamente
dos extras de intent da atividade:
Kotlin
... if (savedInstanceState == null) { val fragment = ProductDetailsFragment() // Intent extras and Fragment Args are both of type android.os.Bundle. fragment.arguments = intent.extras supportFragmentManager .beginTransaction() .add(R.id.main_content, fragment) .commit() } ...
Java
... if (savedInstanceState == null) { ProductDetailsFragment fragment = new ProductDetailsFragment(); // Intent extras and fragment Args are both of type android.os.Bundle. fragment.setArguments(getIntent().getExtras()); getSupportFragmentManager() .beginTransaction() .add(R.id.main_content, fragment) .commit(); } ...
Neste ponto, você poderá testar a execução do app com a primeira tela atualizada para usar um fragmento. Continue a migração do restante das telas baseadas em atividades e reserve um tempo para testar após cada iteração.
Integrar um componente de navegação
Depois de usar uma arquitetura baseada em fragmento, você estará pronto para começar a integrar a Navigation component.
Primeiro, adicione as dependências de Navigation mais recentes ao projeto, seguindo as instruções nas notas da versão da biblioteca Navigation.
Criar um gráfico de navegação
O componente de navegação representa a configuração de navegação do app em um arquivo de recursos como um gráfico, assim como as visualizações do app são representadas. Isso ajuda a manter a navegação do app organizada fora do codebase e fornece uma maneira de editar visualmente a navegação do app.
Para criar um gráfico de navegação, comece criando uma nova pasta de recursos chamada
navigation
. Para adicionar o gráfico, clique com o botão direito do mouse nesse diretório e escolha
New > Navigation resource file.
A Navigation component usa uma atividade como um
host para navegação
e troca fragmentos individuais nesse host à medida que os usuários navegam
pelo app. Antes de começar a organizar visualmente a navegação do app, você
precisa configurar um NavHost
dentro da atividade que hospedará esse
gráfico. Como estamos usando fragmentos, podemos usar a implementação do
NavHost
padrão do componente de navegação,
NavHostFragment
.
Um NavHostFragment
é configurado por meio de uma FragmentContainerView
colocada dentro de uma atividade de host, como mostrado no exemplo a seguir:
<androidx.fragment.app.FragmentContainerView
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/product_list_graph"
app:defaultNavHost="true"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
O atributo app:NavGraph
aponta para o gráfico de navegação associado a este
host de navegação. A definição dessa propriedade aumenta o gráfico de navegação e define a propriedade do
gráfico em NavHostFragment
. O atributo app:defaultNavHost
garante
que o NavHostFragment
intercepte o botão "Voltar" do sistema.
Se você estiver usando a navegação de nível superior, como DrawerLayout
ou
BottomNavigationView
, esssa FragmentContainerView
substitui o principal elemento de visualização de conteúdo. Consulte
Atualizar componentes de IU com NavigationUI
para ver exemplos.
Para um layout simples, você pode incluir esse elemento FragmentContainerView
como filho do ViewGroup
raiz:
<FrameLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_content"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/product_list_graph"
app:defaultNavHost="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Se você clicar na guia Design na parte inferior, verá um gráfico semelhante
ao mostrado abaixo. No lado superior esquerdo do gráfico, em Destinations, você pode ver uma referência à atividade NavHost
na forma
de layout_name (resource_id)
.
Clique no botão de adição perto do topo para adicionar os fragmentos a este gráfico.
O componente de navegação refere-se a telas individuais como destinos. Os destinos podem ser fragmentos, atividades ou destinos personalizados. Você pode adicionar qualquer tipo de destino ao gráfico, mas os destinos de atividades são considerados destinos terminais, porque quando você navega para um destino de atividade, opera em um host de navegação e um gráfico separados.
O componente de navegação refere-se à maneira como os usuários vão de um destino para outro como ações. As ações também podem descrever animações de transição e comportamentos de transição de tela.
Remover transações de fragmentos
Agora que você está usando a Navigation component, se estiver navegando entre telas baseadas em fragmentos com a mesma atividade, poderá remover
as interações de
FragmentManager
.
Caso o app esteja usando vários fragmentos na mesma atividade ou navegação
de nível superior, como layout de gaveta ou navegação inferior, você provavelmente
está usando FragmentManager
e
FragmentTransactions
para adicionar ou substituir fragmentos na seção de conteúdo principal da IU. Agora, isso pode
ser substituído e simplificado usando o componente de navegação, fornecendo ações
para vincular destinos dentro do gráfico e navegando usando
NavController
.
Veja alguns cenários que você pode encontrar e como abordar a migração para cada um deles.
Atividade única para gerenciar vários fragmentos
Se você tiver uma única atividade que gerencia vários fragmentos, o código de atividade pode ter esta aparência:
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Logic to load the starting destination // when the Activity is first created if (savedInstanceState == null) { val fragment = ProductListFragment() supportFragmentManager.beginTransaction() .add(R.id.fragment_container, fragment, ProductListFragment.TAG) .commit() } } // Logic to navigate the user to another destination. // This may include logic to initialize and set arguments on the destination // fragment or even transition animations between the fragments (not shown here). fun navigateToProductDetail(productId: String) { val fragment = new ProductDetailsFragment() val args = Bundle().apply { putInt(KEY_PRODUCT_ID, productId) } fragment.arguments = args supportFragmentManager.beginTransaction() .addToBackStack(ProductDetailsFragment.TAG) .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG) .commit() } }
Java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Logic to load the starting destination when the activity is first created. if (savedInstanceState == null) { val fragment = ProductListFragment() supportFragmentManager.beginTransaction() .add(R.id.fragment_container, fragment, ProductListFragment.TAG) .commit(); } } // Logic to navigate the user to another destination. // This may include logic to initialize and set arguments on the destination // fragment or even transition animations between the fragments (not shown here). public void navigateToProductDetail(String productId) { Fragment fragment = new ProductDetailsFragment(); Bundle args = new Bundle(); args.putInt(KEY_PRODUCT_ID, productId); fragment.setArguments(args); getSupportFragmentManager().beginTransaction() .addToBackStack(ProductDetailsFragment.TAG) .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG) .commit(); } }
Dentro do destino de origem, você pode estar invocando uma função de navegação em resposta a algum evento, conforme mostrado abaixo:
Kotlin
class ProductListFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // In this example a callback is passed to respond to an item clicked // in a RecyclerView productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) } ... // The callback makes the call to the activity to make the transition. private val productClickCallback = ProductClickCallback { product -> (requireActivity() as MainActivity).navigateToProductDetail(product.id) } }
Java
public class ProductListFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // In this example a callback is passed to respond to an item clicked in a RecyclerView productAdapter = new ProductAdapter(productClickCallback); binding.productsList.setAdapter(productAdapter); } ... // The callback makes the call to the activity to make the transition. private ProductClickCallback productClickCallback = product -> ( ((MainActivity) requireActivity()).navigateToProductDetail(product.getId()) ); }
Isso pode ser substituído por meio da atualização do gráfico de navegação para definir o destino inicial e as ações para vincular os destinos e definir argumentos onde necessário:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List"
tools:layout="@layout/product_list">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_detail" />
</fragment>
<fragment
android:id="@+id/product_detail"
android:name="com.example.android.persistence.ui.ProductDetailFragment"
android:label="Product Detail"
tools:layout="@layout/product_detail">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
Em seguida, você pode atualizar a atividade:
Kotlin
class MainActivity : AppCompatActivity() { // No need to load the start destination, handled automatically by the Navigation component override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
Java
public class MainActivity extends AppCompatActivity { // No need to load the start destination, handled automatically by the Navigation component @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
A atividade não precisa mais de um método navigateToProductDetail()
. Na próxima
seção, atualizamos ProductListFragment
para usar o NavController
para navegar
para a próxima tela de detalhes do produto.
Passar argumentos com segurança
A Navigation component tem um plug-in para Gradle chamado Safe Args que gera objetos simples e classes do builder para acesso seguro a argumentos especificados para destinos e ações.
Depois que o plug-in é aplicado, os argumentos definidos em um destino no
gráfico de navegação fazem com que a estrutura do componente de navegação gere uma
classe Arguments
que fornece argumentos de tipos seguros para o destino.
Definir uma ação faz com que o plug-in gere uma classe de configuração Directions
que pode ser usada para informar ao NavController
como navegar o usuário
até o destino. Quando uma ação aponta para um destino que requer
argumentos, a classe Directions
gerada inclui métodos de construtor que
exigem esses parâmetros.
Dentro do fragmento, use NavController
e a classe Directions
gerada
para fornecer argumentos de tipo seguro ao destino, como mostrado no exemplo
a seguir:
Kotlin
class ProductListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // In this example a callback is passed to respond to an item clicked in a RecyclerView productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) } ... // The callback makes the call to the NavController to make the transition. private val productClickCallback = ProductClickCallback { product -> val directions = ProductListDirections.navigateToProductDetail(product.id) findNavController().navigate(directions) } }
Java
public class ProductListFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // In this example a callback is passed to respond to an item clicked in a RecyclerView productAdapter = new ProductAdapter(productClickCallback); binding.productsList.setAdapter(productAdapter); } ... // The callback makes the call to the activity to make the transition. private ProductClickCallback productClickCallback = product -> { ProductListDirections.ViewProductDetails directions = ProductListDirections.navigateToProductDetail(product.getId()); NavHostFragment.findNavController(this).navigate(directions); }; }
Navegação de nível superior
Se o app usa um DrawerLayout
, você pode ter muita lógica de configuração
na atividade que gerencia abrir e fechar a gaveta e navegar
para outros destinos.
A atividade resultante terá esta aparência:
Kotlin
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) val navView: NavigationView = findViewById(R.id.nav_view) val toggle = ActionBarDrawerToggle( this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) drawerLayout.addDrawerListener(toggle) toggle.syncState() navView.setNavigationItemSelectedListener(this) } override fun onBackPressed() { val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) if (drawerLayout.isDrawerOpen(GravityCompat.START)) { drawerLayout.closeDrawer(GravityCompat.START) } else { super.onBackPressed() } } override fun onNavigationItemSelected(item: MenuItem): Boolean { // Handle navigation view item clicks here. when (item.itemId) { R.id.home -> { val homeFragment = HomeFragment() show(homeFragment) } R.id.gallery -> { val galleryFragment = GalleryFragment() show(galleryFragment) } R.id.slide_show -> { val slideShowFragment = SlideShowFragment() show(slideShowFragment) } R.id.tools -> { val toolsFragment = ToolsFragment() show(toolsFragment) } } val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) drawerLayout.closeDrawer(GravityCompat.START) return true } } private fun show(fragment: Fragment) { val drawerLayout = drawer_layout as DrawerLayout val fragmentManager = supportFragmentManager fragmentManager .beginTransaction() .replace(R.id.main_content, fragment) .commit() drawerLayout.closeDrawer(GravityCompat.START) }
Java
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); DrawerLayout drawer = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); toggle.syncState(); navigationView.setNavigationItemSelectedListener(this); } @Override public void onBackPressed() { DrawerLayout drawer = findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } @Override public boolean onNavigationItemSelected(MenuItem item) { // Handle navigation view item clicks here. int id = item.getItemId(); if (id == R.id.home) { Fragment homeFragment = new HomeFragment(); show(homeFragment); } else if (id == R.id.gallery) { Fragment galleryFragment = new GalleryFragment(); show(galleryFragment); } else if (id == R.id.slide_show) { Fragment slideShowFragment = new SlideShowFragment(); show(slideShowFragment); } else if (id == R.id.tools) { Fragment toolsFragment = new ToolsFragment(); show(toolsFragment); } DrawerLayout drawer = findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; } private void show(Fragment fragment) { DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager .beginTransaction() .replace(R.id.main_content, fragment) .commit(); drawerLayout.closeDrawer(GravityCompat.START); } }
Depois de adicionar um componente de navegação ao projeto e criar
um gráfico de navegação, adicione cada um dos destinos de conteúdo do gráfico, como
Home, Gallery, SlideShow e Tools do exemplo acima. Confirme
se os valores do item de menu id
são os mesmos no destino associado id
,
conforme mostrado abaixo:
<!-- activity_main_drawer.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/home"
android:icon="@drawable/ic_menu_camera"
android:title="@string/menu_home" />
<item
android:id="@+id/gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="@string/menu_gallery" />
<item
android:id="@+id/slide_show"
android:icon="@drawable/ic_menu_slideshow"
android:title="@string/menu_slideshow" />
<item
android:id="@+id/tools"
android:icon="@drawable/ic_menu_manage"
android:title="@string/menu_tools" />
</group>
</menu>
<!-- activity_main_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_graph"
app:startDestination="@id/home">
<fragment
android:id="@+id/home"
android:name="com.example.HomeFragment"
android:label="Home"
tools:layout="@layout/home" />
<fragment
android:id="@+id/gallery"
android:name="com.example.GalleryFragment"
android:label="Gallery"
tools:layout="@layout/gallery" />
<fragment
android:id="@+id/slide_show"
android:name="com.example.SlideShowFragment"
android:label="Slide Show"
tools:layout="@layout/slide_show" />
<fragment
android:id="@+id/tools"
android:name="com.example.ToolsFragment"
android:label="Tools"
tools:layout="@layout/tools" />
</navigation>
Se os valores id
do menu e do gráfico forem iguais, você poderá conectar
NavController
para esta atividade e gerenciar a navegação automaticamente com base no
item do menu. O NavController
também gerencia adequadamente como abrir e fechar o
DrawerLayout
, além do comportamento dos botões "Para cima" e "Voltar".
O MainActivity
pode ser atualizado para conectar NavController
a
Toolbar
e NavigationView
.
Veja o seguinte snippet para um exemplo:
Kotlin
class MainActivity : AppCompatActivity() { val drawerLayout by lazy { findViewById<DrawerLayout>(R.id.drawer_layout) } val navController by lazy { (supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment).navController } val navigationView by lazy { findViewById<NavigationView>(R.id.nav_view) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById<Toolbar>(R.id.toolbar) setSupportActionBar(toolbar) // Show and Manage the Drawer and Back Icon setupActionBarWithNavController(navController, drawerLayout) // Handle Navigation item clicks // This works with no further action on your part if the menu and destination id’s match. navigationView.setupWithNavController(navController) } override fun onSupportNavigateUp(): Boolean { // Allows NavigationUI to support proper up navigation or the drawer layout // drawer menu, depending on the situation return navController.navigateUp(drawerLayout) } }
Java
public class MainActivity extends AppCompatActivity { private DrawerLayout drawerLayout; private NavController navController; private NavigationView navigationView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); drawerLayout = findViewById(R.id.drawer_layout); NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_content); navController = navHostFragment.getNavController(); navigationView = findViewById(R.id.nav_view); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Show and Manage the Drawer and Back Icon NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout); // Handle Navigation item clicks // This works with no further action on your part if the menu and destination id’s match. NavigationUI.setupWithNavController(navigationView, navController); } @Override public boolean onSupportNavigateUp() { // Allows NavigationUI to support proper up navigation or the drawer layout // drawer menu, depending on the situation. return NavigationUI.navigateUp(navController, drawerLayout); } }
Você pode usar essa mesma técnica com a navegação baseada em BottomNavigationView e em menus. Consulte Atualizar componentes de IU com NavigationUI para ver mais exemplos.
Adicionar destinos de atividades
Quando cada tela do app estiver programada para usar o componente de navegação e
você não estiver mais usando FragmentTransactions
para fazer a transição entre
destinos baseados em fragmento, a próxima etapa será eliminar chamadas
startActivity
.
Primeiro, identifique locais no app em que você tem dois gráficos de navegação separados
e está usando startActivity
para fazer a transição entre eles.
Este exemplo contém dois gráficos (A e B) e uma chamada startActivity()
para fazer a
transição de A para B.
Kotlin
fun navigateToProductDetails(productId: String) { val intent = Intent(this, ProductDetailsActivity::class.java) intent.putExtra(KEY_PRODUCT_ID, productId) startActivity(intent) }
Java
private void navigateToProductDetails(String productId) { Intent intent = new Intent(this, ProductDetailsActivity.class); intent.putExtra(KEY_PRODUCT_ID, productId); startActivity(intent);
A seguir, substitua-os por um destino da atividade no gráfico A que representa a navegação para a atividade do host do gráfico B. Se você tiver argumentos para passar para o destino inicial do gráfico B, poderá designá-los na definição de destino da atividade.
No exemplo a seguir, o gráfico A define um destino de atividade que leva o argumento
product_id
com uma ação. O gráfico B não contém mudanças.
A representação XML dos gráficos A e B pode ter esta aparência:
<!-- Graph A -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List"
tools:layout="@layout/product_list_fragment">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_details_activity" />
</fragment>
<activity
android:id="@+id/product_details_activity"
android:name="com.example.android.persistence.ui.ProductDetailsActivity"
android:label="Product Details"
tools:layout="@layout/product_details_host">
<argument
android:name="product_id"
app:argType="integer" />
</activity>
</navigation>
<!-- Graph B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/product_details">
<fragment
android:id="@+id/product_details"
android:name="com.example.android.persistence.ui.ProductDetailsFragment"
android:label="Product Details"
tools:layout="@layout/product_details_fragment">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
Você pode navegar para a atividade de host do gráfico B usando os mesmos mecanismos usados para navegar para destinos de fragmento:
Kotlin
fun navigateToProductDetails(productId: String) { val directions = ProductListDirections.navigateToProductDetail(productId) findNavController().navigate(directions) }
Java
private void navigateToProductDetails(String productId) { ProductListDirections.NavigateToProductDetail directions = ProductListDirections.navigateToProductDetail(productId); Navigation.findNavController(getView()).navigate(directions);
Passar argumentos de destino da atividade para um fragmento de destino inicial
Se a atividade de destino receber extras, como no exemplo anterior, você
poderá passá-los diretamente ao destino como argumentos, mas precisará definir
manualmente o gráfico de navegação do host dentro do método
onCreate()
da atividade do host para transmitir os extras de intent como argumentos para o
fragmento, como mostrado abaixo:
Kotlin
class ProductDetailsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.product_details_host) val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment val navController = navHostFramgent.navController navController .setGraph(R.navigation.product_detail_graph, intent.extras) } }
Java
public class ProductDetailsActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.product_details_host); NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_content); NavController navController = navHostFragment.getNavController(); navController .setGraph(R.navigation.product_detail_graph, getIntent().getExtras()); } }
Os dados podem ser extraídos dos argumentos de fragmento Bundle
usando a
classe de argumentos gerada, como mostrado no exemplo a seguir:
Kotlin
class ProductDetailsFragment : Fragment() { val args by navArgs<ProductDetailsArgs>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val productId = args.productId ... } ...
Java
public class ProductDetailsFragment extends Fragment { ProductDetailsArgs args; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); args = ProductDetailsArgs.fromBundle(requireArguments()); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { int productId = args.getProductId(); ... } ...
Combinar atividades
Você pode combinar gráficos de navegação nos casos em que várias atividades compartilham
o mesmo layout, como um FrameLayout
simples contendo um único fragmento. Na
maioria dos casos, basta combinar todos os elementos de cada
gráfico de navegação e atualizar quaisquer elementos de destino da atividade para destinos de
fragmento.
O exemplo a seguir combina os gráficos A e B da seção anterior:
Antes da combinação:
<!-- Graph A -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List Fragment"
tools:layout="@layout/product_list">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_details_activity" />
</fragment>
<activity
android:id="@+id/product_details_activity"
android:name="com.example.android.persistence.ui.ProductDetailsActivity"
android:label="Product Details Host"
tools:layout="@layout/product_details_host">
<argument android:name="product_id"
app:argType="integer" />
</activity>
</navigation>
<!-- Graph B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/product_detail_graph"
app:startDestination="@id/product_details">
<fragment
android:id="@+id/product_details"
android:name="com.example.android.persistence.ui.ProductDetailsFragment"
android:label="Product Details"
tools:layout="@layout/product_details">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
Após a combinação:
<!-- Combined Graph A and B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List Fragment"
tools:layout="@layout/product_list">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_details" />
</fragment>
<fragment
android:id="@+id/product_details"
android:name="com.example.android.persistence.ui.ProductDetailsFragment"
android:label="Product Details"
tools:layout="@layout/product_details">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
Manter os nomes das ações iguais ao mesclar pode tornar esse processo contínuo,
não exigindo alterações na base do código existente. Por exemplo,
navigateToProductDetail
permanece igual aqui. A única diferença é que
essa ação agora representa a navegação para um fragmento de destino dentro do mesmo
NavHost
em vez de um destino de atividade:
Kotlin
fun navigateToProductDetails(productId: String) { val directions = ProductListDirections.navigateToProductDetail(productId) findNavController().navigate(directions) }
Java
private void navigateToProductDetails(String productId) { ProductListDirections.NavigateToProductDetail directions = ProductListDirections.navigateToProductDetail(productId); Navigation.findNavController(getView()).navigate(directions);
Recursos adicionais
Para mais informações relacionadas à navegação, consulte os seguintes tópicos:
- Atualizar componentes de IU com NavigationUI: saiba como gerenciar a navegação com a barra superior do app, a gaveta de navegação e a navegação na parte inferior
- Navegação de teste: saiba como testar fluxos de trabalho de navegação para o app