Suporte a Tablets e Smartphones no Android

A plataforma Android pode ser executada em uma grande variedade de tamanhos de telas e o sistema redimensiona graciosamente a interface da aplicação para caber em cada um desses tamanhos. Tipicamente, tudo o que você precisa fazer é projetar a sua interface de forma que ela seja flexível e otimizar alguns elementos para tamanhos diferentes pelo fornecimento de recursos alternativos (como layouts alternativos que reposicionam alguns views ou valores de dimensionamento alternativos para os views). Porém, algumas vezes você pode querer dar um passo adiante e otimizar toda a experiência do usuário em diferentes tamanhos de tela.

Por exemplo, tablets oferecem mais espaço no qual sua aplicação pode apresentar múltiplos conjuntos de informações de uma vez, enquanto um dispositivo de mão (como um smartphone) normalmente necessita que você divida esses conjuntos e exiba-os separadamente. Assim, mesmo uma interface projetada para dispositivos manuais que seja redimensionada de forma adequada em um tablet, não se beneficia do potencial da tela maior do tablet para melhorar a experiência do usuário.
Com o Android3.0 (API level 11), o Android introduziu um novo framework que permite que você projete de forma mais eficiente Activities que tirem vantagem de telas maiores: a API Fragment. Fragmentos permitem que você separe componentes de comportamento distintos de sua interface em partes separadas, que você pode combinar para criar layouts multi-painel quando a aplicação estiver sendo executada em um tablet ou coloca-las em Activities separadas quando executada em um dispositivo de mão. O Android 3.0 também introduziu o ActionBar, que fornece uma interface dedicada no topo da tela para identificar a aplicação e fornecer ações e meios de navegação ao usuário.
Esse artigo é um guia que pode ajudar você a criar uma aplicação que oferece uma experiência ao usuário otimizada e única tanto em dispositivos de mão quanto em tablets, usando fragmentos e a barra de açções (Action Bar). Antes de continuar a ler esse guia, é importante ler primeiro o artigo Suporte a múltiplas telas no Android. Esse artigo descreve os principios fundamentais para desenvolver uma interface que suporte diferentes tamanhos e densidades de tela com layouts flexíveis e bitmaps alternativos, respectivamente.

Diretrizes básicas


Abaixo seguem algumas diretrizes que ajudarão você a criar uma aplicação que forneça uma experiência ao usuário otimizada e única tanto em dispositivos de mão quanto em tablets:

  • Construa sua Actibity baseada em fragmentos que você pode reutilizar em combinações diferentes – em layouts multiu-painel em tablets ou de um único painel em dispositivos de mão. Um Fragmento representa um comportamento ou pedaço da interface com o usuário em um Activity. Você pode imaginar um fragmente como uma seção modular de uma Activity, que possui seu próprio ciclo de vida e que você pode adicionar ou remover enquanto a Activity estiver sendo executada.
  • Use a barra de ações, mas siga as boas práticas para garantir que seu projeto seja flexível o suficiente para que o sistema ajuste o layout da barra de ações de acordo como o tamanho da tela. O ActionBar é um componente da interface para que as Activities que substitui a tradicional barra de titulo do topo da tela. Por padrão, a barra de ações inclui o logotipo da aplicação do lado esquerdo, seguido pelo titulo dela, e acesso ao menu de opções do lado direito.Você pode fazer com que item do menu opções apareçam diretamente na barra de ações. Você pode também adicionar recursos de navegação a barra de ações, como abas ou uma lista drop-down, e usa o ícone da aplicação para complementar o comportamento do botão Back com opções para navegar para a tela inicial da aplicação ou subir um nível na hierarquia dela.
  • Implemente layouts flexíveis, como discutidas no artigo Best Practices para suportar múltiplas telas e no post do blog Thinking Like a Web Designer.Um layout flexível permite que sua aplicação se adapte a vários tamanhos de tela. Nem todos os tablets ou dispositivos de mão tem o mesmo tamanho. Enquanto você pode fornecer diferentes combinações de fragmentos para “tablets” ou “handsets”, ainda é necessário que cada projeto seja flexível para redimensionar a interface para cada variação de tamanho e aspect ratio.

Criando um painel único e um layout multi-painel


Permanecendo compatível com versões antigas

Se você quiser usar fragmentos em sua aplicação e permanecer compatível com versões do Android anteriores a 3.0, você pode fazer isso pelo uso da Support Library( que pode ser baixada pelo SDK Manager).
Essa biblioteca de suporte inclui APIs para fragmentsloaders, e outras APIs adicionadas em versões mais novas do Android. Simplesmente adicionando essa biblioteca ao seu projeto Android, você pode usar versões compatíveis dessas APIs em sua aplicação e permanecer compatível com o Android 1.6 (o valor de android:minSdkVersion pode ser tão baixo quanto 4). Para obter informações sobre como obter a biblioteca e começar a usa-la, veja o documento Support Library.
A biblioteca de suporte não fornece APIs para a barra de ações, mas você pode usar o código da aplicação exemplo, Action Bar Compatibility, para criar uma barra de ações que suporte todos os dispositivos.

A maneira mais efetiva de criar uma experiência com o usuário distinta para tablets e dispositivos de mão é criar layouts com diferentes combinações de fragmentos, como layouts “multi-painel” para tablets e com um único painel para dispositivos de mão. Por exemplo, uma aplicação de notícias em um tablet poderia exibir uma lista de artigos do lado esquerdo e o artigo completo no lado direito – selecionando um artigo a esquerda o artigo seria exibido na direita. Em um smartphone, porém, esses dois componentes seriam exibidos em telas separadas – selecionado um artigo em uma lista toda a tela é mudada para exibir o artigo. Existem duas técnicas para por em prática esse design com fragmentos:

  • Múltiplos fragmentos, uma Activity: Use uma Actiity não importando o tamanho do dispositivo, mas decida durante a execução se deve combinar os fragmentos no layout (para criar um design multi-painel) ou alternar fragmentos (para criar um design de único painel). Ou…
  • Múltiplos fragmentos, múltiplas Activities: Em um tablet, coloque múltiplos fragmentos em uma Activity; já em um smartphone, use Activities separadas para hospedar cada fragmento. Por exemplo, quando o design do tablet usa dois fragmentos em uma Activity, use a mesma Activity para smartphones, mas forneça um layout alternativo que inclua o primeiro fragmento. Quando a aplicação estiver sendo executada em um smartphone e você precisa alternar entre fragmentos (como ao selecionar um item), inicie uma outra Activity que hospedará p segundo fragmento.

A abordagem que você deve escolher depende de seu design e preferências pessoas. A primeira opção (uma Activity, fragmentos alternados) requer que você determine o tamanho da tela durante a execução e dinamicamente adicione cada fragmento como for apropriado – ao invés de declarar os fragmentos no layout XML da Activity – porque você não pode remover um fragmento de uma Activity se ele foi declarado no layout XML. Quando usar a primeira técnica, você pode precisar também atualizar a barra de ações a cada vez que os fragmentos forem alterados, dependendo de quais ações ou modo de navegação estiverem disponíveis para cada fragmento. Em alguns casos, esses fatores podem não afetar o seu design, de forma que usar uma Activity e alternar fragmentos pode funcionar bem (especialmente se o seu design para tablets necessite que você adicione fragmentos dinamicamente de qualquer forma). Outras vezes, porém, alternar dinamicamente fragmentos em seu design para dispositivos de mão pode fazer o seu código mais complicado, porque você vai precisar gerenciar todas as combinações de fragmentos no código da Activity (ao invés de usar layouts alternativos para definir as combinações de fragmentos) e gerenciar o vai-e-vem de fragmentos por si (ao invés de permitir que a pilha de Activity manipule a navegação entre fragmentos).
Esse artigo foca na segunda opção, em que você exibe cada fragmento em uma Activity separada quando esta em uma tela pequena. Usar essa técnica significa que você pode usar arquivos de layout alternativos que definem diferentes combinações de fragmentos para diferentes tamanhos de tela, mantendo o código do fragmento modular, simplificando o gerenciamento da barra de ações, e deixando o sistema manipular a navegação entre fragmentos em um tablet.
A figura 1 ilustra como uma aplicação com dois fragmentos pode ser arranjada tanto em dispositivos de mão quanto em tablets quando se usa Activities separada para o design da interface:

Na aplicação mostrada na figura 1, a Activity A é a “activity principal” e usa layouts diferentes para exibir um ou dois fragmentos ao mesmo tempo, dependendo do tamanho da tela:

  • Em uma tela de tablet, o layout da Activity A contém tanto o fragmento A quanto o fragmento B.
  • Em uma tela de um dispositivo de mão, o layout da Activity A contém apenas o fragmento A (a ListView). Para que sejam exibidos os detalhes no fragmento B, a Activity B deve ser aberta.

Nota: A Activity B nunca é usada em um tablet. Ela simplesmente é um contêiner para apresentar o fragmento B, de forma que é usada apena em dispositivos de mão quando os dois fragmentos precisam ser exibidos separadamente.
Dependendo do tamanho da tela, o sistema aplicará um arquivo de layout main.xml diferente:

public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
    ...
    /** This is a callback that the list fragment (Fragment A)
        calls when a list item is selected */
    public void onItemSelected(int position) {
        DisplayFragment displayFrag = (DisplayFragment) getFragmentManager()
                                    .findFragmentById(R.id.display_frag);
        if (displayFrag == null) {
            // DisplayFragment (Fragment B) is not in the layout (handset layout),
            // so start DisplayActivity (Activity B)
            // and pass it the info about the selected item
            Intent intent = new Intent(this, DisplayActivity.class);
            intent.putExtra("position", position);
            startActivity(intent);
        } else {
            // DisplayFragment (Fragment B) is in the layout (tablet layout),
            // so tell the fragment to update
            displayFrag.updateContent(position);
        }
    }
}

Suporte a vários tamanhos baseado na largura da tela

O Android 3.2 (API Level 13) adiciona novas APIs que fornecem um controle mais preciso sobre quais tamanhos de tela sua aplicação irá suportar e quais recursos ela usará, através da declaração de tamanhos de tela baseados na largura mínima que o layout necessita. Por exemplo, tanto um dispositivo de 5″ quanto um de 7″ qualificam-se como tela “grande”, assim seus recursos de layout “grande” são usados nos dois dispositivos. Com o API Level 13, você pode distinguir entre esses dois tamanhos com base na largura da tela, medida em pixels independente de densidade.
Nota: Apesar do exemplo acima de layout para tablets ser baseado num qualificador de configuração de tela “grande”, você deve também usar o novo qualificador de tamanho “minimum width” de maneira a controlar com precisão o tamanho de tela no qual o sistema irá aplicar seu layout.

Como a aplicação responde quando um usuário seleciona um item da lista depende  da disponibilidade do fragmento B no layout:

  • Se o Fragmento B estiver no layout, a Activity A notifica o Fragmento B para se auto atualizar.
  • Se o Fragmento B não estiver no layout, a Activity A inicia a Activity B (que hospeda o Fragmento B).

Para implementar esse padrão em sua aplicação, é importante que você desenvolva seus fragmentos de forma que sejam o mais compartimentalizado possível. Especificamente, você deve seguir duas diretrizes:

  • Não manipule um fragmente diretamente a partir de outro.
  • Mantenha todo o código de interesse do conteúdo de um fragmento dentro desse fragmento, ao invés de coloca-lo no código da Activity hospedeira.

Para evitar a chamada de um fragmente diretamente a partir de outro, defina uma interface de callback em cada classe de fragmento, que pode ser usada para entregar eventos para a Activity hospedeira, que implementa a interface de callback. Quando a Activity recebe uma chamada devido a um evento (como a seleção de um item da lista pelo usuário), a Activity responde baseada na configuração atual do fragmento.
Por exemplo, a Activity A do exemplo acima pode manipular a seleção de itens dependendo do uso de um tablet ou dispositivo de mão dessa forma:

public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
    ...
    /** This is a callback that the list fragment (Fragment A)
        calls when a list item is selected */
    public void onItemSelected(int position) {
        DisplayFragment displayFrag = (DisplayFragment) getFragmentManager()
                                    .findFragmentById(R.id.display_frag);
        if (displayFrag == null) {
            // DisplayFragment (Fragment B) is not in the layout (handset layout),
            // so start DisplayActivity (Activity B)
            // and pass it the info about the selected item
            Intent intent = new Intent(this, DisplayActivity.class);
            intent.putExtra("position", position);
            startActivity(intent);
        } else {
            // DisplayFragment (Fragment B) is in the layout (tablet layout),
            // so tell the fragment to update
            displayFrag.updateContent(position);
        }
    }
}

Quando DisplayActivity (Activity B) é iniciado, ele lê os dados entregues pelo Intent e passa-os ao DisplayFragment (Fragmento B).
Se o Fragmento B precisar entregar um resultado de volta para o Fragmento A (pois a Activity B que foi iniciada com startActivityForResult()), o processo funciona de forma similar a uma interface de callback entre o Fragmento B e a Activity B. Isso é, a Activity B implementa uma interface de callback diferente definida pelo Fragmento B. Quando a Activity B recebe a chamada com o resultado do fragmento, configura o resultado para a Activity (com setResult()) e encerra-o. A Activity A então recebe o resultado e entrega-o ao Fragmento A.
Para um demonstração dessa técnica para criação de diferentes combinações de fragmentos para tablets e dispositivos de mão, veja a versão atualizada do exemplo Honeycomb Gallery.

Usando a Barra de Ações (Action Bar)


Action Bar é um componente importante para aplicações Android tanto em tablets quanto em dispositivos de mão. Para garantir que ela se comporte de forma apropriada em todas as telas, é importante que você use as APIs da ActionBar sem adicionar customizações complexas. Usando a API padrão ActionBar para criar a sua barra de ações, o sistema Android faz todo o trabalho de adaptação da barra em diferentes tamanhos de tela. Abaixo segue algumas dicas a serem seguidas quando criar sua barra de ações:

  • Quando configurar um item de menu para ser um item de ação, evite o valor "always". No seu menu resource, use "ifRoom" para o atributo android:showAsAction se você quiser que o item do menu apareça na barra de ação. Porém, você deve precisar do "always" quando um view  de ação não fornecer uma ação padrão para o menu (isso é, precisa aparecer como um view  de ação). Porém, você não deve usar "always" mais do que um ou duas vezes. Em quase todos os casos, use "ifRoom" como o valor de "android:showAsAction" quando você quiser que o item apareça como um item de ação. Forçar com muitos itens na barra de ações pode fazer com que a interface fique amontoada e os itens podem sobrepor outros elementos da barra de ação como o titulo ou itens de navegação.
  • Quando adicionar itens a barra de ação com um texto de título, também forneça um ícone, quando for apropriado, e declara showAsAction="ifRoom|withText". Dessa forma, se não houver espaço suficiente para o titulo, mas houver espaço suficiente para o ícone, então apenas esse último sera usado.
  • Sempre forneça um titulo para os seus itens da barra de ações, mesmo se você não ativar o atributo "withText", porque os usuários podem visualizar o titulo como uma “dica” executando um “clique longo” sobre o item – o titulo aparece momentaneamente em uma mensagem. Fornecendo um titulo também é importante para a acessibilidade, pois leitores de tela podem ler em voz alta o titulo do item mesmo que não esteja visível.
  • Evite usar modos de navegação personalizados quando possível. Use as abas embutidas e os modos de navegação “drop-down” quando possível—eles são projetoados de forma que o sistema pode se adaptar a diferentes tamanhos de tela. Por exemplo, quando a largura for muito estreita tanto para as abas quando outros itens da barra (como em um dispositivo de mão na orientação retrato), as abas aparecem abaixo da barra de ação (modo conhecido como barra de ação empilhada). Se você precisa criar um modo de navegação personalizado ou outros views na barra de ações, proceda testes deles em telas pequenas e faça qualquer ajuste necessário para suportar uma barra de ações estreita.

Por exemplo, as telas abaixo demonstram como o sistema pode adaptar a barra de ações baseando-se no tamanho disponível de tela. No dispositivo de mão, apenas dois itens cabem na tela, de forma que os demais itens aparecem em um menu (pois android:showAsAction foi configurado para "ifRoom") e as abas aparecem em uma linha separada. No tablet, mais itens podem ser encaixados assim como as abas.

Usando uma barra de ações dividida

Quando sua aplicação estiver sendo executada no Android 4.0 (API level 14) ou superior, haverá um modo extra disponível para a barra de ações chamada “barra de ações divida”. Quando você ativa esse modo, uma barra separada aparece na parte inferior da tela para exibir todos os itens quando a Activity estiver sendo executada em uma tela estreita. Dividir a barra de ações garante que uma quantidade razoável de espaço seja disponibilizado para exibir itens em uma tela estreita e também deixe espaço para elementos de navegação e titulo na parte superior.
Para ativar o modo “barra de ações divida”, simplesmente adicione uiOptions="splitActionBarWhenNarrow" ao seu  elemento <activity> ou<application> no arquivo de manifesto.

Se você quiser esconder a barra de ação principal na parte superior, por estar usando as abas de navegação junto com a barra de ações divida, chame setDisplayShowHomeEnabled(false) para  desativar o ícone da aplicação na barra de ações. Nesse caso, nada será deixado na barra de ações principal, de forma que ela desaparece e tudo que permanece a vista são as abas de navegação e os itens de ação na parte inferior., como mostra a imagem acima.
Nota: Apesar do atributo uiOptions ter sido adicionado no Android 4.0 (API level 14), você pode incluir ela com segurança em sua aplicação mesmo que o valor de minSdkVersion esteja configurado para um valor menor do que "14" para manter a compatibilidade com versões antigas do Android. Quando a aplicação estiver sendo executada em versões antigas, o sistema simplesmente ignora o atributo que ele não entende. A única condição para adicionar essa opção ao seu manifesto é que você precisa compilar sua aplicação para um versão de plataforma que suporte a API level 14 ou superior. Somente certifique-se de que você não esteja usando abertamente APIs no código de sua aplicação que não são suportadas pela versão declarada pelo atributo minSdkVersion.

Outras dicas de projeto


  • Quando estiver trabalhando com uma ListView, considere como você deve fornecer mais ou menos informações em cada item da lista baseado no espaço disponível.Isso é, você pode criar lauouts alternativos para serem usados pelos itens de sua lista assim como em telas grandes pode-se exibir mais detalhes de cada item.
  • Crie arquivos de recurso alternativo para valores como inteiros, dimensões e mesmo boleanos. Usando qualificadores de tamanho para esse recursos, você pode facilmente aplicar diferentes tamanhos de layout, de fonte, ou ativar/desativar comportamentos baseados no tamanho da tela.