Usando XML de uma ActionBar para criar um menu Navigation Drawer

navigation drawer com xml

Nível: Intermediário

Versão 1.0

11.09.2014


Este artigo tem por objetivo apresentar uma forma mais rápida e fácil de se trabalhar com o Navigation Drawer do Android. Irei mostrar como esse menu, utilizado para navegação, pode ser construido utilizando apenas um arquivo XML idêntico ao da ActionBar.

A principal motivação de escrever esse artigo é de mostrar os problemas que passei usando o Nav Drawer e mostrar uma solução usando o que estamos acostumados no dia a dia com Android. O artigo foca apenas na criação da lista contendo os itens do menu e no tratamento de clique do item.

Não será explicado como implementar todo o Navigation Drawer, logo irei assumir que você já trabalhou com ele antes.

Navigation Drawer: Como normalmente é feito

O Navigation Drawer é um componente de interface do Android empregado como um opção para navegação.

Basicamente, é um painel contendo uma lista de ações que surge no canto esquerdo ou direito da tela. Esse tipo de menu é empregado quando a aplicação inclue muitos tipos de telas (ações) e assim pode facilitar a mudança de uma tela para outra estando em qualquer uma delas.

Sendo um alternativa ao menu da ActionBar, a API do Nav Drawer, da forma como é disponibilizada atualmente, não acompanhou o estilo de programação que temos quando definimos um menu na ActionBar.

Basicamente, um menu da ActionBar é criado inflando um arquivo XML no método onCreateOptionsMenu e tratando o clique do menu no método onOptionsItemSelected através do id do item do menu.

No Nav Drawer, ao contrário, não existe XML para definir o menu e o código de tratamento é feito usando a ordem de posicionamento dos itens no menu.

Como sabemos o menu consiste numa lista de itens criados a partir de um ListView. Basicamente a tarefa envolve criar um Adapter com a lista de itens do menu juntamente com um layout definindo um ícone e um texto. Além disso é preciso tratar o evento de clique no menu.

Este commit no meu github traz uma implementação normalmente utilizada. O adapter (do tipo ArrayAdapter) é criado no método onCreateView do fragmento que exibe o menu (linha 88 da classe NavigationDrawerFragment). O menu é criado com três itens, onde cada item exibe apenas um texto (layout simple_list_item_1 do próprio Android).

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        
        listView = (ListView) inflater.inflate(
                R.layout.fragment_navigation_drawer, container, false);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                selectItem(position);
            }
        });

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                getActionBar().getThemedContext(),
                android.R.layout.simple_list_item_1,
                android.R.id.text1,
                new String[]{
                        getString(R.string.title_section1),
                        getString(R.string.title_section2),
                        getString(R.string.title_section3),
                });

        listView.setAdapter(adapter);

        return listView;
    }

Como toda lista, o tratamento de clique do menu inicia-se registrando o listener do clique do menu usando o método setOnItemClickListener do ListView (linha 92). O listener dispara uma chamada ao método selectItem (linha 112), que por sua vez chama o método onNavigationDrawerItemSelected da atividade MenuActivity.

O método onNavigationDrawerItemSelected da atividade MenuActivity (linha 41 de MenuActivity) é responsável por fazer o tratamento do clique em si. Ele exibe cada tela (fragmento) a partir do clique. Observe que isto é feito relacionando cada posição da lista com sua respectiva tela.

Problemas existentes

Essa abordagem atual possui duas deficiências que impactam na produtividade e na manutenção do código:

  • Usar o posicionamento da lista para tratamento de clique do menu

Usando o posicionamento da lista temos sempre que saber qual a tela corresponde a posição na lista. Quando temos muitos itens de menu, isso torna-se passível a erros. pois teremos muitas associações para lembrar mentalmente.

Além disso, não favorece quando é preciso inserir uma nova ação no menu. Por exemplo, caso um novo item de menu seja adicionado a ordem do menu seria modificada e consequentemente a associação posição-tela teria que ser refeita novamente.

O código pode ser melhorado utilizando uma constante para identificar cada posição da lista, entretanto o segundo problema ainda persiste, pois quando a ordem do menu é alterada teremos que refazer o código de tratamento novamente.

  • Complexidade para modificar o menu

Quando é preciso modificar o menu, por exemplo, adicionando um novo item ou modificando a sua ordem, é preciso ir até o adapter na classe do fragmento e adicionar o código necessário.

Esse tipo de tarefa, apesar de ser simples, quando já feito anteriormente, adiciona mais complexidade ao projeto, irá despreender tempo e será mais uma possibilidade de introduzir erros. Vejamos como melhorar isso.

Abordagem utilizando XML

A ideia que vou mostrar aqui é como construir qualquer menu Navigation Drawer utilizando a mesma estrutura de programação empregada nos menus da ActionBar. Isto é, usando um XML para definir o menu e o tratamento do clique do menu usando um id do item do menu.

Definimos os três itens do nosso Nav Drawer usando o seguinte XML:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MenuActivity">

    <item
        android:id="@+id/menu_home"
        android:icon="@drawable/ic_home"
        android:title="@string/menu_home_title" />

    <item
        android:id="@+id/menu_find_people"
        android:icon="@drawable/ic_search"
        android:title="@string/menu_find_people_title"/>

    <item
        android:id="@+id/menu_photos"
        android:icon="@drawable/ic_photos"
        android:title="@string/menu_photos_title"/>

</menu>

Observe que o XML é o mesmo daquele quando definimos um menu para a ActionBar. Ele possui uma tag menu e para cada item do menu a tag item. Cada item é definido com seu respectivo id (menu_home, menu_find_people e menu_photos) juntamente com seu icone e título.

O próximo passo é modificar o Adapter do ListView. A única alteração a ser feita é tornar o adapter do tipo MenuItem, que é o tipo do item de menu. Dessa forma, o adapter passará a armazenar a lista de itens do menu. O trecho de código do nosso adapter fica:

List<MenuItem> menuItems = createMenu();
adapter = new ArrayAdapter<MenuItem>(
                getActionBar().getThemedContext(),
                android.R.layout.simple_list_item_1,
                android.R.id.text1,
                menuItems);
listView.setAdapter(adapter);

Observe que o adapter continua sendo um ArrayAdapter, mas agora esta ligado ao tipo MenuItem e a sua lista de objetos é a lista de items do menu. Essa lista pode ser obtida via código da seguinte forma:

private List<MenuItem> createMenu() {
    Menu menu = new MenuBuilder(getActivity());
    MenuInflater menuInflater = getActivity().getMenuInflater();
    menuInflater.inflate(R.menu.nav_menu, menu);
    List<MenuItem> menuItems = new ArrayList<MenuItem>();
    for(int i = 0;i < menu.size();i++) {
        MenuItem item = menu.getItem(i);
        menuItems.add(item);
    }
    return menuItems;
}

Com isso já temos o menu pronto com todos os itens do nosso XML. Caso queiramos um icone em cada item da lista do menu, basta implementar um adapter com um layout que contenha uma imagem e usar a propriedade icon do próprio MenuItem para especificar a imagem do item do menu.

A parte visual esta pronta, mas ele ainda não esta funcional. O próximo passo é reimplementar o método de tratamento de clique:

@Override
public void onNavigationDrawerItemSelected(MenuItem item) {
    …
    switch (item.getItemId()) {
        case R.id.menu_home:
            fragment = new HomeFragment();
            break;
        case R.id.menu_find_people:
            fragment = new FindPeopleFragment();
            break;
        case R.id.menu_photos:
            fragment = new PhotosFragment();
            break;
    }
    …
}

A mudança principal é diferenciar cada item do menu através do seu id. Para isso, estamos passando agora o próprio item do menu que foi clicado. O método getItemId do MenuItem e as constantes R.id.menu_home, R.id.menu_find_people e R.id.menu_photos são usados para saber qual o item foi clicado.

Observe que agora não mais usamos a posição da lista para identificar qual o item do menu foi clicado. Tudo é feito pelo id do menu que foi definido no XML.

Esta solução irá tornar mais fácil e rápida a utilização do Nav Drawer e também quando formos reusá-lo em outras aplicações. Com ela conseguimos:

Menos probabilidade a erros

A vantagem é que qualquer mudança no menu, seja para adicionar um novo item ou mudar a ordem atual dele, fica restrito ao XML. Podemos alterar icone, título, ordem dos itens sem ser preciso mexer no código do adapter nem no fragmento do menu.

Poupa tempo e complexidade na implementação do menu para a aplicação.

Como visto acima, implementar e manter um menu para uma aplicação demanda tempo. Sobretudo quando queremos reimplementar o mesmo menu para outras aplicações. Neste caso, ao reusar o menu em outro projeto teremos que mexer novamente no código.

Este tipo de tarefa, apesar de ser simples quando já feita anteriormente, adiciona mais complexidade ao projeto, irá despreender tempo e será mais uma possibilidade de introduzir erros.

Como o menu é definido em um xml, não é necessário intervir no código para adicionar novas ações. Basta modificar o xml e definir o tratamento de evento pelo ID do item clicado que teremos o Navigation Drawer funcional. Com isso estamos adicionado ao nosso projeto manutenabilidade, reusabilidade e diminuição da complexidade do código. Ou seja, em face as mudanças deixamos muito fácil a modificação do código existente.

Você pode ver as mudanças necessárias para implemetar esse tipo de menu com XML através do meu github. O código do Nav Drawer completo também pode ser obtido no link.

Advertisements

2 comments on “Usando XML de uma ActionBar para criar um menu Navigation Drawer

  1. Thales, boa tarde,

    Parabéns pela matéria

    Essa abordagem é recomendada pela google ?

    Cumprimentos
    Malainho

    • Olá Malainho, agradecido em você ter gostado.

      Até onde eu sei não. Nesse quesito, a criação do Navigation Drawer fica muito a cargo do desenvolvedor.

      A meu ver não vejo motivos nessa abordagem que interfiram no funcionamento da app.

      Abs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s