Os Fragments são fragmentos de código que tem um ciclo de vida muito parecido com o da Activity. Sua criação foi necessária, pois como as telas dos tablets são maiores que as dos celulares, uma melhor gestão de espaço era necessária.

Para melhor entender o funcionamento de um Fragment precisamos conhecer o seu ciclo de vida que irá ditar o seu comportamento.

Nesse artigo veremos como a informação é passada e como o outro Fragment a recebe usando o Android Studio.

Basicamente, para dois fragments se comunicarem, é preciso fazer uma referência do Fragment A para o Fragment B e está tudo resolvido. Contudo, como uma das boas práticas é o reaproveitamento de Fragment, isso passa a não funcionar, pois ele estará com um alto acoplamento e sua reusabilidade seria muito baixa. A ideia principal do Fragment é a sua possível reutilização, em outra Activity por exemplo.

Esse problema de comunicação pode ser resolvido utilizando um Event Carrier, que é uma Interface que contém um método qualquer que será o responsável por carregar o evento de um Fragment para o outro passando pela Activity. Observem a Listagem 1 para melhor esclarecimento.


public interface Comunicador{
  public void evento(String dado);
}

public class MainActivity extends Activity implements Comunicador{
  //Implementação obrigatória do método evento
  public void evento(String dado){
      //Código que ira definir o método
  }
}

public FragmentA extends Fragment{
    Comunicador comunicador;

    public void onAttach(Activity activity){
          comunicador = (Comunicador)activity;
    }
Listagem 1. Comunicação entre fragments

Reparem que o Fragment A tem acesso através do atributo comunicador ao método implementado na MainActivity. Pode-se reparar que o método onAttach, presente no ciclo de vida do Fragment, foi utilizado por receber do sistema operacional uma referência da Activity, e como esta implementa a interface Comunicador, não existe problema nenhum em fazer um cast para atribui-lo ao objeto.

Existem outros padrões para fazer essa mesma tarefa, como, por exemplo, a interface Communicator poderia ser colocada dentro da classe Fragment A, garantindo que a interface não seria exposta mais do que isso deveria. Porém, esse foi escolhido por sua facilidade no entendimento dos passos.

Para vermos como funciona a comunicação primeiramente serão criados os arquivos de layout tanto dos dois Fragments quanto o da Activity. Notem que serão colocadas cores bem diferentes e evidentes, para que seja possível a visualização de cada elemento na tela. Os nomes dos arquivos são: activity_main.xml, fragment_a_layout.xml e fragment_b_layout.xml.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0000FF">
 
    <fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:name="br.com.home.fragmentflexibleui.FragmentA"
        android:id="@+id/fragment" />
 
    <fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:name="br.com.home.fragmentflexibleui.FragmentB"
        android:id="@+id/fragment2" />
</LinearLayout>
Listagem 2. Código do Layout da Activity

O código da Listagem 2 define a Activity e dentro dela são colocadas os dois Fragments. Notem que essa é uma inserção estática, ou seja esses Fragments não podem ser substituídos em tempo de execução. A cor de fundo dessa Activity será um Azul Escuro.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:background="#FFCC00">
 
    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true" />
</RelativeLayout>
Listagem 3. Código do layout do Fragment A

A Listagem 3 mostra o código do Fragment A, que implementa um ListView onde podemos selecionar valores. A cor de fundo desse Fragment é laranja.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:background="#00FF00">
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Large Text"
        android:id="@+id/textView"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>
Listagem 4. Código do layout do Fragment B

O código presente na Listagem 4 define na tela apenas um Text View de tamanho grande para exibir as informações selecionadas no Fragment A. A cor de fundo desse Fragment é verde.

O que será feito agora é popular a massa de dados a ser exibida. No Android Studio, dentro do diretório res->values, abra o arquivo string.xml e coloque a massa de dados que será acessada pelo aplicativo. Esse arquivo já deve estar populado com algumas informações. Entre as tags e insira o conteúdo da Listagem 5.


<string-array name="titulos">
        <item>Gandalf</item>
        <item>Aragorn</item>
        <item>Legolas</item>
        <item>Gimli</item>
        <item>Arwen</item>
    </string-array>
 
    <string-array name="descrip">
        <item>Gandalf, por vezes Gandalf, o Cinzento ou Gandalf, o Branco é 
        um personagem fictício das obras do autor e filólogo britânico 
        J. R. R. Tolkien</item>
        <item>Aragorn II, filho de Arathorn é um personagem fictício criado pelo 
        professor e filólogo britânico J. R. R. Tolkien em sua obra O Senhor dos 
        Anéis, onde é um dos protagonistas.</item>
        <item>Legolas Greenleaf, dentro do universo de fantasia criado pelo 
        escritor J.R.R. Tolkien, foi um elfo Sindar, filho de Thranduil, rei da 
        Floresta das Trevas. Em 3019 da Terceira Era, Legolas foi a Valfenda 
        como mensageiro dos elfos de sua terra.</item>
        <item>Gimli é um dos mais importantes personagens criados por 
        J.R.R.Tolkien para a trilogia O Senhor dos Anéis. Era um anão do povo de 
        Durin que se habilitou para acompanhar Frodo Bolseiro na Sociedade do Anel, 
        que tinha por intuito destruir o Um Anel.</item>
<item>Arwen Undómiel é uma personagem fictícia nas obras de J. R. R. Tolkien. 
Ela aparece em seu romance, O Senhor dos Anéis, normalmente publicado em três 
volumes. Arwen é uma dos Meio-Elfos que viveram durante a Terceira Era. Era 
extremamente parecida com Lúthien Tinúviel, de quem era parente, e conheceu 
Aragorn na floresta de Rivendell enquanto caminhava. </item>   
</string-array>
Listagem 5. Massa de dados

Agora o código Java que faz toda a mágica acontecer será implementado: o comunicador, a activity e os fragments serão definidos com uma pequena diferença, pois aqui o parâmetro será um inteiro e não uma String, como demonstrado na Listagem 1, já que estamos com o índice do vetor que foi definido no arquivo strings.xml.


package br.com.home.fragmentflexibleui;
 
/**
 * Created by allanromanato on 9/24/15.
 */
public interface Communicator {
    public void responde(int id);
}
Listagem 6. Código da interface Comunicator

A Listagem 6 é a interface do Event Carrier e será implementada na Activity.


package br.com.home.fragmentflexibleui;
 
import android.app.Activity;
import android.app.FragmentManager;
import android.os.Bundle;
 
/**
 * Created by allanromanato on 9/24/15.
 */
 
public class MainActivity extends Activity implements Communicator{
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    @Override
    public void responde(int data) {
        FragmentManager manager = getFragmentManager();
        FragmentB frag = (FragmentB)manager.findFragmentById(R.id.fragment2);
        frag.change(data);
    }
}
Listagem 7. Código da Activity

A Listagem 7 mostra o código da Activity, onde o onCreate é chamado e o layout é anexado à Activity, como acontece normalmente nas aplicações Android.


@Override
public void responde(int data) {
    FragmentManager manager = getFragmentManager();
    FragmentB frag = (FragmentB)manager.findFragmentById(R.id.fragment2);
    frag.change(data);
}
Listagem 8. Método responde

Na Listagem 8 temos a assinatura do método responde contida dentro da interface. Sua implementação requer que um objeto do tipo FragmentManager seja definido e que receba o retorno do chamado do método getFragmentManager(), que é o responsável por gerenciar os Fragments.

No código FragmentB frag = (FragmentB)manager.findFragmentById(R.id.fragment2); é instanciado um objeto do tipo FragmentB que receberá o retorno do método findFragmentById, onde é passado o id do Fragment em questão (no caso FragmentB).

O cast é importante nesse caso, portanto não pode ser esquecido. Por fim, o método change é chamado passando como parâmetro o dado, mas só explicaremos mais sobre quando for implementado o código do FragmentB.


package br.com.home.fragmentflexibleui;
 
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
 
/**
 * Created by allanromanato on 9/24/15.
 */
public class FragmentA extends Fragment implements AdapterView.OnItemClickListener{
    private ListView list;
    private Communicator comm;
 
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        comm = (Communicator)activity;
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, 
    ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_a_layout, container, false);
    }
 
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
 
        list = (ListView) getActivity().findViewById(R.id.listView);
       ArrayAdapter adapter = ArrayAdapter.createFromResource(
       getActivity(),R.array.titulos,android.R.layout.simple_list_item_1);
        list.setAdapter(adapter);
        list.setOnItemClickListener(this);
    }
 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        comm.responde(position);
    }
}
Listagem 9. Código do FragmentA

A Listagem 9 representa o código do Fragment A responsável por exibir a lista na tela e enviar os dados para o Fragment B.

Notem que o Comunicator é definido no início da classe como um atributo privado, pois será utilizado apenas dentro dessa classe.


public void onAttach(Activity activity) {
    super.onAttach(activity);
    comm = (Communicator)activity;
}
Listagem 10. Método onAttach

Na Listagem 10 pode-se notar que o objeto do tipo Communicator recebe a Activity em que o Fragment está contido, isso pode ser feito apenas porque a Activity implementa a interface Communicator. Aqui nosso Fragment já tem a referência que precisa para passar a informação, mas lembre-se de não executar nenhuma operação de UI dentro desse método, pois não é garantido que a UI esteja pronta. Para mais informações consulte o artigo de Ciclo de Vida dos Fragments.


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, 
ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_a_layout, container, false);
}
Listagem 11. Método onCreateView

Na Listagem 11 tem-se o método onCreateView responsável por anexar o layout XML ao Fragment utilizando o método inflate, pois ele irá “transformar"um layout em um objeto Java do tipo View.

Quando é criada a view “public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {“, o retorno é um tipo view, portanto é necessário fazer um inflate, que é um método contido dentro da classe LayoutInflater, responsável por “inflar” o XML de layout. Em outras palavras, ele transforma o XML em um objeto do tipo View. É um processo custoso, pois ele pega o XML e cria um objeto Java desse XML, setando todos os atributos, e recursivamente faz isso para todos as tags filhas que estão no XML.


@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    list = (ListView) getActivity().findViewById(R.id.listView);
   ArrayAdapter adapter = ArrayAdapter.createFromResource(
   getActivity(),R.array.titulos,android.R.layout.simple_list_item_1);
    list.setAdapter(adapter);
    list.setOnItemClickListener(this);
}
Listagem 12. Método onActivityCreated

E recomendado que as operações de UI do Fragment sejam executadas a partir do método onActivityCreated da Listagem 12, pois ele garante que a Activity já foi completamente criada. Aqui será colocado o Array de título criado anteriormente no arquivo strings.xml.

Dentro de um objeto do tipo ListView deve-se anexar o elemento que representa a lista no XML de layout do Fragment, para isso é utilizado o método findViewById passando a referência desse elemento. Assim, um objeto do tipo ArrayAdapter deve ser criado.

Como o Array foi definido na massa de dados dentro do arquivo strings.xml, deve-se carregar os dados que estão lá dentro usando o método estático createFromResource, passando três parâmetros: primeiro a Activity, segundo o Array que está no arquivo, e terceiro o layout da lista que será exibida.

Agora o que será feito é atrelar o Array de títulos com um layout básico do Android que mostra uma lista simples.

Por fim, o que será feito é ligar o ArrayAdapter com o objeto ListView, para que possa ser mostrado na tela oconteúdo do array.

É possível também criar um adapter customizado, onde os dados são trazidos do banco de dados SQLite, mas como não é o foco desse artigo, consulte o artigo Adapter Customizado do Android para inserção de imagens no SQLite.


@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    comm.responde(position);
}
Listagem 13. Método onItemClick

Na Listagem 13 o listener identificará a posição que foi clicada e a passará como parâmetro no método responde. Lembrem-se que esse método é definido na Activity. Com isso a classe FragmentA está completa.


package br.com.home.fragmentflexibleui;

import android.app.Activity;
import android.app.Fragment;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
* Created by allanromanato on 9/24/15.
*/
public class FragmentB extends Fragment {
private TextView text;
private Activity activity;

public void onAttach(Activity activity){
  super.onAttach(activity);
  this.activity = activity;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  return inflater.inflate(R.layout.fragment_b_layout, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);
  text = (TextView) activity.findViewById(R.id.textView);
}

public void change(int data){
  Resources res = getResources();
  String[] desc = res.getStringArray(R.array.descrip);
  text.setText(desc[data]);
}
Listagem 14. Código do FragmentB

Na Listagem 14 pode-se notar que o FragmentB tem alguns métodos bem parecidos com o FragmentA: o onAttach copiará uma referência para a Activity que será utilizada no método onActivityCreated e o método onCreateView faz a mesma coisa que no FragmentA.

O método onActivityCreate tem também a mesma função do FragmentA, com a exceção de aqui será feito o link entre um objeto TextView, responsável por exibir os elementos na tela. Notem que, por motivos didáticos, aqui foi feita a referência ao objeto do tipo Activity instanciado anteriormente no onAttach para simplificar. O método onAttach poderia ser deletado e o objeto do tipo Activity poderia ser substituído pelo método getActivity().


public void change(int data){
  Resources res = getResources();
  String[] desc = res.getStringArray(R.array.descrip);
  text.setText(desc[data]);
}
Listagem 15. Método change

A Listagem 15 mostra o método responsável por exibir na tela o conteúdo do array cujo o índice foi enviado pelo FragmentA. Aqui novamente os resources são carregados pelo método getResources(), o Array de Strings contendo a massa de dados referente a descrição é armazenado em um objeto do tipo Array de String e, por fim, o objeto do tipo TextView exibe o dado na tela referente a posição do Array enviado pelo FragmentA.

As Figuras 1 e 2 mostram o resultado, que variam de acordo com o item selecionado na lista.

Resultado final 1
Figura 1. Resultado final 1
Resultado final 2
Figura 2. Resultado final 2

Esperam que tenham gostado. Até a próxima oportunidade.