Android Jetpack — Navegação

Photo by Joseph Barrientos on Unsplash

Então pessoal, de volta aos posts sobre o Jetpack, e hoje vou falar sobre Navegação, uma das libs mais problemáticas e interessantes do Jetpack. Problemática porque demorou um pouco para eu conseguir fazer rodar (e se você quiser usar tem que estar no Android Studio Preview, ou Canary, que é a versão com features ainda em teste do AS), mesmo usando o AS Canary mais recente. É claro que tenho que dar um certo desconto, a lib ainda tá em alfa e tal... Ela é interessante porque novamente o Google vem nos propor uma arquitetura de desenvolvimento de apps, dessa vez uma Activity e múltiplos Fragments.

Quem já desenvolve para Android (nem precisa ser por muito tempo) já sabe que Fragments são uma criatura estranha do ecossistema. São um componente de UI, mas não são um contexto (como as Activities), entretanto eles tem um lifecycle próprio que juntamente com o da Activity vira uma bagunça entranhada:

E tudo isso é muito lindo e importantede saber, mas cria um overhead no desenvolvimento que a lib de Navegação vem tentar simplificar.

A primeira vez que eu vi a biblioteca de Navegação eu pensei: “Olha, um Storyboard pra Android”. Enganado eu estava j que a proposta é completamente outra. Mas vamos começar do começo:

Navigation é um Arch Component (tal qual ViewModel, LifeCycle e etc) que ajuda a simplificar a navegação entre “destinos” na sua aplicação. Isso que dizer que utilizando esse componente, poderemos ligar telas de forma que a navegação entre elas vai ser gerenciada pelo componente.

O app de exemplo que vamos utilizar é um que fiz recentemente para testar outro Arch Component (Room) e achei que seria ideal para fazer testes na lib de Navegação também. O código completo pode ser encontrado aqui.

Como vocês podem ver, uma aplicação muito comum. Utilizamos uma BottomNavigationView para administrar as várias tabs da nossa app e para cada tab temos um Fragment diferente.

Anteriormente, a administração da troca de tabs era responsabilidade do desenvolvedor. Tinhamos que criar um mecanismo que reagisse aos clicks das tabs e trocasse os Fragments de acordo, ou seja, ficar rodando um código parecido com esse:

E geralmente, para evitar tendo que adminstrar a BackStack quando tinha a necessidade de abrir uma outra tela a partir de um desses Fragments a gente simplesmente abria uma nova Activity e dava bye bye para o BottomNavigationView. Inclusive o Facebook ainda faz isso…

Usando a lib

Primeiro, como toda e qualquer biblioteca, temos que importá-la no nosso arquivo build.gradle:

Depois você deve ter uma Activity pronta para ser a NavHost, ou seja, ela que vai abrigar o fluxo que iremos definir no grafo de navegação mais na frente. Para definir uma Activity como NavHost temos que adicionar ao xml dela essa indicação:

<android.support.constraint.ConstraintLayout 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/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>

É bem simples, temos o BottomNavigationView e uma tag fragment onde definimos o name como NavHostFragment e o navGraph apontamos para o grafo de navegação que vamos criar no próximo passo.

Para criar o grafo de navegação basta clicar com o botão direito em cima da pasta de resources (res) e selecionar adicionar novo recurso xml. No pop up que vai aparecer preenchemos o nome do recurso nav_graph (ou outro nome que você prefereir) e selecionamos Navigation no tipo de recurso:

Isso irá criar um novo subdiretório de recursos e irá adicionar o novo xml que podemos abrir no editor de navegação. Para abrir o editor basta clicar para abrir o arquivo e selecionar Design na tab inferior, do mesmo modo quando vamos editar um xml de layout.

O editor tem 3 áreas, Destinations, a área de edição e a de atributos:

A sua Activity NavHost deve aparecer na área de destinations. Você pode adicionar Fragments existentes no seu projeto como destinations ou criar novos. As ligações entre Fragments são chamadas Actions . Aqui no nosso exemplo eu coloquei os 5 Fragments que representam as tabs, bem como coloquei Fragments adicionais representando outras telas que podem estar ligadas a cada tab.

Os comportamentos dos Fragments são escritos em kotlin normalmente, mas a instanciação deles fica por conta da lib.

Depois de definidos tudo isso, basta apenas fazermos a ligação entre as tabs do BottomNavigationView e os seus respectivos Fragments. Sempre que definimos um BottomNavigationView, definimos também um arquivo xml de menu que definirá as tabs dele, o do nosso exemplo é assim:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home"
android:title="@string/title_home" />
<item
android:id="@+id/navigation_people"
android:icon="@drawable/ic_group"
android:title="@string/title_people" />
<item
android:id="@+id/navigation_organization"
android:icon="@drawable/ic_organization"
android:title="@string/title_organization" />
<item
android:id="@+id/navigation_business"
android:icon="@drawable/ic_business"
android:title="@string/title_business" />
<item
android:id="@+id/navigation_tasks"
android:icon="@drawable/ic_dashboard"
android:title="@string/title_tasks" />
</menu>

Para fazer a ligação entre os dois basta que os ids definidos para cada Fragment no editor de grafo sejam iguais aos definidos acima:

No exemplo o id para o Fragment destination home é navigation_home, ou seja o mesmo que o definido acima no arquivo de menu do BottomNavigationView. Finalmente, utilizamos uma linha de kotlin para finalizar com a ligação:

E simples assim o BottomNavigationView vai funcionar sem a necessidade de ficarmos gerenciando as transactions (embora possamos adicionar animações via xml nas transições através do editor de grafos).

Outra parte importante do editor de grafos são as actions, ou as trocas de Fragments. Na tela abaixo por exemplo, queremos associar o clique no FAB à troca de Fragment de listagem e o de adicionar item:

Tela bugada :) Mas dá pra entender a ideia

Quando definimos uma Action de uma “destination” a outra, podemos adicionar várias funcionalidades a essa action tais como animações, argumentos que serão passados, comportamento ao receber o click to Back Button e opções de como será iniciado esse Fragment:

Um dos atributos mais importantes da action é o ID e geralmente o próprio AS o preenche automaticamente. Para utilizar o FAB para invocar essa transição (action) existem dois métodos:

Podemos invocar a action diretamente utilizando o método navigate() que tem como parâmetro o id da action ou o id do Fragment de destino. E no caso de botões podemos utilizar o método createNavigateOnClickListener() que também recebe o ID da action ou o ID do Fragment de destino e já retorna um ClickListener.

Bem é isso pessoal, a lib ainda esté bem no começo, mas dá para se divertir bastante com ela.

If you like it and you know it clap you hands, clap clap clap :)