Layouts mais flexíveis com Android Fragments

Desenvolver um aplicativo Android com uma boa interface com o usuário não é fácil: os diferentes tamanhos de tela podem dar trabalho para chegar a um resultado satisfatório em múltiplos dispositivos. Quando levamos em conta os tablets surge um problema ainda maior: como criar uma aplicação para smartphone que, ao rodar em um tablet, aproveite o espaço extra que o dispositivo oferece? Mais ainda: como fazer isso de maneira fácil?

A partir da versão 3 do Android, através da a API de Fragments, podemos dividir nossa tela e dar comportamento a cada pedaço (fragmento) de uma view com um Fragment.

Como exemplo vamos usar uma aplicação que contém uma listagem de nomes de arquivos de fotos em um ListView. Queremos que o evento de clique em um item da lista nos mostre a imagem selecionada.

Caso nosso Tablet esteja na horizontal gostaríamos que a listagem e a imagem escolhida apareçam na mesma tela, conforme ilustrado na imagem abaixo:

Comportamento da seleção de uma imagem no Tablet na horizontal

Comportamento da seleção de uma imagem no Tablet na horizontal

No caso do tablet estar na vertical gostaríamos de ver apenas a listagem ou a imagem na tela:

Comportamento da seleção da imagem em um Tablet na vertical

Comportamento da seleção da imagem em um Tablet na vertical

Para que isso seja possível precisamos ter layouts diferenciados para o tablet em landscape. Podemos fazer isso colocando qualifiers nas pastas de layout. Em nosso caso telas grandes em landscape.

Esquema de qualifiers nas pastas de Layout

Na view em landscape podemos usar a tag fragment:

<LinearLayout ...>
  <fragment android:id="@+id/fragment1"
     android:name="br.com.caelum.hubbletablet.ListaImagensFragment" .../>

  <fragment android:id="@+id/fragment2"
     android:name="br.com.caelum.hubbletablet.ImagemFragment" .../>
</LinearLayout>

Enquanto a outra será apenas um framelayout para inflar:

<LinearLayout ...>
     <FrameLayout android:id="@+id/ambos_fragments" .../>
</LinearLayout>

Para criarmos um Fragment basta herdarmos da classe e implementar o método onCreateView, onde receberemos um inflater o qual podemos usar para inflar uma view que queremos associar ao Fragment.

public class ListaImagensFragment extends Fragment {
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

       View viewLista = inflater.inflate(R.layout.lista, null);
		
       ListView listaImagens = (ListView) viewLista
           .findViewById(R.id.listaImagens);

       colocaDadosNoListView(listaImagens);
   }
   // ...
}    

O controle da tela ainda é feito pela Activity e através dela manipularemos os Fragments. Caso o tablet esteja na horizontal, a Activity estará associada à versão do main.xml que usa a tag fragment, que informa ao Android as classes dos Fragments que desejamos usar, então o próprio Android se encarregará de instanciá-los. Caso o tablet esteja na vertical, a Activity estará associada à versão do main.xml que contem apenas uma tag FrameLayout, nesse caso precisaremos criar o fragment que trará a lista e associá-lo ao FrameLayout. Fazemos isso no onCreate da Activity:

getFragmentManager().beginTransaction()
    .add(R.id.ambos_fragments, new ListaImagensFragment())
.commit();

Para termos a funcionalidade de apertar o botao de back no Android e o mesmo empilhar e desempilhar os fragments, podemos fazer:

getFragmentManager().beginTransaction()
    .replace(R.id.ambos_fragments, new ListaImagensFragment())
    .addToBackStack(null)
.commit();

O código acima só deve ser executado se o tablet estiver na horizontal, portanto precisamos fazer essa verificação, uma maneira de fazer isso é através do Activity.getResources().getConfiguration().orientation.

No onCreateView do ListaImagensFragment podemos guardar em um atributo o endereço da imagem selecionada quando houver um clique em um item na lista. Como fazemos agora para passar essa informação para o ImagemFragment para que ele possa mostrar a imagem?

Quando estamos trabalhando apenas com Activities temos que usar o putExtra em Intents para enviar dados de uma Activity para outra. O envio de informações de um Fragment para o outro pode ser feito na Activity de uma forma menos orientada a Strings e maps, tornando nosso código mais orientado a objetos.

Se o tablet estiver na horizontal:

ListaImagensFragment listaFragment = (ListaImagensFragment) 
	getFragmentManager().findFragmentById(R.id.fragment1);
			
ImagemFragment imagemFragment = (ImagemFragment)
	getFragmentManager().findFragmentById(R.id.fragment2);
			
imagemFragment.setImagem(listaFragment.getImagemSelecionada());
imagemFragment.atualizaImagem();

Se estiver na vertical:

ListaImagensFragment listaFragment = (ListaImagensFragment)
	getFragmentManager().findFragmentById(R.id.ambos_fragments);
			
ImagemFragment imagemFragment = new ImagemFragment();
imagemFragment.setImagem(listaFragment.getImagemSelecionada());
			
getFragmentManager().beginTransaction()
	.replace(R.id.ambos_fragments, imagemFragment)
	.addToBackStack(null)
.commit();

Essa solução ficou tão interessante que os desenvolvedores da plataforma resolveram criar um pacote de compatibilidade para poder usar praticamente as mesmas ideias nos dispositivos antigos, sem precisar de um dispositivo moderno com versão 3.x ou 4.x, de maneira bem simples.

Um grande cuidado que devemos ter com o uso de Fragments é evitar espalhar por toda aplicação if’s que verificam se a tela é grande ou o aparelho está na horizontal, etc. Podemos criar diferentes classes que implementam o comportamento em aparelhos grandes e em pequenos e então fazê-las implementar uma interface java. Assim podemos utilizar o polimorfismo e chegar a uma solução mais elegante.

15 Comentários

  1. Priscila 24/01/2012 at 15:55 #

    Adorei o post, parabéns a Caelum mais uma vez. Sempre muito útil =)

  2. Felipe Conde 24/01/2012 at 16:50 #

    Aê Erich!
    Excelente post cara! Bastante didático!
    Todas as informações em um só lugar..
    O uso de Fragments é bastante útil quando se desenvolve para tablets!

    abraço!

  3. Jean Fernandes 31/01/2012 at 09:47 #

    Muito útil, inclusive já tive muitos problemas quando instalava em tablets.

    Parabéns pelo post.

    abs.

  4. Erich Egert 31/01/2012 at 18:35 #

    Valeu, Jean! Espero que ajude a motivar a galera a dar suporte aos dispositivos de tela grande que estão cada vez mais comuns…

  5. Erich Egert 31/01/2012 at 18:43 #

    Valeu Felipe! O post foi uma pequena demonstração de tópicos bem legais: o Application Resources e seus qualifiers, Fragments e o pacote de compatibilidade. Espero que não tenha ficado longo demais 🙂

  6. Vinicius 24/09/2012 at 10:59 #

    COmo seria o codigo dos arquivos imagem.xml e lista.xml?

  7. Daniel Gabarra 18/10/2012 at 10:55 #

    Felipe,
    Você saberia me dizer se já existem apk que gerenciem a divisão da tela para diferentes aplicativos, voltados para o usuário final?
    Sei que o Samsung note tem algo do tipo, mas não encontrei nenhuma outra referência a respeito.
    Obrigado,

    Daniel

  8. Marcos 30/03/2013 at 12:16 #

    Muito bom mesmo, parabéns pelo post 🙂

  9. Bolívar Mardegan 25/02/2014 at 20:56 #

    Boa Noite Erick. Primeiramente parabéns pelo material!
    Estou com um problema:
    – Meu emulador quando rotaciona a tela, a aplicação continua sendo exibida na vertical. Tanto as aplicações que estou criando quanto as aplicações default.
    Tem alguma configuração onde devo corrigir isso?

  10. thiano pereira lima 10/04/2014 at 13:03 #

    gostei do post, porem gostaria de saber como faco para recuperar dados de fragments quando se usa FragmentAdapter ao em vez de ? Criei um tabswipe e gostaria de ao clicar no botao salvar da minha actionbar conseguir pegar o dados de todos os fragments. Nao encontro solução em nenhum lugar para isso.

  11. thiano pereira lima 10/04/2014 at 13:04 #

    corrigindo o texto acima eu estou usando FragmentPagerAdapter e nao FragmentAdapter

  12. Edmar Luiz Leone Fogaça 11/11/2014 at 17:20 #

    Olá amigos,
    Pelos comentários de vocês, percebi que todos são fera no assunto “Layouts mais flexíveis com Android Fragments”
    Como estou começando no Android, não consegui montar o aplicativo sugerido na matéria. Pergunto, se num especial favor, alguém poderia enviar-me o pacote do projeto compactado. Agradeço muito.
    Abs elfogaca@gmail.com

  13. Jean 19/11/2014 at 12:56 #

    Me diga uma coisa, o que há na classe Main.java ? Ela é uma Activity ou Fragment ? Estou tentando fazer mas está dando erro!

  14. Adriano 19/03/2015 at 18:21 #

    Erich , tudo bom ?
    gostei do seu post, e tenho um desafio da qual estou passando.

    Este modelo de fragment é muito bom, mas preciso fazer com que o primeiro fragment ( que tem a lista ) , esteja em um Slide Panel que quando esta em modo smartphone fica fechado, e quando esta em modo tablet esteja aberto. Assim terei um menu aberto em tablet e fechado em mobile.

    Sei que tem o Navegation Drower , mas ele não fica aberto para o modo tablet. Então sei que o Slide Panel fica.

    pode me ajudar?

  15. Paulo Linhares 21/08/2015 at 04:51 #

    O mais sucinto e melhor que já li. Limpo, direto e eficaz . Parabéns.

Deixe uma resposta