Como usar a AppBar

A barra de apps superior proporciona um lugar consistente na parte superior da janela do app para exibir informações e ações da tela atual.

Exemplo de barra de apps superior
Figura 1. Exemplo de barra de apps superior.

Ao usar fragmentos, a barra de apps pode ser implementada como uma ActionBar que pertence à atividade do host ou a uma barra de ferramentas no layout do fragmento. A propriedade da barra de apps varia de acordo com as necessidades do app.

Se todas as suas telas usam a mesma barra de apps que está sempre na parte superior e se estende pela largura da tela, você precisa usar uma barra de ações fornecida por tema hospedada pela atividade. O uso de barras de apps em temas ajuda a manter uma aparência consistente e oferece um local para hospedar menus de opção e um botão para cima.

Use uma barra de ferramentas hospedada pelo fragmento se você quiser ter mais controle sobre o tamanho, a posição e a animação da barra de apps em várias telas. Por exemplo, talvez você precise de uma barra de apps recolhível ou de uma que se estenda apenas até a metade da tela e seja centralizada verticalmente.

Entender as diferentes abordagens e empregar uma opção correta economiza tempo e ajuda a garantir que o app funcione corretamente. Diferentes situações exigem diferentes abordagens para aspectos como inflação de menus e resposta à interação do usuário.

Os exemplos neste tópico se referem a um ExampleFragment que contém um perfil editável. O fragmento infla o seguinte menu definido por XML na barra de apps:

<!-- sample_menu.xml -->
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

O menu contém duas opções: uma para navegar até uma tela de perfil e outra para salvar as mudanças feitas no perfil.

Barra de apps da atividade

A barra de apps geralmente é de propriedade da atividade do host. Quando a barra de apps pertence a uma atividade, os fragmentos podem interagir com ela substituindo os métodos de framework que são chamados durante a criação de fragmentos.

Registrar com atividades

Informe ao sistema que o fragmento da barra de apps está participando do preenchimento do menu de opções. Para fazer isso, chame setHasOptionsMenu(true) no método onCreate(Bundle) do fragmento, conforme mostrado no exemplo a seguir:

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

public class ExampleFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

setHasOptionsMenu(true) informa ao sistema que seu fragmento quer receber callbacks relacionados ao menu. Quando ocorre um evento relacionado a um menu (criação, cliques e assim por diante), o método de gerenciamento de eventos é chamado na atividade antes de ser chamado no fragmento. Observe que a lógica do aplicativo não pode depender dessa ordem. Se vários fragmentos estiverem hospedados pela mesma atividade, cada um deles poderá fornecer opções de menu. Nesse caso, a ordem do callback depende da ordem em que os fragmentos foram adicionados.

Como inflar um menu

Para mesclar seu menu ao menu de opções da barra de apps, substitua onCreateOptionsMenu() no seu fragmento. Esse método recebe o menu da barra de apps atual e um MenuInflater como parâmetros. Use o inflador de menu para criar uma instância do menu do fragmento e mescle-a no menu atual, como mostrado no exemplo a seguir:

Kotlin

class ExampleFragment : Fragment() {
    ...

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

A Figura 2 mostra o menu atualizado.

O menu de opções agora contém seu fragmento do menu
Figura 2. O menu de opções agora contém seu fragmento do menu.

Processamento de eventos de clique

Todos os fragmentos e atividades que participam no menu de opções podem responder aos toques. O onOptionsItemSelected() do fragmento recebe o item de menu selecionado como um parâmetro e retorna um booleano para indicar se o toque foi consumido ou não. Quando uma atividade ou um fragmento retorna true de onOptionsItemSelected(), nenhum outro fragmento participante recebe o callback.

Na sua implementação de onOptionsItemSelected(), use uma instrução switch no itemId do item do menu. Se o item selecionado for seu, processe o toque adequadamente e retorne true para indicar que o evento de clique foi processado. Se o item selecionado não for seu, chame a implementação de super. Por padrão, a implementação de super retorna o valor false para permitir que o processamento do menu continue.

Kotlin

class ExampleFragment : Fragment() {
    ...

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // navigate to settings screen
                true
            }
            R.id.action_done -> {
                // save profile changes
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // navigate to settings screen
                return true;
            }
            case R.id.action_done: {
                // save profile changes
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

Modificar o menu de forma dinâmica

A lógica para ocultar ou mostrar um botão ou mudar o ícone precisa ser colocada em onPrepareOptionsMenu(). Esse método é chamado logo antes de cada instância exibida no menu.

Continuando com o exemplo anterior, o botão Salvar ficará invisível até que o usuário inicie a edição e desaparecerá depois que o usuário salvá-la. A adição dessa lógica a onPrepareOptionsMenu() garante que o menu sempre seja apresentado corretamente:

Kotlin

class ExampleFragment : Fragment() {
    ...

    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

Quando você precisar atualizar o menu, como quando um usuário pressionar o botão Editar para editar as informações do perfil, chame invalidateOptionsMenu() na atividade do host para solicitar que o sistema chame onCreateOptionsMenu(). Após a invalidação, é possível fazer as atualizações em onCreateOptionsMenu(). Depois que o menu é inflado, o sistema chama onPrepareOptionsMenu() e atualiza o menu para refletir o estado atual do fragmento.

Kotlin

class ExampleFragment : Fragment() {
    ...

    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

Barra de apps do fragmento

Se a maioria das telas do seu app não precisar de uma barra de apps ou se uma tela precisar de uma barra muito diferente, adicione uma Toolbar ao layout do fragmento. Embora você possa adicionar uma Toolbar em qualquer lugar dentro da hierarquia de visualização do fragmento, ela geralmente precisa ser mantida na parte superior da tela. Para usar a Toolbar no seu fragmento, forneça um ID e consiga uma referência a ele no fragmento, da mesma forma que você faria com qualquer outra visualização.

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

Ao usar uma barra de apps de um fragmento, é altamente recomendado o uso direto das APIs Toolbar. Não use setSupportActionBar() e as APIs de menu do Fragment, que são adequadas apenas para barras de apps de atividades.

Como inflar um menu

O método de conveniência inflateMenu(int) da Toolbar usa o ID de um recurso de menu como parâmetro. Para inflar um recurso de menu XML na barra de ferramentas, transmita o resId para esse método, conforme mostrado no exemplo a seguir:

Kotlin

class ExampleFragment : Fragment() {
    ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...

        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...

        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

Para inflar outro recurso de menu XML, chame o método novamente com o resId do novo menu. Os novos itens são adicionados ao menu, e os itens existentes não são modificados nem removidos.

Se você quiser substituir o conjunto de menus existente, limpe o menu antes de chamar inflateMenu(int) com o ID do novo menu.

Kotlin

class ExampleFragment : Fragment() {
    ...

    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {

    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

Processamento de eventos de clique

Você pode transmitir um OnMenuItemClickListener diretamente para a barra de ferramentas usando o método setOnMenuItemClickListener(). Esse listener é invocado sempre que um usuário seleciona um item de menu usando os botões de ação apresentados no final da barra de ferramentas ou no menu flutuante associado. O MenuItem selecionado é transmitido para o método onMenuItemClick() do listener e pode ser usado para consumir a ação, conforme mostrado no exemplo a seguir:

Kotlin

class ExampleFragment : Fragment() {
    ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...

        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen
                    true
                }
                R.id.action_done -> {
                    // Save profile changes
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...

        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen
                    return true;
                case R.id.action_done:
                    // Save profile changes
                    return true;
                default:
                    return false;
            }
        });
    }
}

Modificar o menu de forma dinâmica

Quando seu fragmento é proprietário da barra de apps, você pode modificar a Toolbar no ambiente de execução da mesma forma que faria com qualquer outra visualização.

Continuando com o exemplo anterior, a opção de menu Salvar ficará invisível até o usuário iniciar a edição, desaparecendo novamente depois que for pressionada:

Kotlin

class ExampleFragment : Fragment() {
    ...

    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

Se estiver presente, o botão de navegação aparecerá no início da barra de ferramentas. Quando você define um ícone de navegação na barra de ferramentas, ela fica visível. Você também pode definir um onClickListener() específico de navegação, que será chamado sempre que o usuário clicar no botão de navegação, conforme mostrado no exemplo a seguir:

Kotlin

class ExampleFragment : Fragment() {
    ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...

        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...

        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere
            }
        });
    }
}