Como fornecer uma navegação de retorno adequada

A navegação de retorno é a forma como os usuários se movem para trás no histórico de telas que eles visitaram anteriormente. Todos os dispositivos Android fornecem um botão Voltar para esse tipo de navegação, portanto, não adicione um botão "Voltar" à IU com o app.

Quase sempre, o sistema mantém uma pilha de retorno de atividades enquanto o usuário navega no app. Isso permite a navegação correta do sistema para trás quando o usuário pressiona o botão Voltar. No entanto, em alguns casos é necessário especificar manualmente o comportamento de Voltar no app para proporcionar a melhor experiência ao usuário.

Veja também Como projetar uma navegação de retorno e de topo Tarefas e pilha de retorno e Design para Android: navegação.

Os padrões de navegação que exigem a especificação manual do comportamento de Voltar incluem:

A forma de implementar a navegação de retorno apropriada nessas situações está descrita nas seções a seguir.

Sintetizar uma nova pilha de retorno para links diretos

Normalmente, o sistema constrói a pilha de retorno de maneira incremental à medida que o usuário navega de uma atividade para outra. No entanto, quando o usuário insere seu app com um link direto que inicia a atividade na própria tarefa, é necessário que você sintetize uma nova pilha de retorno, porque a atividade está sendo executada em uma nova tarefa sem nenhuma pilha desse tipo.

Por exemplo, quando uma notificação leva o usuário a uma atividade para baixo na hierarquia do app, você precisa adicionar atividades à pilha de retorno da tarefa, de modo que pressionar o botão Voltar leve a navegação para cima na hierarquia do app em vez de sair dele. Esse padrão é descrito mais adiante no guia de design de navegação.

Especificar atividades pai no manifest

A partir do Android 4.1 (nível de API 16), é possível declarar o pai lógico de cada atividade especificando o atributo android:parentActivityName no elemento <activity>. Isso permite que o sistema facilite os padrões de navegação ao determinar o caminho lógico de navegação Voltar ou Topo com essas informações.

Se o app for compatível com Android 4.0 e anterior, inclua a Support Library com o app e adicione um elemento <meta-data> dentro de <activity>. Em seguida, especifique a atividade pai como o valor para android.support.PARENT_ACTIVITY, correspondendo ao atributo android:parentActivityName.

Exemplo:

<application ... >
    ...
    <!-- The main/home activity (it has no parent activity) -->
    <activity
        android:name="com.example.myfirstapp.MainActivity" ...>
        ...
    </activity>
    <!-- A child of the main activity -->
    <activity
        android:name="com.example.myfirstapp.DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName="com.example.myfirstapp.MainActivity" >
        <!-- The meta-data element is needed for versions lower than 4.1 -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>
</application>

Com a atividade pai declarada dessa maneira, você pode usar as APIs NavUtils para sintetizar uma nova pilha de retorno, identificando qual atividade é o pai apropriado de cada atividade.

Criar pilha de retorno ao iniciar a atividade

A adição de atividades à pilha de retorno começa no evento que leva o usuário ao seu app. Ou seja, em vez de chamar startActivity(), use as APIs TaskStackBuilder para definir cada atividade que precisa ser colocada em uma nova pilha de retorno. Em seguida, inicie a atividade de destino chamando startActivities() ou crie o PendingIntent apropriado chamando getPendingIntent().

Por exemplo, quando uma notificação leva o usuário a uma atividade para baixo na hierarquia do app, você pode usar esse código para criar um PendingIntent que inicie uma atividade e insira uma nova pilha de retorno na tarefa de destino:

Kotlin

val detailsIntent = Intent(this, DetailsActivity::class.java)

val pendingIntent: PendingIntent? = TaskStackBuilder.create(this)
        // add all of DetailsActivity's parents to the stack,
        // followed by DetailsActivity itself
        .addNextIntentWithParentStack(detailsIntent)
        .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)

val builder = NotificationCompat.Builder(this)
        .setContentIntent(pendingIntent)
...

Java

// Intent for the activity to open when user selects the notification
Intent detailsIntent = new Intent(this, DetailsActivity.class);

// Use TaskStackBuilder to build the back stack and get the PendingIntent
PendingIntent pendingIntent =
        TaskStackBuilder.create(this)
                        // add all of DetailsActivity's parents to the stack,
                        // followed by DetailsActivity itself
                        .addNextIntentWithParentStack(detailsIntent)
                        .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(pendingIntent);
...

O PendingIntent resultante especifica não apenas a atividade a ser iniciada (conforme definida por detailsIntent), mas também a pilha de retorno que precisa ser inserida na tarefa (todos os pais de DetailsActivity definidos por detailsIntent). Portanto, quando DetailsActivity for iniciado, pressionar a opção Voltar levará a navegação para trás em cada uma das atividades pai da classe DetailsActivity.

Observação: para que o método addNextIntentWithParentStack() funcione, você precisa declarar o pai lógico de cada atividade no seu arquivo manifest, usando o atributo android:parentActivityName (e o elemento <meta-data> correspondente) conforme descrito acima.

Implementar a navegação de retorno para fragmentos

Ao usar fragmentos no app, os objetos FragmentTransaction individuais podem representar alterações de contexto que precisam ser adicionadas à pilha de retorno. Por exemplo, se você estiver implementando um fluxo mestre/detalhe em um aparelho trocando fragmentos, certifique-se de que pressionar o botão Voltar em uma tela de detalhes leve o usuário de volta à tela principal. Para fazer isso, chame addToBackStack() antes de confirmar a transação:

Kotlin

// Works with either the framework FragmentManager or the
// support package FragmentManager (supportFragmentManager).
supportFragmentManager.beginTransaction()
        .add(detailFragment, "detail")
        // Add this transaction to the back stack
        .addToBackStack(null)
        .commit()

Java

// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
                           .add(detailFragment, "detail")
                           // Add this transaction to the back stack
                           .addToBackStack(null)
                           .commit();

Quando há objetos FragmentTransaction na pilha de retorno e o usuário pressiona o botão Voltar, o FragmentManager insere a transação mais recente na pilha de retorno e executa a ação reversa (como remover um fragmento se a transação o adicionou).

Observação: não adicione transações à pilha de retorno quando a transação for para navegação horizontal (como ao alternar guias) ou ao modificar a aparência do conteúdo (como no ajuste de filtros). Para ver mais informações sobre quando a navegação de retorno é apropriada, consulte o guia de design Navegação.

Se o app atualizar outros elementos da interface do usuário para refletir o estado atual dos fragmentos, como a barra de ações, lembre-se de atualizar a IU ao confirmar a transação. Além de atualizar sua interface do usuário quando confirmar a transação, é necessário atualizá-la também após alterações na pilha de retorno. Para ouvir quando um FragmentTransaction for revertido, basta configurar um FragmentManager.OnBackStackChangedListener:

Kotlin

supportFragmentManager.addOnBackStackChangedListener {
    // Update your UI here.
}

Java

getSupportFragmentManager().addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            public void onBackStackChanged() {
                // Update your UI here.
            }
        });

Implementar a navegação de retorno para WebViews

Se uma parte do app estiver contida em um WebView, pode ser apropriado que a opção Voltar percorra o histórico do navegador. Para fazer isso, modifique onBackPressed() e use WebView como proxy se ele tiver histórico:

Kotlin

override fun onBackPressed() {
    if (mWebView.canGoBack()) {
        mWebView.goBack()
    } else {
        // Otherwise defer to system default behavior.
        super.onBackPressed()
    }
}

Java

@Override
public void onBackPressed() {
    if (mWebView.canGoBack()) {
        mWebView.goBack();
        return;
    }

    // Otherwise defer to system default behavior.
    super.onBackPressed();
}

Tenha cuidado ao usar esse mecanismo com páginas da Web altamente dinâmicas que podem gerar um histórico grande. Páginas que geram um histórico extenso, como aquelas que fazem alterações frequentes no hash do documento, podem tornar tediosa a saída dos usuários da atividade.

Para ver mais informações sobre como usar o WebView, leia Como criar apps da Web no WebView.