Maintenant que la théorie sur les fragments n'a plus de secrets pour vous, nous allons aborder dans ce chapitre leur implémentation, à travers une mini-application : MyFragmentApp.
Pour cela, nous allons créer deux activités, contenant chacune un seul fragment, afin de vous familiariser avec ce nouveau concept.
Création du projet
Nous allons partir d'un nouveau projet vierge, que vous pouvez retrouver et télécharger à cette adresse. Vous pouvez également le créer vous-même en utilisant l'assistant de création d'Android Studio, comme vu dans le premier cours.
Voici un aperçu de ce que nous allons réaliser :
Création de l'écran principal
Pour commencer, nous allons créer notre premier écran qui sera composé de :
Une activité, MainActivity, créée par défaut lors de la création du projet
Un fragment, MainFragment, qui sera contenu dans son activité parente, MainActivity
Mise à jour de l'arborescence
Avant toute chose, nous allons organiser notre projet afin de rester dans une architecture Android MVC standard. Pour cela, nous allons créer l'arborescente suivante :
Création du premier fragment
Nous allons créer maintenant notre premier fragment, MainFragment. Pour cela rien de plus simple, il suffit de faire un clic droit sur notre package "Fragments" et cliquer sur New ➞ Fragment ➞ Fragment(Blank) puis nommer votre premier fragment MainFragment :
Félicitations, votre premier fragment est créé ! En effet, l'assistant d'Android Studio a créé pour nous deux fichiers, MainFragment.java et fragment_main.xml. Tiens, c'est marrant, c'est exactement pareil que lors de la création d'une activité... :-° Voici donc notre premier fragment, MainFragment :
Fichier MainFragment.java :
public class MainFragment extends Fragment {
public MainFragment() { }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
}
Explication :
Comme nous l'avons vu précédemment, un fragment hérite toujours de la classe mère Fragment. Il est également important de préciser qu'un fragment doit toujours avoir un constructeur vide implémenté par défaut.
La méthode onCreateView quant à elle, permet de récupérer le layout fragment_main.xml afin de le définir comme layout principal de notre fragment (comme nous le faisons par ailleurs dans la méthode onCreate() d'une activité).
J'ai bien compris comment créer un fragment, mais comment le lier avec l'activité maintenant ?
Minute papillon, ça arrive ! Avant cela, il faut que je vous explique les deux manières de gérer les fragments dans une activité :
La manière statique : Très facile à utiliser, elle nous permet d'ajouter un fragment directement depuis le layout d'une activité (par exemple activity_main.xml). Cependant, celui-ci ne pourra plus être retiré ou modifié dynamiquement. C'est cette méthode que j'ai choisi de vous présenter dans ce chapitre.
La manière dynamique : L'ajout ou la suppression de fragment se fera directement depuis le FragmentManager de l'activité parente (MainActivity.java). Nous verrons cette implémentation dès le prochain chapitre.
Ajout du fragment dans son activité parente
Passons maintenant aux choses sérieuses ! Nous allons ajouter notre fragment fraîchement créé, MainFragment, dans notre activité MainActivity.
Avant cela, nous allons simplement modifier l'apparence de notre MainFragment en modifiant son fichier layout (fragment_main.xml) afin de lui ajouter un bouton pour appeler par la suite une seconde activité.
Fichier fragment_main.xml :
<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"
tools:context="com.openclassrooms.myfragmentapp.Controllers.Fragments.MainFragment"
android:gravity="center"
android:background="#D8DCF0">
<Button
android:id="@+id/fragment_main_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SHOW ME DETAILS !"
android:background="#00000000"
android:textColor="#FFF"
android:textSize="30sp"/>
</LinearLayout>
Maintenant, nous allons intégrer littéralement notre fragment MainFragment dans l'activité MainActivity via son layout activity_main.xml.
Fichier activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.openclassrooms.myfragmentapp.Controllers.Fragments.MainFragment"/>
Explication :
Nous avons ici ajouté notre fragment grâce à la balise <fragment> directement dans le layout de l'activité MainActivity. Grâce à l'attribut "android:name", nous réalisons la liaison avec le fragment MainFragment.
Lancez maintenant votre application. Votre fragment MainFragment est bien affiché à l'intérieur de votre activité MainActivity !
Communication entre le fragment et l'activité
Imaginons maintenant que nous voulions, au clic de l'utilisateur sur le bouton "SHOW ME DETAILS", ouvrir une nouvelle activité contenant également un fragment. Bon en fait nous n'allons pas l'imaginer, mais le faire ensemble !
Ce qui serait super, c'est de laisser la responsabilité à l'activité MainActivity de gérer le lancement de cette nouvelle activité.
Mais comment faire sachant que le bouton "SHOW ME DETAILS" (et par conséquent son listener) se trouveront dans MainFragment ?
Excellente question ! Et bien en faisant communiquer le fragment MainFragment, avec son activité parente MainActivity, grâce à un callback via une interface (si le concept d'interface est pour vous encore un peu vague, n'hésitez pas à relire le chapitre JAVA dédié).
Création du listener de notre bouton
Tout d'abord, nous allons créer dans MainFragment le listener du bouton "SHOW ME DETAILS" en implémentant l'interface View.OnClickListener et en enregistrant le bouton sur ce listener. Cela devrait vous parler, car nous l'avons déjà fait dans le premier cours.
Fichier MainFragment.java :
public class MainFragment extends Fragment implements View.OnClickListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Inflate the layout of MainFragment
View result=inflater.inflate(R.layout.fragment_main, container, false);
//Set onClickListener to button "SHOW ME DETAILS"
result.findViewById(R.id.fragment_main_button).setOnClickListener(this);
return result;
}
// --------------
// ACTIONS
// --------------
@Override
public void onClick(View v) {
//Here is handled the button click
}
}
Création du Callback pour communiquer avec l'activité
Ensuite, nous allons implémenter le Callback qui nous permettra de communiquer avec notre activité parente. Pour cela nous allons modifier notre fragment MainFragment comme ce qui suit :
Fichier MainFragment.java:
public class MainFragment extends Fragment implements View.OnClickListener {
//2 - Declare callback
private OnButtonClickedListener mCallback;
// 1 - Declare our interface that will be implemented by any container activity
public interface OnButtonClickedListener {
public void onButtonClicked(View view);
}
// --------------
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Inflate the layout of MainFragment
View result=inflater.inflate(R.layout.fragment_main, container, false);
//Set onClickListener to button "SHOW ME DETAILS"
result.findViewById(R.id.fragment_main_button).setOnClickListener(this);
return result;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// 4 - Call the method that creating callback after being attached to parent activity
this.createCallbackToParentActivity();
}
// --------------
// ACTIONS
// --------------
@Override
public void onClick(View v) {
// 5 - Spread the click to the parent activity
mCallback.onButtonClicked(v);
}
// --------------
// FRAGMENT SUPPORT
// --------------
// 3 - Create callback to parent activity
private void createCallbackToParentActivity(){
try {
//Parent activity will automatically subscribe to callback
mCallback = (OnButtonClickedListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(e.toString()+ " must implement OnButtonClickedListener");
}
}
}
Explications :
Création de l'interface : Nous créons ici une interface dans le but d'obliger toute activité souhaitant communiquer avec ce fragment à implémenter la méthode onButtonClicked(View).
Déclaration de notre Callback : En déclarant ce Callback comme variable au sein du fragment, nous allons pouvoir créer un lien direct avec notre activité parente.
Création du Callback pointant vers notre activité : Nous allons lier notre Callback avec notre activité parente en y souscrivant depuis le fragment enfant. Cependant, il faudra que notre activité parente (donc qui contient ce fragment) implémente l'interface OnButtonClickedListener.
Nous appelons ici la méthode de création du callback depuis onAttach(), car c'est à ce moment uniquement que l'on sait avec certitude que notre fragment est bien attaché à son activité parente.
Propagation du clic : Nous allons ici propager le clic de notre utilisateur directement à notre activité parente via la méthode onButtonClicked(View).
Lancer maintenant votre application. Que se passe-t-il ?
Et c'est normal ! Rappelez-vous, vous avez déclaré une interface afin d'obliger la communication entre le fragment et son activité parente. L'activité MainActivity doit donc implémenter cette interface afin de se soumettre à ce contrat !
Fichier MainActivity.java :
public class MainActivity extends AppCompatActivity implements MainFragment.OnButtonClickedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// --------------
// CallBack
// --------------
@Override
public void onButtonClicked(View view) {
Log.e(getClass().getSimpleName(),"Button clicked !");
}
}
Explications :
Dans votre activité MainActivity, vous allez devoir implémenter l'interface OnButtonClickedListener du fragment MainFragment, et ainsi déclarer la méthode onButtonClicked(View) qui sera appelée dès lors qu'un utilisateur appuiera sur le bouton "SHOW ME DETAILS".
La communication est maintenant opérationnelle entre notre Fragment et son activité parente. N'hésitez pas à tester votre application afin de vous rendre compte du résultat.
Création de l'écran secondaire
Maintenant que tout fonctionne, il faudrait afficher l'activité DetailActivity lorsque l'on clique sur le bouton "SHOW ME DETAILS", non ?
Cette activité contiendra également un fragment, DetailFragment, qui affichera le texte "I have no details to show you...".
Avec ce que nous avons vu précédemment ainsi que vos solides compétences, je suis persuadé que vous êtes complètement capable de réaliser cela tout seul... ;) Ainsi, vous allez donc devoir créer 4 fichiers :
La classe DetailActivity.java et son layout activity_detail.xml qui contiendra un fragment.
La classe DetailFragment.java et son layout fragment_detail.xml.
Prêt·e ? Comptez moins de 5 minutes pour réaliser ce mini exercice... Tic, tac... :ange:
Vous avez vu ! Cela s'est fait rapidement, n'est-ce pas ? Voici donc les différents fichiers que vous avez normalement dû créer :
Fichier DetailActivity.java :
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
}
}
Fichier activity_detail.xml :
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.openclassrooms.myfragmentapp.Controllers.Fragments.DetailFragment"/>
Fichier DetailFragment.java :
public class DetailFragment extends Fragment {
public DetailFragment() { }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_detail, container, false);
}
}
Fichier fragment_detail.xml :
<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"
tools:context="com.openclassrooms.myfragmentapp.Controllers.Fragments.DetailFragment"
android:gravity="center"
android:background="#D8DCF0">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I have no details to show you..."
android:textColor="#FFF"
android:textSize="20sp"/>
</LinearLayout>
Pour finir, vous avez également été obligés de modifier le fichier MainActivity.java afin de lancer cette nouvelle activité au clic de l'utilisateur via un Intent, non ? ;)
Aperçu du fichier MainActivity.java :
public class MainActivity extends AppCompatActivity implements MainFragment.OnButtonClickedListener {
...
@Override
public void onButtonClicked(View view) {
Log.e(getClass().getSimpleName(),"Button clicked !");
startActivity(new Intent(this, DetailActivity.class));
}
}
Conclusion
Félicitations, vous avez créé votre première mini-application implémentant deux fragments. Vous pouvez être fier ! L'application entière est disponible à ce lien.
Cependant, le petit hic, c'est que notre application n'est pas encore optimisée pour l'affichage sur une tablette... Vous trouvez ça intolérable ? Parfait ! Nous allons y remédier dès le prochain chapitre. C'est parti !