Introduzidos a partir do Android 3.0, os Fragments são componentes esssenciais para a construção de interfaces de usuário dinâmicas e flexíveis. Um Fragment pode ser visto como uma "sub-Activity" ou um "pedaço" da interface do usuário que reside dentro de uma Activity. É crucial entender que um Fragment não pode existir de forma independente; ele deve sempre estar associado a uma Activity.
Por Que Utilizar Fragments?
O ecossistema Android abrange uma vasta gama de dispositivos, desde smartphones com telas menores até tablets, TVs e dispositivos dobráveis com telas muito maiores. Um layout de interface do usuário projetado para um smartphone pode parecer esticado ou inadequado em um tablet. Os Fragments foram criados para resolver esse desafio, permitindo que os desenvolvedores criem aplicativos que se adaptam elegantemente a diferentes tamanhos e orientações de tela, proporcionando uma experiência de usuário otimizada em múltiplos dispositivos a partir de uma única base de código.
Principais Características dos Fragments
- São módulos de interface do usuário que podem ser incorporados em uma Activity, contribuindo para a construção de layouts complexos e dinâmicos.
- Promovem a modularidade, permitindo que a lógica de UI e a manipulação de eventos sejam encapsuladas em unidades separadas, facilitando a organização do código.
- Fomentam a reutilização: um mesmo Fragment pode ser utilizado em diversas Activities, ou múltiplas vezes na mesma Activity, em diferentes contextos.
- Possibilitma a criação de layouts adaptativos, ajustando a apresentação da UI com base no tamanho, orientação ou tipo de dispositivo, essencial para uma experiência multi-dispositivo consistente.
Ciclo de Vida de um Fragment
Assim como as Activities, os Fragments posuem um ciclo de vida que reflete os estados pelos quais passam. Entender este ciclo é fundamental para gerenciar corretamente os recursos e a interação da interface. Aqui estão os principais métodos de callback:
onAttach(): Chamado quando o Fragment é associado à sua Activity. É o primeiro método a ser invocado. Neste ponto, você pode obter uma referência à Activity ou argumentos passados.onCreate(): Invocado quando o Fragment está sendo criado. Use este método para inicializar componentes que o Fragment retém quando pausado ou parado, mas que serão usados mesmo se a UI do Fragment não for criada (e.g., dados não visuais).onCreateView(): Chamado para inflar o layout da interface do usuário do Fragment. Retorna a View raiz para a interface do Fragment.onViewCreated(): Invocado imediatamente apósonCreateView()ter retornado, mas antes que qualquer estado salvo seja restaurado na View. Utilize este método para configurar suas Views.onActivityCreated(): Chamado quando o métodoonCreate()da Activity hospedeira é concluído. Indica que a Activity está totalmente criada e o Fragment pode interagir com ela.onStart(): O Fragment torna-se visível para o usuário.onResume(): O Fragment torna-se ativo e interativo para o usuário.onPause(): O Fragment não está mais em primeiro plano, mas ainda pode ser visível. Geralmente, ocorre quando outra Activity ou Fragment parcial cobre a tela.onStop(): O Fragment não está mais visível para o usuário.onDestroyView(): Chamado quando a View associada ao Fragment é destruída. A instância do Fragment ainda existe, mas sua UI é removida.onDestroy(): Chamado para realizar a limpeza final do estado do Fragment.onDetach(): O Fragment não está mais associado à sua Activity. É o último método do ciclo de vida a ser invocado quando o Fragment está sendo removido.
Estágios do Ciclo de Vida do Fragment:
- Criação: Quando um Fragment é instanciado, a sequência de chamadas é:
onAttach()→onCreate()→onCreateView()→onViewCreated()→onActivityCreated(). - Visibilidade e Interação: Para que o Fragment se torne visível e interativo, os métodos invocados são:
onStart()→onResume(). - Em Segundo Plano: Quando o Fragment entra em segundo plano (mas a Activity ainda está visível), a sequência é:
onPause()→onStop(). - Destruição: Quando o Fragment é destruído ou a Activity que o hospeda é destruída, a sequência de chamadas é:
onPause()→onStop()→onDestroyView()→onDestroy()→onDetach().
Comunicação da Activity para o Fragment
Para passar dados de uma Activity para um Fragment, a abordagem recomendada é utilizar um objeto Bundle. O Bundle é então anexado ao Fragment usando o método setArguments(). O Fragment pode recuperar esses argumentos no seu método onCreate() ou onCreateView().
A prática comum envolve a criação de um método estático de fábrica, como newInstance(), dentro do Fragment para encapsular a criação do Fragment e a passagem de argumentos.
Exemplo de Passagem de Dados (Activity para Fragment):
1. No Fragment (exemplo: DetailContentFragment.java):
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class DetailContentFragment extends Fragment {
private static final String ARG_CONTENT_TEXT = "param_content_text"; // Chave para o Bundle
private String displayedContent;
// Método de fábrica para criar uma nova instância do Fragment com argumentos
public static DetailContentFragment createInstance(String content) {
DetailContentFragment fragment = new DetailContentFragment();
Bundle args = new Bundle();
args.putString(ARG_CONTENT_TEXT, content);
fragment.setArguments(args); // Define os argumentos para o Fragment
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Recupera os argumentos no onCreate do Fragment
if (getArguments() != null) {
displayedContent = getArguments().getString(ARG_CONTENT_TEXT);
} else {
displayedContent = "Nenhum conteúdo fornecido."; // Valor padrão
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail_content, container, false);
TextView contentTextView = rootView.findViewById(R.id.detail_text_view);
contentTextView.setText(displayedContent); // Exibe o conteúdo recebido
return rootView;
}
}
2. Na Activity (para usar o Fragment):
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class HostActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_host);
if (savedInstanceState == null) {
// Cria uma instância do Fragment e passa os dados
DetailContentFragment fragment = DetailContentFragment.createInstance("Conteúdo enviado da Activity!");
// Adiciona o Fragment à Activity
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
}
}
}
3. Layout do Fragment (fragment_detail_content.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/detail_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:padding="16dp"
android:text="Conteúdo do Fragment" />
</LinearLayout>
Neste exemplo, a HostActivity cria uma instância de DetailContentFragment passando uma String, que é então exibida dentro do Fragment.
Comunicação do Fragment para a Activity
Para que um Fragment se comunique de volta com sua Activity hospedeira ou com outros Fragments, o padrão recomendado é usar interfaces de callback. Isso garante um acoplamento fraco e permite que a Activity responda a eventos dentro do Fragment sem que o Fragment precise conhecer detalhes específicos da Activity.
Nota importante: Se você precisar passar objetos complexos via callback, certifique-se de que esses objetos implementem a interface Serializable ou Parcelable para garantir que possam ser devidamente empacotados e desempacotados.
Exemplo de Callback (Fragment para Activity):
1. No Fragment (exemplo: ProductListFragment.java):
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class ProductListFragment extends Fragment {
// 1. Define a interface de callback
public interface OnProductSelectedListener {
void onProductSelected(String productName);
}
// 2. Declara uma instância da interface
private OnProductSelectedListener listener;
private String currentProduct = "Produto Padrão"; // Exemplo de dado no Fragment
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// Garante que a Activity implemente a interface
if (context instanceof OnProductSelectedListener) {
listener = (OnProductSelectedListener) context;
} else {
throw new RuntimeException(context.toString()
+ " deve implementar OnProductSelectedListener");
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_product_list, container, false);
TextView productDisplay = view.findViewById(R.id.product_name_text);
productDisplay.setText(currentProduct);
Button selectButton = view.findViewById(R.id.select_product_button);
selectButton.setOnClickListener(v -> {
// 3. Dispara o callback quando o botão é clicado
if (listener != null) {
listener.onProductSelected(currentProduct + " (selecionado!)");
}
});
return view;
}
@Override
public void onDetach() {
super.onDetach();
listener = null; // Libera a referência para evitar vazamentos de memória
}
}
2. Na Activity Hospedeira (exemplo: MainActivity.java):
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements ProductListFragment.OnProductSelectedListener {
private TextView selectedProductTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
selectedProductTextView = findViewById(R.id.selected_product_display);
if (savedInstanceState == null) {
// Adiciona o ProductListFragment à Activity
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container_callback, new ProductListFragment())
.commit();
}
}
// 4. Implementa o método de callback da interface
@Override
public void onProductSelected(String productName) {
// A Activity recebe o dado do Fragment e pode agir sobre ele
selectedProductTextView.setText("Produto Selecionado: " + productName);
Toast.makeText(this, "Produto recebido: " + productName, Toast.LENGTH_SHORT).show();
}
}
3. Layouts (fragment_product_list.xml e activity_main.xml):
<!-- fragment_product_list.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<TextView
android:id="@+id/product_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nome do Produto"
android:textSize="22sp"
android:textStyle="bold"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/select_product_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selecionar Produto" />
</LinearLayout>
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<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="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Status da Activity:"
android:textSize="18sp"
android:padding="8dp"/>
<TextView
android:id="@+id/selected_product_display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Nenhum produto selecionado"
android:textSize="16sp"
android:textStyle="italic"
android:paddingLeft="8dp"
android:paddingBottom="16dp"/>
<FrameLayout
android:id="@+id/fragment_container_callback"
android:layout_width="match_height"
android:layout_height="match_parent" />
</LinearLayout>
Nesse cenário, quando o botão no ProductListFragment é clicado, o método onProductSelected() na MainActivity é invocado, permitindo que a Activity reaja ao evento do Fragment.
Breve Introdução ao ViewPager
O ViewPager é um componente de layout no Android que permite aos usuários deslizar entre várias páginas (Views) ou Fragments de forma horizontal. É comumente utilizado para apresentar coleções de dados onde cada item pode ser visualizado em uma página separada, como em galerias de imagens, tutoriais de aplicativos ou abas de navegação. Ele é frequentemente usado em conjunto com PagerAdapter ou FragmentStatePagerAdapter/FragmentPagerAdapter para gerenciar o conteúdo das páginas.