Android/El fragment com a element bàsic d'una interfície
Introducció als fragments
modificaUn Fragment[1] és un "contenidor" o mòdul que ocupa una fracció de la Activity. Tenen el seu propi cicle de vida lligat a la activitat a la que pertanyen.
- Link al repositori: Pàgina principal d'Android, Fragments, Activity
- Anotacions: No
- Vegeu també: No
//Provat en les versions 6.0 d'Android
import android.support.v4.app.Fragment;
public class Fragment
extends Object implements ComponentCallbacks2, View.OnCreateContextMenuListener
Els fragments es van afegir a partir de la API 11[2]. La filosofia dels fragments neix de la necessitat de suportar dissenys més dinàmics i flexibles en pantalles més llargues com les de les tablets. Es recomana dissenyar cada fragment el màxim de modular possible per poder reaprofitar-ne el mòdul en una altra activitat.
Al dividir l'activitat en fragments, et permet modificar l'aparença de l'activitat de forma dinàmica, per exemple en funció de la mida de la pantalla, tot i mantenit-ne les funcionalitats.
Per exemple, una aplicació implementa dos fragments. El primer és una llista de items, el segon mostra la informació de l'item seleccionat. Per exemple, una llista de noticies i la noticia en sí: la idea és poder veure a la esquerra la llista i a la dreta la noticia oberta. En una pantalla gran, ens caben els dos perfectament en una mateixa activitat. En una pantalla normal, com la d'un telèfon mòbil, no ens hi cap tot alhora, però podem tenir en una activitat la llista i en una altra activitat la noticia seleccionada. Gràcies als fragments podem usar el mateix codi per a ambdues situacions.
Una segona funció que tenen els fragments gràcies a que poden implementar funcions pròpies, és la d'implementar widgets.
Fragments VS Activities
modificaMentre que una activitat és estàtica, en el sentit de que està lligada a l'aplicació de la qual la hem creada, un fragment no te cap lligam. Un fragment incorpora els seus propis mètodes públics estàndar per poder cridar-lo des de qualsevol activitat. Això permet entre altres cosses, aglutinar una serie de funcionalitats en un contenidor reaprofitable.
Usos dels Fragments
modificaActualment, els fragments tenen molts usos. Alguns d'aquests, no tenen res a veure amb la concepció del propi fragment, però l'ampli ús d'aquests en tutorials i plantilles de Google, fa que aparegin moltes funcionalitats lligats a aquestos.
Alguns exemples dels usos que li podem donar a un fragment son els següents.
Reposicionament dinàmic
modificaUn dels usos principals dels fragments es permetre la redistribució de la UI depenent de la grandària de la pantalla. Al fer aquesta redistribució amb fragments el que estem fent és modificar els Layouts segons la grandària de la pantalla, semblant a com funcionen les pagines web, de manera que extraiem tota aquesta complexitat del codi.
Tabbed Activity
modificaAquesta versió d'Activity amb fragments afegeix a la UI la possibilitat de canviar les pantalles amb un swipe lateral "desplaçament amb el dit" i que es visualitzi la transitorietat entre les dues views. Sense els fragments l'única forma possible que teníem de canviar entre les diferents pantalles era usar la barra superior amb els diferents tabs.
Una alternativa als fragments per a fer el desplaçament lateral de la vista (Swipe Lateral) el podem trobar en versions antigues d'Android en la galeria de fotos. Aquesta utilitzava el component ViewPager per a fer això.
Tasques en background
modificaEl fragment es pot utilitzar per tal d'executar codi en background o guardar certa informació de l'activitat, per fer això simplement creem un fragment i no li assignem cap Layout: en el mètode OnCreateView, retornem un null. Això ja està previst per al framework que tractarà el fragment de forma similar a un thread fill de la activitat que l'ha creat, però amb l'excepció que en destruir l'activitat el fragment quedarà guardat a la pila i en tornar a crear l'activitat el podrem recuperar.
Contingut d'un Fragment
modificaLa biblioteca de suport
modificaLa principal funció de biblioteca suport, és aportar nous elements a la UI que no estan disponibles en el framework Android. Aquests nous elements compatibilitat enrere, és a dir si usem un nou element de la api21, aquest es podrà visualitzar mitjançant la biblioteca suport en un mòbil que tingui una api inferior, api15 per exemple. Tot i això la visualització d'aquests elements no serà perfecta perquè en les apis anteriors poden mancar certs components essencials i cal tenir controlat com es visualitza la nostra app en totes les versions suportades al usar aquesta llibreria.
Per tal d'usar aquesta biblioteca primer de tot necesitem que l'activitat sigui del tipus "AppCompatActivity" que es l'activity especifica per usar la llibreria de suport.
FragmentManager
modificaÉs l'objecte principal Android per tal d'interaccionar amb qualsevol fragment. El necessitem per operar amb els fragments a traves del Fragment Transaction.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
getFragmentManager().beginTransaction().commit();
FragmentTransaction
modificaAquest objecte android s'usa conjuntament amb el FragmentManager. La seva finalitat és la d'afegir, canviar, editar o treure fragments. També ens permet guardar l'estat anterior a la pila, per tal de poder retornar en prémer el botó de tornar enrere.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
fragmentTransaction.replace(R.id.fragment_container, MyFragment);
fragmentTransaction.addToBackStack(null);
Estructura bàsica
modificaMètodes imprescindibles per implementar el mínim funcionament de un fragment, extrets de la plantilla generada per l'Android Studio d’un fragment del tipus Blank (API23). Alguns dels mètodes corresponen a una implementació concreta, com per exemple l'aplicació d'algun patró.
OnCreate()
modificaMètode que es crida per crear el fragment. Hem de tenir present que quan es crida aquest mètode encara no està completament creada la activitat, i per tant no podem fer cap crida o operació amb dades de la activitat;si cal hi haurem de fer a OnActivityCreated o en les crides següents del cicle de vida del fragment.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_layout);
}
OnCreateView()
modificaMètode que es crida per crear la view. En aquest mètode hem d'inflar la view que volem assignar al fragment i retornar-la. Cal notar que degut a què la view de l'activity encara no està creada no podem accedir encara a cap objecte de la UI. En el cas en que no volguéssim cap view per al fragment podríem retornar un null.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank, container, false);
}
OnViewCreated()
modificaMètode que es crida un cop creada la view. En aquest mètode ja podem inicialitzar tots els mètodes/objectes que usin la UI.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
|anotacions= Exemple de codi amb els metodes emprats a clase per poder fer servir correctament un Fragment
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
inicialitza();
}
private void inicialitza() {
camp = (EditText) getActivity().findViewById(R.id.camp);
res = (TextView) getActivity().findViewById(R.id.resultat);
boto = (Button) getActivity().findViewById(R.id.boto);
boto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
esborraResultat(v);
}
});
camp.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
nouContingut(v);
return true;
}
});
}
public void nouContingut(View v) {
afegeixAResultat(camp.getText());
camp.setText("");
}
private void afegeixAResultat(CharSequence text) {
res.append("\n");
res.append(text);
}
private void esborraResultat(View v) {
res.setText("");
camp.setText("");
}
OnPause()
modificaCridat quan el fragment es pausat i es posa en background. Normalment va lligat a quan es crida al onPause() de l'activitat a la que està lligat. Aquesta pausa ve donada per una interacció de l'usuari que fa que el fragment o l'activitat deixa de ser visible i es posa en background.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public void onPause(){
super.onPause();
}
Quan "s'acaba" la pausa, s'invoca al mètode onResume() del fragment. Normalment, també com en el mètode onPause(), va lligat a la crida del onPause() de l'activitat a la que esta lligat.
OnAttach()
modificaCridat quan el fragment és afegit per primera vegada a una view. Es crida abans que el OnCreate().
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listener = (OnArticleSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
}
}
OnDetach()
modificaMètode cridat després de OnDestroy() un cop el fragment ha sigut separat de la seva activitat.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
NewInstance()
modificaNecessari per poder crear un nou fragment des de l'activitat i tractar el fragment. Aquest mètode ens permet implementar amb un patró d'autoconstrucció el fragment, lo que ens facilita poder passar arguments en el moment de la seva creació.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
Com podem comprovar en aquest exemple, passem com paràmetres dos Strings que s'afegeixen a un Bundle i aquest com a atribut del fragment. Això ens permet recuperar desprès aquests dos Strings des del codi del fragment.
Aquesta és una forma d'implementar la creació d'un Fragment, però també podem fer-ho creant directament el fragment amb la seva constructora.
OnFragmentInteractionListener()
modificaInterfície pública que cal implementar en totes les classes on es vulgui usar el fragment. La utilitzem per tal de transmetre informació del fragment al pare. Aquesta implementació és resultat de fer ús del patró observador. Aquesta interficie l'ha d'implementar l'observador per rebre les interaccions que desitgem del fragment.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
Com incloure el fragment en la activitat
modificaPrimer de tot hem d’implementar el mètode abstracte de la classe action listener
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public class MainActivity extends AppCompatActivity implements
BlankFragment.OnFragmentInteractionListener
Si es volgués algun tipus de comunicació entre el fragment i la activitat la posem aquí. |títol= Mètode emprat entre la comunicacó del Fragment i la Activity.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
public void onFragmentInteraction(Foo foo){
}
Per aquest exemple, partim d’un Linear Layout a la que hem afegit un text de prova, i un Framelayout que hem deixat buit. És en aquest Frame Layout on en el main afegirem el fragment. També podem utilitzar un altre tipus de Layout com a base, però és important col·locar correctament el Framelayout per a que el Fragment es visualitzi correctament.
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="xml">
//Provat en les versions 8.0 d'Android
@Override
<?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"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.recra.fragmentsdamo.MainActivity">
<TextView
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
</syntaxhighlight>
Exemple
modificaEn aquest exemple, hem implementat en un fragment un conjunt de widgets. La funció és agafar text d'una entrada de text i imprimir una llista de paraules que s'han entrat. També hi ha un botó que borra tota la llista de paraules. Tot això està dins del fragment, lo que ens permet ficar-lo dins d'una activitat diferent i conservar-ne les funcions.
MainActivity.java
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
package com.example.recra.fragmentsdamo;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity implements
BlankFragment.OnFragmentInteractionListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BlankFragment frag = BlankFragment.newInstance("a","b");
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();
}
public void onFragmentInteraction(Uri uri){
}
}
BlankFragment.java
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
package com.example.recra.fragmentsdamo;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link BlankFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link BlankFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class BlankFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
EditText camp;
TextView res;
Button boto;
private OnFragmentInteractionListener mListener;
public BlankFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment BlankFragment.
*/
// TODO: Rename and change types and number of parameters
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
inicialitza();
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onPause(){
super.onPause();
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
private void inicialitza() {
camp = (EditText) getActivity().findViewById(R.id.camp);
res = (TextView) getActivity().findViewById(R.id.resultat);
boto = (Button) getActivity().findViewById(R.id.boto);
boto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
esborraResultat(v);
}
});
camp.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
nouContingut(v);
return true;
}
});
}
public void nouContingut(View v) {
afegeixAResultat(camp.getText());
camp.setText("");
}
private void afegeixAResultat(CharSequence text) {
res.append("\n");
res.append(text);
}
private void esborraResultat(View v) {
res.setText("");
camp.setText("");
}
}
Layout.xml
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="xml">
//Provat en les versions 8.0 d'Android
'''Layout.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"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.recra.fragmentsdamo.MainActivity">
<TextView
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
</LinearLayout>
Resultat: Seguidament podem veure el resultat d'executar en un emulador o mobil android l'app anterior. Si observem la foto detingudament podrem apreciar que la primera linea es la propia activitat i tot el que ve després es el fragment android que te programats diversos widgets internament.
Exemple amb 2 fragments
modificaAquest exemple es igual que l'anterior amb l'afegit que ara en comptes de tenir un sol fragment usem dues vegades el mateix fragment.
Tot seguit mostrem el codi que cal substituir/afegir a l'exemple anterior per tal de tenir els 2 fragments:
MainActivity.java
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="java">
//Provat en les versions 8.0 d'Android
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BlankFragment frag = BlankFragment.newInstance("a","b");
BlankFragment frag1 = BlankFragment.newInstance("b","c");
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container1, frag1).commit();
}
Layout.xml
- Link al repositori: Pàgina principal d'Android, Fragments
- Anotacions: No
- Vegeu també: No
<source lang="xml">
//Provat en les versions 8.0 d'Android
<?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"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.recra.fragmentsdamo.MainActivity">
<TextView
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
<FrameLayout
android:id="@+id/fragment_container1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
</LinearLayout>
Resultat: Seguidament podem veure el resultat d'executar en un emulador o mòbil Android l'app anterior. Si observem la foto detingudament podrem apreciar que la primera línia es la pròpia activitat i tot seguit tenim dos fragments Android, a diferència de l'exemple anterior on només tenim un fragment.
Referències
modifica- ↑ «Fragment | Android Developers» (en anglès). [Consulta: 13/10/2016].
- ↑ «Android 3.0 APIs» (en anglès).
- ↑ «Cicle de vida d'un Fragment».