Por que eu devo ler este artigo:

Este artigo apresenta uma das formas de lidar com um dos maiores desafios no desenvolvimento de sistemas para dispositivos móveis: a portabilidade. Manipular os diferentes tamanhos de interface e orientação dos dispositivos não é nada fácil.

Neste contexto, o artigo apresenta duas soluções, sendo o uso de fragments recomendado quando portabilidade for uma característica importante em sua aplicação. Para testar e demonstrar alguns recursos do Android Fragments será desenvolvido um exemplo clássico de aplicativo Lista – Detalhe, onde é apresentada uma lista de elementos com palavras chaves, e ao clicar sobre este, detalhes são apresentados.


Guia do artigo:

Um dos maiores desafios para os programadores mobile sempre foi a portabilidade visual dos aplicativos. Desde os primórdios do desenvolvimento para a plataforma móvel, onde existiam apenas plataformas de desenvolvimento mais limitadas como Java ME, Symbiam e Brew, um dos grandes desafios era garantir que um programa desenvolvido rodasse bem na maior quantidade de dispositivos possíveis.

Ao contrário da plataforma Desktop, por exemplo, onde existe um conjunto limitado de resoluções possíveis (800x600, 1024x768, etc), nos dispositivos móveis a quantidade de resoluções disponíveis é muito grande, isso se tratando de devices como smartphones, onde embora haja diferenças, as resoluções são parecidas.

Entretanto, um mesmo aplicativo, em teoria, deveria rodar bem em um smartphone com resolução 480x800, assim como em um tablet com resolução 720x1280. Outro agravante nestes devices é que a resolução é mutável, sendo 720x1280 se o tablet estiver na posição retrato, ou 1280x720 se estiver na posição paisagem.

Uma técnica para “resolver” este problema é aumentar proporcionalmente o tamanho dos componentes da tela de acordo com a resolução. Assim, em um device 600x800, os componentes visuais terão a metade do tamanho se comparado a um device 1200x1600.

Aparentemente esta técnica resolve o problema, entretanto, não de forma inteligente, pois poderíamos apresentar ainda mais informações, por exemplo, em device maiores, e resumir o número de informações em devices menores.

Um exemplo muito interessante do uso desta técnica é o aplicativo BM&F Bovespa, que possui um comportamento diferenciado em smartphones (Figura 1) e em tablets (Figura 2).

BM&F Bovespa em um Smartphone
Figura 1. BM&F Bovespa em um Smartphone
BM&F Bovespa em um Tablet
Figura 2. BM&F Bovespa em um Tablet

Entretanto, outros exemplos desta técnica podem ser observados nas aplicações nativas dos próprios devices Android como, por exemplo, a calculadora, que na orientação retrato apresenta apenas as operações básicas de uma calculadora, já na orientação paisagem apresenta operações presentes nas calculadoras científicas, como raiz quadrada e exponenciação (Figura 3).

Aplicativo Calculadora de um device Android
Figura 3. Aplicativo Calculadora de um device Android

O problema

Desenvolver aplicativos com conteúdo dinâmico, que se ajusta ao tamanho ou orientação da tela automaticamente, fazendo com que quanto maior a tela, mais informação seja apresentada ao mesmo tempo.

A solução

Utilizar o Android Fragments, este disponível a partir da versão 3.0 do Android (HoneyComb). Ele foi criado para minimizar as incompatibilidades visuais dos aplicativos Android que rodam em smartphones (tela pequenas) e tablets (tela grande). A ideia é que a tela seja dividida em pequenos pedaços ou fragmentos (o que dá o nome ao componente).

Na verdade, um Fragment é muito mais do que um pedaço de tela, este pode ser considerado um componente visual, permitindo o desenvolvimento modular e sua reutilização em outras partes do aplicativo. Ele possui um ciclo de vida próprio e características próprias também, como efeito de transição de telas, entre outros.

Cenário para analisar o problema e a solução

Para testar e demonstrar alguns recursos do Android Fragments será desenvolvido um exemplo clássico de aplicativo Lista – Detalhe, onde é apresentada uma lista de elementos com palavras chaves, e ao clicar sobre este, detalhes são apresentados.

Este aplicativo demonstra bem a funcionalidade dos Fragments, já que a primeira parte (em azul na Figura 4) representa a listagem, e a segunda parte (em laranja) representa os detalhes do que foi selecionado.

Cada uma destas partes pode ser um Fragment, e as mesmas podem ser ajustadas de acordo com o tamanho da tela. Se a tela for maior ou estiver na posição paisagem, ambas as partes (azul e laranja) serão apresentadas na mesma tela, se a tela for menor ou estiver na posição retrato, cada parte será apresentada separadamente, conforme a Figura 4.

Figura que representa o cenário abordado
Figura 4. Figura que representa o cenário abordado

Assim, em um primeiro momento será desenvolvido o software sugerido sem Fragments. Em seguida, este será ajustado com o uso do Fragment, sendo este último a melhor prática para o problema proposto.

Para o desenvolvimento, será utilizada uma lista estática com nome de animais, e ao ser clicada, este apresentará a imagem do animal.

Aplicativo desenvolvido sem Fragments

Será desenvolvido um projeto Android chamado “SemFragments”, este armazenado no pacote br.edu.utfpr.semfragments. Para os testes, foi utilizado o Android 4.0.3 (API 15 – IceCream Sandwich), conforme Figura 5.

Tela para criação do projeto Sem Fragments
Figura 5. Tela para criação do projeto Sem Fragments

Por fim, a criação do projeto foi concluído, utilizando o nome MainActivity para a classe, e activity_main para a interface gráfica.

Após a criação do projeto, algumas imagens foram adicionadas nas pastas drawable do projeto, totalizando cinco animais (cinco itens na lista), conforme apresentada na Figura 6.

strutura das imagens presentes no projeto
Figura 6. Estrutura das imagens presentes no projeto
Dica: Como cada imagem é mapeada como variável no arquivo R.java, estas devem possuir nomes compatíveis com nome de variáveis, sendo que os arquivos não devem iniciar com números, não podem ter espaços ou caracteres especiais.

Feito isso, foi criado um arquivo com o nome list_elements.xml, na pasta values do projeto, contendo os elementos da lista, conforme a Listagem 1.


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="animais_sul">
        <item >Curucaca</item>
        <item >Gralha Azul</item>
        <item >Graxaim</item>
        <item >Ouriço</item>
        <item >Quero Quero</item>
    </string-array>
</resources>
Listagem 1. list_element.xml – Com androidannotations

Os elementos formarão a tela inicial do aplicativo. Esta é declarada no arquivo activity_main.xml, conforme apresentado na Listagem 2.

Listagem 2. activity_main.xml – Lista principal com os itens para seleção.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <ListView
        android:id="@+id/lvAnimais"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:entries="@array/animais_sul" />
 
</RelativeLayout>

A interface se resume a um componente ListView (linha 6) cujo nome é lvAnimais (linha 7). Este componente ocupa toda a tela (linhas 8 e 9), e será formado pelos itens presentes no array de String animais_sul (linha 10) criado anteriormente na Listagem 1. O componente ListView foi declarado dentro de um RelativeLayout (linha 1).

Para o processamento desta tela, foi codificada a classe MainActivity.java, apresentada com detalhes na Listagem 3.


 package br.edu.utfpr.semfragments;
  
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ListView;
  
 public class MainActivity extends Activity {
  
    private ListView lvAnimais;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          
          lvAnimais = (ListView) findViewById( R.id.lvAnimais );
          
          lvAnimais.setOnItemClickListener( new AdapterView.OnItemClickListener() {
  
                 @Override
                 public void onItemClick(AdapterView<?> adapter, View view, int position,
                               long id) {
                        
                        tratarSelecao( position );
                        
                 }
          
          } );
    }
  
    protected void tratarSelecao( int position ) {
          Intent i = new Intent( this, ImageActivity.class );
          i.putExtra( "pos", position );
          i.putExtra( "descricao", lvAnimais.getItemAtPosition( position ).toString() );
          startActivity( i );
    }
  
 }
Listagem 3. MainActivity.java – Activity responsável pela listagem principal

Basicamente, essa listagem se resume à declaração do pacote (linha 1) e das classes importadas (linhas 3 a 9), na sequência é declarada a Activity (linha 11), assim como o único componente visual da tela – ListView (linha 13).

O método onCreate (linha 16) executa o método da classe pai (linha 17), assim como apresenta a interface gráfica para o usuário (linha 18). Na linha 20 é recuperado o ListView (linha 20), assim como tratado o evento de clique no item da lista (linha 22).

Esse executará o método tratarSelecao(linha 28), o qual recebe por parâmetro o índice do item selecionado na lista.

O método tratarSelecao() – linha 35 – prepara a chamada para a nova tela, criando um objeto da classe Intent (linha 36), adicionando a este objeto a posição do item selecionado (linha 37), bem como a descrição (linha 38) e por fim, a nova tela é chamada (linha 39). A activity que trata a nova tela chama-se ImageActivity.java, e o arquivo de interface activity_image.xml.

Para criar uma nova activity, deve-se clicar com o botão direito sobre o projeto, escolhendo a opção – NewOther, e na janela apresentada a categoria Android – Android Activity, informando o nome dos arquivos, conforme a Figura 7.

Criação do segundo Activity para apresentação da imagem
Figura 7. Criação do segundo Activity para apresentação da imagem

Após criar o Activity, deve-se codificar o arquivo activity_image.xml, conforme a Listagem 4.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <ImageView
        android:id="@+id/imAnimal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />
    
   <TextView
       android:id="@+id/tvDescricao"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Testando"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="#FFF"
        />    
 
</RelativeLayout>
Listagem 4. activity_image.xml – Interface gráfica para a apresentação da figura

A interface apresentada é organizada pelo gerenciador RelativeLayout (linha 1), onde existem dois componentes visuais: um ImageView (linha 6) e o TextView (linha 12). O primeiro componente apresentará a imagem do animal e ocupará a tela inteira do device (linhas 8 e 9).

Já o TextView apresentará o nome do animal e terá um tamanho maior do que o padrão (linha 19), o texto será em branco (linha 20) e o mesmo ficará ao centro da tela (linhas 16 e 17).

O próximo passo é codificar a Activity, apresentado na Listagem 5.


 package br.edu.utfpr.semfragments;
  
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.Menu;
 import android.widget.ImageView;
 import android.widget.TextView;
  
 public class ImageActivity extends Activity {
  
    private int imagens_id[] = { R.drawable.curucaca,
                 R.drawable.gralhaazul,
                 R.drawable.graxaim,
                 R.drawable.ourico,
                 R.drawable.queroquero };
    
    private ImageView imAnimal;
    private TextView tvDescricao;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_image);
          
          imAnimal = (ImageView) findViewById( R.id.imAnimal );
          tvDescricao = (TextView) findViewById( R.id.tvDescricao );
          
          Intent i = getIntent();
          int pos = i.getIntExtra( "pos", 0 );
          String descricao = i.getStringExtra( "descricao" ); 
  
          imAnimal.setImageResource( imagens_id[ pos ] );
          imAnimal.setScaleType(ImageView.ScaleType.FIT_XY);
          
          tvDescricao.setText( descricao );
          
    }
  
 }
Listagem 5. ImageActivity.java – Classe que apresenta a imagem na tela do device

O código da linha 1 até a linha 8 é responsável pela declaração do pacote e importação das classes utilizadas na Activity. Já a linha 10 é utilizada para a declaração da Activity.

Como o aplicativo tem a função de apresentar uma imagem, que está presente em drawable, na tela do device, foi declarada na linha 12 o id de todas as imagens. Estas imagens estão armazenadas em um array para facilitar a alteração da imagem na tela.

Como pode ser observado, a ordem dos elementos no ListView é a mesma dos ids das imagens no array de int (linha 12), desta forma, o índice do elemento selecionado na tela é o mesmo índice do id da imagem no array, o que facilitará sua apresentação.

As linhas 18 e 19 declaram os dois componentes visuais da Activity que são recuperadas da interface gráfica nas linhas 26 e 27.

Como essa Activity é chamada a partir da tela inicial, e esta passará duas informações (id do item selecionado na lista e a descrição do elemento selecionado), estas informações devem ser recuperadas nesta Activity.

Para isso, utilizamos uma instância de Intent (linha 29), da qual será recuperada a informação referente ao id da imagem (linha 30) e sua descrição (linha 31).

Estas informações valorizarão a propriedade imagem do ImageView (linha 33) e também o texto do TextView (linha 36). Para ficar mais bonito visualmente, aconselha-se estender a imagem usando a propriedade setScaleType (linha 34).

Com isso, o aplicativo está pronto para ser executado e o resultado é apresentado na Figura 8.

Aplicação executada com o device em formato retrato
Figura 8. Aplicação executada com o device em formato retrato, apresentando a lista de elementos na primeira tela (à esquerda) e a imagem correspondente na segunda tela (a direita)

Entretanto, para demonstrar que esta não é uma boa prática de programação, em especial em tablets, que no formato paisagem disponibiliza muito espaço na tela, vamos girar a tela do emulador.

Para isso, pressione simultaneamente CTRL + F12, você observará que a tela gira, assim, execute novamente o aplicativo. Verifique que o resultado não é tão interessante, fica muito espaço subutilizado na tela, conforme apresentado na Figura 9.

Mesma aplicação executada com o device no formato paisagem
Figura 9. Mesma aplicação executada com o device no formato paisagem

Agora vamos ajustar o aplicativo de acordo com o que o Android Fragments disponibiliza para portabilidade visual do aplicativo e aproveitamento de tela para diferentes formatos (retrato e paisagem).

Desenvolvimento Adequado

O aplicativo se comportará de forma parecida com o aplicativo anterior, se o device estiver no formato retrato, o aplicativo apresentará a lista em uma tela, e a imagem correspondente ao item selecionado em outra (Figura 10 – lado esquerdo), já se o device estiver em formato paisagem, uma única tela será apresentada ao usuário, essa com a listagem do lado esquerdo e a imagem do lado direito (Figura 10 – lado direito).

Representação visual do aplicativo com Fragments em diferentes telas
Figura 10. Representação visual do aplicativo com Fragments em diferentes telas

A ideia básica quando se trabalha com Fragments é imaginar que não exista mais Activities, e sim, Fragments. Claro que um Activity será necessário para controlar o ciclo de vida da aplicação e gerenciar o aplicativo, mas as telas em si serão Fragments.

Estes Fragments podem ser apresentados em telas isoladas, ou ainda, uma tela pode ser formada por mais de um Fragment, e esta disposição pode ser alterada em tempo de execução recuperando se o device está no formato retrato ou paisagem, ou recuperando a resolução da tela, ou ainda qualquer outra característica do dispositivo.

A ideia é que cada Fragment seja um pedaço de tela independente, que pode trabalhar sozinho, porém, ele pode trocar informações com outros Fragments também, assim como acontecia com os Activities.

Outra característica dos Fragments é que o mesmo possui um ciclo de vida específico, diferente do ciclo de vida da Activity (ver Figura 11).

Comparação entre o ciclo de vida de uma Activity e o ciclo de vida de um Fragment
Figura 11. Comparação entre o ciclo de vida de uma Activity e o ciclo de vida de um Fragment

Dados os conceitos básicos de Fragments, vamos ao desenvolvimento de um aplicativo que faz deste recurso. Para isso, crie um projeto Android nomeado ComFragment, similar ao projeto SemFragment desenvolvido anteriormente (mesma nomenclatura de Activity, versão do Android, etc. – ver Figura 5).

Após criar o projeto, também adicione na pasta drawable as imagens que serão apresentadas – ver Figura 6, assim como o arquivo list_elements.xml à pasta values – ver Listagem 1.

O próximo passo, naturalmente, é o desenvolvimento da interface gráfica – o arquivo de layout.xml. Entretanto, agora não se tratará de telas chamadas por Activities, na verdade parcialmente.

O aplicativo terá um Activity principal, que por sua vez terá uma tela principal, porém esta tela terá no seu interior dois “componentes” Fragments, um para o lado esquerdo e outro para o lado direito da tela.

Desta forma, estamos formatando o layout considerando que o device estará no formato paisagem, porém, via código podemos reorganizar estes Fragments.

Outra consideração importante sobre Fragments é que cada “componente” Fragment na interface visual (arquivo XML) deve possuir obrigatoriamente uma classe Java, está sendo uma subclasse de Fragment, para fazer o tratamento dos eventos e realizar a lógica de negócio.

Assim, primeiramente vamos modificar o arquivo activity_main.xml, conforme a Listagem 6.


   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="horizontal" >
    
       <fragment
           android:layout_weight="1"
          android:layout_width="0dp"
          android:layout_height="match_parent" 
           class="br.edu.utfpr.comfragments.LeftFragment"
           android:id="@+id/leftFragment"
          />
       
       <fragment
                android:layout_weight="1"
          android:layout_width="0dp"
          android:layout_height="match_parent"         
           class="br.edu.utfpr.comfragments.RightFragment"
           android:id="@+id/rightFragment"
          />
    
   </LinearLayout>
Listagem 6. activity_main.xml – Interface gráfica principal do aplicativo com Fragments

O layout desenvolvido é baseado no layout LinearLayout (linha 1), este organizado na horizontal (onde os componentes ficam um ao lado do outro) – linha 5.

Na sequência dois fragments dividirão a tela, o primeiro (linha 07) tem o nome de leftFragment (linha 11) e está associado à classe com o mesmo nome (linha 12). O segundo Fragment (linha 15) possui nome de rightFragment (linha 20), sendo também mapeado para uma classe de mesmo nome (linha 19). O próximo passo é criar um conjunto de arquivos .java/.xml para cada um dos Fragments do nosso aplicativo.

Para simplificar, pode ser utilizada a sequência de criação de um Activity, o qual já cria um .java/.xml, apagando sua referência no manifest.xml (se deixar a referência, também não dará problema, é interessante tirar por questão de organização).

Outra opção é criar individualmente um arquivo .java e um arquivo .xml, armazenando o primeiro na pasta src (respeitando os caminhos informados nas linhas 11 e 19) e o segundo na pasta layout.

Os arquivos podem possuir os nomes fragment_left.xml/LeftFragment.java e fragment_right.xml/RigthFragment.java.

A seguir, a Listagem 7 apresenta a interface do fragment_left.xml.


 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
  
     <ListView
         android:id="@+id/lvAnimais"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:entries="@array/animais_sul" />
  
 </RelativeLayout>
Listagem 7. fragment_left.xml – Interface da parte esquerda da tela (lista)

O Layout é idêntico ao activity_main.xmlListagem 2 – desenvolvido no primeiro exemplo, a diferença é que agora ele estará associado a um Fragment. Já a Listagem 8 é o fragment_right.xml, que possui o componente de imagem e de texto, este código é similar ao activity_image.xmlListagem 4.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <ImageView
        android:id="@+id/imAnimal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/curucaca"
         />
    
    <TextView
       android:id="@+id/tvDescricao"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="#FFF"
        />    
 
</RelativeLayout>
Listagem 8. fragment_right.xml – Interface da parte direita da tela (detalhe com imagem e texto)

Agora que a interface gráfica do aplicativo foi criada (activity_main.xml) e a interface dos dois Fragments (fragment_left.xml e fragment_right.xml), é hora de codificar as classes Java. Iniciamos pelo MainActivity.java, conforme a Listagem 9.


 package br.edu.utfpr.comfragmentultimo;
  
 import android.os.Bundle;
 import android.app.Activity;
  
 public class MainActivity extends Activity {
  
        @Override
        protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
        }
  
 }
Listagem 9. MainActivity.java – Activity principal do aplicativo ComFragment

O código apresentado não possui peculiaridades, somente ao executar a aplicação, apresentará na tela a interface activity_main.xml (linha 11). Entretanto, ao apresentar esta interface, a mesma é formada por dois Fragments, assim, existe a necessidade da codificação das classes que a tratarão, iniciando pela classe LeftFragment.java – Listagem 10.


  package br.edu.utfpr.comfragment;
   
  import android.app.Fragment;
  import android.app.FragmentManager;
  import android.app.FragmentTransaction;
  import android.os.Bundle;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.AdapterView;
  import android.widget.ListView;
   
  public class LeftFragment extends Fragment {
   
         private ListView lvAnimais;
         
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container, 
           Bundle savedIntanceState) {
               
               View view = inflater.inflate( R.layout.left_fragment, null );
               
               lvAnimais = (ListView) view.findViewById( R.id.lvAnimais );
               
               lvAnimais.setOnItemClickListener( new AdapterView.OnItemClickListener() {
   
                      @Override
                      public void onItemClick(AdapterView<?> adapter, View v, int position,
                                   long id) {
                             
                             tratarSelecao( position );
                             
                      }
               
               } );
               
               return view;
         }//fim do método onCreateView
   
         protected void tratarSelecao( int position ) {
               String descricao = lvAnimais.getItemAtPosition( position ).toString();
         
               FragmentManager fm = getFragmentManager();
               
               RightFragment rf = (RightFragment) fm.findFragmentById( R.id.rightFragment );
               
               rf.setConteudo( position, descricao );        
         } //fim do método tratarSelecao
         
  } //fim da classe LeftFragment
Listagem 10. LeftFragment.java – Código referente ao Fragment com a lista (esquerda da tela)

Este é o primeiro momento do artigo em que uma classe de Fragment é codificada, então será apresentado com um pouco mais de detalhes cada característica.

As primeiras linhas correspondem às declarações de pacote e importações de classes. Na linha 13 acontece a primeira diferença se comparado aos tradicionais programas Android, o extends é de android.app.Fragment, e sendo assim, a classe deve tratar um ciclo de vida diferente do Activity tradicional, conforme apresentado na Figura 11. Sendo assim, o tradicional método onCreate() do Activity foi substituído pelo onCreateView() – linha 18.

Mesmo assim, todos os componentes visuais da tela devem ser declarados (neste acaso, apenas um ListView – linha 15), e a recuperação dos componentes não se dá através do findViewById() da classe Activity, e sim por um objeto View (instanciado na linha 20) criado a partir do comando inflate aplicado ao layout XML correspondente ao Fragment. Assim, o comando FindViewById foi executado na linha 22.

Feito isso, a linha 24 trata o evento de clique na lista chamando o método tratarSeleção() – linha 30.

O método tratarSeleção() recupera a descrição do item selecionado na lista, sendo na sequência necessário instanciar um objeto do tipo FragmentManager (linha 42), o que permite recuperar uma instância de outro Fragment, neste caso, o RightFragment (linha 44), executando com isso o método setConteudo() – linha 46. Este é responsável por apresentar a imagem e a descrição na tela de detalhe.

Agora vamos ao desenvolvimento da última classe do aplicativo – RightFragment.java – Listagem 11.


 package br.edu.utfpr.comfragmentultimo;
  
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
  
 public class RightFragment extends Fragment {
  
        private int imagens_id[] = { R.drawable.curucaca,
                     R.drawable.gralhaazul,
                     R.drawable.graxaim,
                     R.drawable.ourico,
                     R.drawable.queroquero };
        
        private ImageView imAnimal;
        private TextView tvDescricao;
        
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedIntanceState) {
              
              View view = inflater.inflate( R.layout.fragment_rigth, null );
              
              imAnimal = (ImageView) view.findViewById( R.id.imAnimal );
              tvDescricao = (TextView) view.findViewById( R.id.tvDescricao );
              
              imAnimal.setScaleType(ImageView.ScaleType.FIT_XY);
              
              return view;
        }//fim do método onCreateView
        
        public void setConteudo( int pos, String descricao ) {
              imAnimal.setImageResource( imagens_id[ pos ] );
              tvDescricao.setText( descricao );
        }//fim do método setConteudo
        
 }//fim da classe RightFragment
Listagem 11. RightFragment.java – Código referente ao Fragment de detalhe (direita da tela)

A classe possui a declaração do pacote e importação das classes utilizadas (linhas 1 a 12), assim como a declaração da classe que herda funcionalidades de Fragment (linha 14).

Na linha 16 é declarado o array com os ids das imagens que serão apresentadas, assim como declarados os dois componentes visuais nas linhas 22 e 23.

No método onCreateView() – linha 26, é recuperada a interface gráfica (linha 28), assim como os componentes visuais (linhas 30 e 31), sendo também modificada a propriedade da componente imagem para ocupar a tela toda (linha 33). Por fim, o método setConteudo() – linha 38 é chamado pelo LeftFragment (linha 46 da Listagem 10).

Com isso o aplicativo está pronto para ser executado em formato paisagem, ocupando a maior parte da tela, como pode ser observado na Figura 12.

Tela do aplicativo executando no formato paisagem
Figura 12. Tela do aplicativo executando no formato paisagem

Porém, ao executar no formato retrato, o aplicativo divide a tela, que já fica estreita, em duas, o que compromete a funcionalidade do aplicativo, conforme observado na Figura 13.

Tela do aplicativo executando no formato retrato
Figura 13. Tela do aplicativo executando no formato retrato

Para resolver este problema, precisamos montar a tela de acordo com os diferentes formatos: retrato ou paisagem. Assim, a primeira mudança deve ser realizada na classe MainActivity.java, chamada inicialmente quando o aplicativo é executado, verificando se o dispositivo está em paisagem (os dois Fragments na mesma tela) ou retrato (um Fragment por tela). A Listagem 12 apresenta o código da classe MainActivity modificado.


  package br.edu.utfpr.comfragmentultimo;
   
  import android.os.Bundle;
  import android.app.Activity;
  import android.app.FragmentManager;
  import android.app.FragmentTransaction;
  import android.content.res.Configuration;
   
  public class MainActivity extends Activity {
   
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
               
             Configuration config = getResources().getConfiguration();
   
             if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                  setContentView( R.layout.activity_main );
             }else{
                  LeftFragment lf = new LeftFragment();
                  
                  FragmentManager fragmentManager = getFragmentManager();
                     
                  FragmentTransaction fragmentTransaction = 
                             fragmentManager.beginTransaction();              
                  
                  fragmentTransaction.add(android.R.id.content, lf);
                  
                  fragmentTransaction.commit();
             }
               
         } //fim do método onCreate
   
  }
Listagem 12. Método tratarSelecao() da classe LeftFragment.java – Código para tratar modo retrato ou paisagem

A mudança no código acontece a partir da linha 15, onde é recuperado um objeto do tipo Configuration, o qual informa se a tela está em retrato ou paisagem. Este teste é realizado na linha 17, e se estiver em formato paisagem, a interface main_activity.xml (linha 18) é apresentada, caso contrário, é instanciado um objeto de LeftFragment (linha 20), sendo este o único Fragment que será apresentado.

Na linha 22 uma instância de FragmentManager é criada, e este utilizado para apresentar na tela o conteúdo do LeftFragment (linha 27). Por fim, a transação é finalizada com um Commit (linha 29).

A próxima mudança acontece na class LeftFragment, em especial no método tratarSelecao(), o qual deve contar o código presente na Listagem 13.


 protected void tratarSelecao( int position ) {
       String descricao = lvAnimais.getItemAtPosition( position ).toString();
       
       Configuration config = getResources().getConfiguration();
       
       if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
       
              FragmentManager fm = getFragmentManager();
              
              FragDir fragDir = (FragDir) fm.findFragmentById( R.id.fragDir );
              
              fragDir.setConteudo( position, descricao );
       
       } else {
              FragDir fragDir = new FragDir();
              
              Bundle args = new Bundle();
              args.putInt("pos", position);
              args.putString( "descricao", descricao );
              fragDir.setArguments(args);
              
              FragmentManager fragmentManager = getFragmentManager();
              FragmentTransaction fragmentTransaction = 
                    fragmentManager.beginTransaction();
           
              fragmentTransaction.add(android.R.id.content, fragDir);
              fragmentTransaction.addToBackStack(null);
              fragmentTransaction.commit();
         
       }
 } //fim do método tratarSelecao
Listagem 13. Método tratarSelecao() da classe LeftFragment.java – Código para tratar modo retrato ou paisagem

O método tratarSeleção() recebe por parâmetro o id do elemento selecionado na lista, recuperando também o texto do item (linha 2). Estas informações estão prontas para serem enviadas para o segundo Fragment (RightFragment.java), sendo elas apresentadas na tela semelhante ao que acontecia com os Activities no projeto SemFragment.

Assim, é necessário saber se o device está em modo retrato ou paisagem, para isso é necessário instanciar um objeto do tipo Configuration (linha 04), sendo que através da propriedade orientation (teste da linha 06) é possível identificar se o device está em modo retrato ou paisagem. Se estiver em modo paisagem, permanece a lógica apresentada na Listagem 10.

Se o layout estiver no formato retrato, o novo Fragment deve ser apresentado em uma tela nova, assim, para passar parâmetros para o segundo Fragment, é necessário instanciar um objeto de Bundle (linha 17), adicionando a estes os dois campos (linha 18 e 19). Este Bundle é adicionado à instância de RightFragment na linha 20, esta instância foi criada anteriormente na linha 15.

Para realizar a chamada ao novo Fragment, fazendo com que seu conteúdo seja adicionado a tela, deve-se recuperar o FragmentManager da classe (linha 22), iniciando na sequência uma transação (linha 23).

Esta adicionará na tela a instância de RightFragment (linha 24) informando que ao se clicar no botão Back, o controle deve retornar ao Fragment chamador (linha 27). Por fim a transação é finalizada através do comando Commit (linha 28).

Seguindo o desenvolvimento, a classe RightFragment também deve ser alterada, conforme apresenta a Listagem 14.


protected void tratarSelecao( int position ) {
      String descricao = lvAnimais.getItemAtPosition( position ).toString();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedIntanceState) {
      
      View view = inflater.inflate( R.layout.frag_dir, null );
      
      imAnimal = (ImageView) view.findViewById( R.id.imAnimal );
      tvDescricao = (TextView) view.findViewById( R.id.tvDescricao );
      
      imAnimal.setScaleType(ImageView.ScaleType.FIT_XY);
      
      Bundle b = this.getArguments();
      
      if ( b != null ) {
             int pos = b.getInt( "pos" );
             String descricao = b.getString( "descricao" );
             setConteudo( pos, descricao );
      }
      
      return view;
}//fim do método onCreateView
Listagem 14. Método onCreateView() da classe RightFragment.java – Código para tratar modo retrato ou paisagem

O código acima é similar ao código apresentado na Listagem 11, diferenciando apenas na recuperação dos dados enviados por parâmetro, onde o mesmo é recuperado na linha 12, sendo que a linha 14 identifica se este objeto é diferente de null, e caso positivo, recupera as informações enviadas e executa o método setConteudo().

Assim, é só executar o aplicativo novamente, e será verificado que a mudança aconteceu, utilizando ao máximo o espaço disponível na tela do device. Caso a tela esteja em paisagem, mais componentes serão apresentados conforme a Figura 14.

Aplicativo pronto com Fragments
Figura 14. Aplicativo pronto com Fragments

Entendendo o porquê de esta ser a melhor solução

No aplicativo desenvolvido utilizando Fragments, temos um novo modelo de desenvolvimento, onde cada pedaço da tela (Fragment) é considerado um pequeno aplicativo, contendo inclusive um ciclo de vida próprio permitindo que estes pedaços sejam usados de diferentes formas (em diferentes locais, em diferentes formatos).

Sem o uso de Fragment, para o exemplo apresentado, ou o usuário desenvolveria dois aplicativos específicos, ou um aplicativo “genérico”, usando flags de controle e layouts diferentes para cada uma das situações.

Em aplicativos simples, com poucas informações em poucas telas, talvez não valha a pena desenvolver usando Fragments, já que isso aumenta a complexidade de desenvolvimento. Entretanto, em aplicativos com muitas informações e que serão usadas nos mais diferentes tipos de devices (tablets, celulares e smartphones), seu uso é praticamente inevitável.

Confira também