Lire un extrait ( PDF 653 Ko)

publicité
4
Créer des interfaces utilisateur
Au sommaire de ce chapitre :
■■
Utiliser les vues et les layouts
■■
Comprendre les fragments
■■
Optimiser les layouts
■■
Créer des interfaces utilisateur indépendantes de la résolution
■■
Étendre, grouper, créer et utiliser les vues
■■
Utiliser des adaptateurs pour lier des données aux vues.
Citons Stephen Fry sur l’importance du style comme essence de la conception d’appareils numériques :
Comme si un appareil pouvait fonctionner sans style. Comme si un appareil pouvait
être stylé s’il ne fonctionnait pas parfaitement. Oui, la beauté compte, et sacrément.
Elle n’est pas superficielle, elle n’est pas optionnelle, elle est la chose elle-même.
Stephen Fry, The Guardian (27 octobre 2007)
Bien que Fry décrit le style des terminaux eux-mêmes, il aurait pu dire la même
chose des applications qui s’exécutent sur ceux-ci. L’augmentation des tailles, des
résolutions et de la luminosité des écrans, ainsi que le support du multitouch, a rendu
les applications de plus en plus visuelles. L’introduction de terminaux optimisés
pour un confort d’utilisation de plus en plus grand – notamment les tablettes et les
télévisions – dans l’écosystème Android n’a fait qu’augmenter l’importance de la
conception visuelle d’une application.
Nous vous présenterons dans ce chapitre les éléments de base de l’interface Android
et vous découvrirez comment utiliser les layouts, les fragments et les vues pour créer
des interfaces fonctionnelles et intuitives pour vos activités.
Les différents éléments d’une interface utilisateur Android sont rangés à l’écran par le
biais de plusieurs gestionnaires de layouts dérivés de la classe ViewGroup. Ce chapitre
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 101
03/08/12 07:26
102

Android 4
introduira plusieurs classes layout natives et montrera comment les utiliser, comment
créer les vôtres et comment vous assurer que vous utilisez les layouts le plus efficacement possible.
La gamme des tailles et des résolutions d’écrans s’est étendue en même temps que
la gamme des terminaux disponibles. Android 3.0 a introduit l’API des fragments
afin d’offrir un meilleur support de création de layouts dynamiques, pouvant être
optimisés pour les tablettes et pour un grand nombre d’écrans de smartphones.
Après avoir découvert quelques contrôles visuels fournis par le SDK, vous apprendrez
à les étendre et à les personnaliser. Grâce aux groupes de vues, vous combinerez
des vues pour créer des éléments d’interfaces atomiques et réutilisables, constitués
de sous-contrôles qui interagissent. Vous créerez également vos propres vues pour
afficher des données et interagir avec vos utilisateurs. Enfin, nous présenterons les
adaptateurs et nous verrons comment les utiliser pour lier votre couche présentation
aux sources de données sous-jacentes.
Fondements de la conception d’interface sous Android
La conception d’interface utilisateur, l’expérience utilisateur, l’interaction hommemachine et la facilité d’utilisation sont de vastes sujets que nous ne couvrirons pas
en profondeur dans cet ouvrage. Il est néanmoins important de les avoir en tête lors
de la création de vos interfaces utilisateur.
Android introduit une nouvelle terminologie pour des métaphores de programmation
familières, que nous allons explorer en détail dans les sections suivantes :
■■
Vues. Les Vues sont les classes de base pour tous les éléments visuels d’interface
(communément appelés contrôles ou widgets). Tous les contrôles d’interface, y
compris les classes layout, sont dérivés de la classe View.
■■
Groupes de vues. Les groupes de vues sont des extensions de la classe View
pouvant contenir plusieurs vues filles. L’extension de la classe ViewGroup permet
de créer des contrôles composites constitués de vues filles interconnectées. La
classe ViewGroup peut être également étendue pour fournir les gestionnaires de
layouts qui aident à disposer les contrôles dans vos activités.
■■
Fragments. Les fragments, introduits par Android 3.0 (API level 11) servent à
encapsuler des portions de votre interface. Cette encapsulation est particulièrement utile pour optimiser les layouts de votre interface pour différentes tailles
d’écrans et pour créer des composants d’interface réutilisables. Chaque fragment
contient son propre layout et reçoit les événements d’entrée concernés, mais il
est étroitement lié à l’activité dans laquelle il doit être intégré. Les fragments
ressemblent aux contrôleurs de vues utilisés dans le développement pour iPhone.
■■
Activités. Les activités, décrites en détail dans le chapitre précédent, représentent
les fenêtres ou les écrans affichés. Elles sont les équivalents Android des Forms
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 102
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
103
de Windows. Pour afficher une interface utilisateur, vous devez affecter une vue
(en général, un layout ou un fragment) à une activité.
Android fournit de nombreux contrôles, widgets et gestionnaires de layouts.
Pour la plupart des applications graphiques, il est probable que vous aurez besoin
d’étendre et de modifier ces vues standard ou d’en créer de nouvelles pour fournir
votre propre expérience utilisateur.
Fondements des interfaces utilisateur sous Android
Tous les composants visuels Android descendent de la classe View et sont généralement désignés sous le terme générique de vues. Vous verrez souvent appelés vues
des contrôles ou des widgets (à ne pas confondre les widgets de l’écran d’accueil, qui
sont décrits au Chapitre 14), termes avec lesquels vous êtes probablement familier si
vous avez déjà développé pour des environnements graphiques.
La classe ViewGroup est une extension de la classe View conçue pour contenir plusieurs
vues. Les groupes de vues sont utilisés le plus souvent pour gérer la disposition des
vues filles, mais également pour construire des composants atomiques réutilisables.
Les groupes de vues qui tiennent le premier rôle sont appelés layouts.
Dans les sections qui suivent, vous apprendrez à concevoir des interfaces de plus en
plus complexes, puis nous présenterons les fragments, les vues offertes par le SDK,
comment les étendre, comment construire vos propres contrôles composés et créer
des vues personnalisées en partant de zéro.
Affecter des interfaces utilisateur aux activités
Une nouvelle activité démarre avec un écran vide dans lequel vous allez placer votre
interface utilisateur. Pour cela, appelez setContentView en lui passant l’instance de
View ou la ressource layout à afficher. Les écrans vides n’étant pas particulièrement
attirants, vous utiliserez presque toujours setContentView pour affecter une interface
à une activité lorsque vous redéfinirez son gestionnaire onCreate.
La méthode setContentView prend en paramètre soit un identifiant de ressource
layout, soit une simple instance de View. Cela vous permet de définir votre interface
utilisateur dans le code ou en utilisant la technique privilégiée des ressources layout
externes.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Utiliser des ressources layout découple votre couche de présentation de la logique
applicative, vous donnant ainsi la souplesse de modifier la présentation sans toucher
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 103
03/08/12 07:26
104

Android 4
au code. Cela permet de préciser différents layouts optimisés pour des configurations
matérielles différentes, voire de les modifier à l’exécution en fonction de changements
matériels (l’orientation de l’écran, par exemple).
La méthode findViewById permet d’obtenir une référence à chacune des vues d’un
layout :
TextView myTextView = (TextView)findViewById(R.id.myTextView);
Si vous préférez l’approche plus traditionnelle, vous pouvez construire l’interface
directement dans votre code :
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView myTextView = new TextView(this);
setContentView(myTextView);
myTextView.setText(“Hello, Android”);
}
La méthode setContentView prend en paramètre une instance unique de View :
vous devez donc utiliser des layouts pour ajouter plusieurs contrôles à votre activité.
Si vous utilisez les fragments pour encapsuler des portions de l’interface de l’activité,
la vue désérialisée dans le gestionnaire onCreate sera un layout qui décrit la position
relative de chaque fragment (ou de ses conteneurs). L’interface utilisateur utilisée pour
chaque fragment est définie dans son propre layout et désérialisée dans le fragment
lui-même, comme nous le verrons plus loin dans ce chapitre.
Lorsqu’un fragment a été désérialisé dans une activité, les vues qu’il contient font
partie de la hiérarchie des vues de cette activité : vous pouvez donc retrouver n’importe
laquelle de ses vues filles dans l’activité parente en utilisant findByViewId, comme
précédemment.
Introduction aux layouts
Les gestionnaires de layout (appelés en général simplement layouts) sont des extensions de la classe ViewGroup utilisées pour placer des vues filles dans votre interface
utilisateur. Les layouts peuvent être imbriqués, ce qui permet, en les combinant, de
créer des interfaces complexes.
Le SDK Android inclut quelques classes layouts. Vous pouvez les utiliser, les modifier
ou créer vos propres layouts pour construire l’interface utilisateur de vos vues, de
vos fragments et de vos activités. À vous de sélectionner la bonne combinaison pour
rendre votre interface facile à comprendre et à utiliser.
La liste qui suit présente quelques-unes des classes layout les plus utilisées :
■■ FrameLayout. Le plus simple des gestionnaires de layout. Il épingle simplement
chaque vue fille dans le coin supérieur gauche (mais vous pouvez changer cette
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 104
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
105
position par défaut à l’aide de l’attribut gravity). Ajouter plusieurs filles les
empile les unes sur les autres, chaque nouvelle vue dissimulant la précédente.
■■ LinearLayout .
Aligne chaque vue fille verticalement ou horizontalement.
Un layout vertical montre une colonne de vues alors qu’un layout horizontal
montre une ligne de vues. Ce gestionnaire vous permet de préciser un "poids"
pour chaque fille à l’aide de l’attribut weight, afin de contrôler la taille relative
de chacune d’entre elles dans l’espace disponible.
■■ RelativeLayout. Le plus souple des layouts natifs. Il vous permet de définir les
positions de chaque vue fille par rapport aux autres et aux limites de l’écran.
■■ GridLayout. Introduit par Android 4.0 (API level 14), ce gestionnaire utilise une
grille rectangulaire de fines lignes pour disposer les vues dans une suite de lignes
et de colonnes. Il est extrêmement souple et permet de beaucoup simplifier les
layouts ou d’éliminer l’imbrication complexe qui est souvent nécessaire avec les
gestionnaires précédents. Pour le construire, il est préférable d’utiliser l’éditeur
de layout plutôt que de créer manuellement sa description en XML
Chacun de ces layouts est conçu pour s’adapter à la taille de l’écran du terminal hôte
en évitant d’utiliser des positions absolues ou des valeurs de pixels prédéterminées.
Ceci les rend particulièrement utiles lorsque l’on conçoit des applications devant
fonctionner sur plusieurs terminaux Android différents.
La documentation Android décrit les caractéristiques et propriétés de chaque
classe layout en détail. Plutôt que la reproduire ici, nous préférons vous y renvoyer :
http://developer.android.com/guide/topics/ui/layout-objects.html
Vous verrez des exemples pratiques d’utilisation de ces layouts car ils sont utilisés
dans les exemples de ce livre. Plus loin, dans ce chapitre, vous apprendrez également
à créer des contrôles composites en utilisant et/ou en étendant ces classes.
Définir les layouts
La façon privilégiée d’implémenter des layouts est d’utiliser des ressources externes
en XML.
Chaque layout XML doit contenir un seul élément racine qui peut contenir autant de
layouts et de vues imbriqués nécessaires à la construction d’un écran arbitrairement
complexe.
L’extrait suivant montre un layout simple qui place une TextView au-dessus d’un
EditText à l’aide d’un LinearLayout vertical :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 105
03/08/12 07:26
106

Android 4
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Entrez votre texte ici"
/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Le texte s\’affiche là !"
/>
</LinearLayout>
Notez que pour chaque élément du layout on utilise les constantes wrap_content et
fill_parent plutôt qu’une hauteur ou une largeur exactes en pixels. Ces constantes,
combinées aux layouts qui savent s’adapter (comme les layouts linéaires, relatifs ou
en grille), sont la technique la plus simple et la plus puissante pour garantir que vos
layouts seront indépendants de la taille et de la résolution de l’écran.
La constante wrap_content fixe la taille de la vue au minimum nécessaire pour
afficher son contenu (la hauteur requise pour afficher un texte, par exemple).
La constante fill_parent étend la vue pour remplir l’espace disponible dans la vue, le
fragment ou l’activité parente.
Vous apprendrez au cours de ce chapitre comment paramétrer la hauteur et la largeur
minimales de vos propres contrôles ainsi que d’autres pratiques permettant de rester
indépendant de la résolution.
L’implémentation des layouts en XML découple la couche de présentation du code
de la vue, du fragment, le code contrôleur de l’activité et le code métier. Elle vous
permet également de créer des variantes spécifiques au matériel, qui seront chargées
dynamiquement et qui ne nécessitent pas de modifications du code.
Si vous le préférez ou si vous le devez, vous pouvez implémenter les layouts dans votre
code. Lorsque vous affectez des vues à des layouts de cette façon, il est important
d’appliquer LayoutParameters à l’aide de la méthode setLayoutParams ou en les
passant à l’appel addView :
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
TextView myTextView = new TextView(this);
EditText myEditText = new EditText(this);
myTextView.setText("Entrez votre texte ici ");
myEditText.setText("Le texte s’affiche là !");
int lHeight = LinearLayout.LayoutParams.FILL_PARENT;
int lWidth = LinearLayout.LayoutParams.WRAP_CONTENT;
ll.addView(myTextView, new LinearLayout.LayoutParams(lHeight, lWidth));
ll.addView(myEditText, new LinearLayout.LayoutParams(lHeight, lWidth));
setContentView(ll);
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 106
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
107
Utiliser des layouts pour créer des interfaces utilisateur indépendantes
du matériel
Une caractéristique des classes layout que nous avons décrites précédemment et des
techniques que nous avons présentées pour les utiliser dans vos applications est leur
faculté à s’adapter à un grand éventail de tailles, de résolutions et d’orientations d’écrans.
La diversité des terminaux Android joue un rôle essentiel dans son succès. Pour les
développeurs, cette diversité implique de concevoir des interfaces qui fourniront le
meilleur confort possible à leurs utilisateurs.
Utilisation d’un layout linéaire
Le layout linéaire est l’une des classes layout les plus simples qui soit. Il permet de
créer des interfaces utilisateur (ou des éléments d’interface) simples dans lesquelles
les vues filles sont alignées verticalement ou horizontalement.
La simplicité des layouts linéaires les rend très simples d’emploi, mais limite
également leur souplesse. Dans la plupart des cas, vous les utiliserez pour construire
des éléments d’interfaces qui seront imbriqués dans d’autres layouts, comme un
layout relatif, par exemple.
Le Listing 4.1 montre deux layouts linéaires imbriqués – un layout horizontal
contenant deux boutons de même taille et un layout vertical qui place ces deux
boutons au-dessus d’une ListView.
Listing 4.1 : Layout linéaire
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”>
<LinearLayout
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal”
android:padding=”5dp”>
<Button
android:text=”@string/cancel_button_text”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1”/>
<Button
android:text=”@string/ok_button_text”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1”/>
</LinearLayout>
<ListView
android:layout_width=”match_parent”
android:layout_height=”match_parent”/>
</LinearLayout>
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 107
03/08/12 07:26
108

Android 4
Si vous vous rendez compte que vous devez créer des imbrications de plus en plus
complexes de layouts linéaires, il est sûrement temps de penser à utiliser un gestionnaire de layouts plus souple.
Utilisation d’un layout relatif
Un layout relatif offre une grande souplesse à vos layouts puisqu’il permet de définir la
position de chaque élément dans le layout par rapport à son parent et aux autres vues.
Le Listing 4.2 modifie le layout décrit dans le Listing 4.1 pour déplacer les boutons
sous la ListView.
Listing 4.2 : Layout relatif
<?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”>
<LinearLayout
android:id=”@+id/button_bar”
android:layout_alignParentBottom=”true”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal”
android:padding=”5dp”>
<Button
android:text=”@string/cancel_button_text”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1”/>
<Button
android:text=”@string/ok_button_text”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1”/>
</LinearLayout>
<ListView
android:layout_above=”@id/button_bar”
android:layout_alignParentLeft=”true”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
</ListView>
</RelativeLayout>
Utilisation d’un layout en grille
Le layout en grille a été introduit par Android 4.0 (API level 14) ; c’est lui qui offre
le plus de souplesse parmi tous les gestionnaires de layouts.
Il utilise une grille arbitraire pour positionner les vues. En utilisant le regroupement
de lignes et de colonnes, la vue spatiale et les attributs gravity, vous pouvez créer
des interfaces complexes sans devoir passer par l’imbrication qui est nécessaire avec
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 108
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
109
le layout relatif que nous venons de décrire. Le layout en grille est tout particulièrement utile pour construire des layouts qui exigent un alignement dans les deux
directions – un formulaire dont les lignes et les colonnes doivent être alignées, mais
qui inclut également des éléments qui ne s’adaptent pas très bien au motif en grille
classique, par exemple.
Vous pouvez également disposer de toutes les fonctionnalités offertes par un layout
relatif en combinant un layout en grille et un layout linéaire. Pour des raisons d’efficacité, il est préférable d’utiliser un layout en grille plutôt que de créer la même
interface en combinant des layouts imbriqués.
Le Listing 4.3 présente le même layout que celui du Listing 4.2, mais se sert d’un
layout en grille à la place du layout relatif.
Listing 4.3 : Layout en grille
<?xml version=”1.0” encoding=”utf-8”?>
<GridLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”>
<ListView
android:background=”#FF444444”
android:layout_gravity=”fill”>
</ListView>
<LinearLayout
android:layout_gravity=”fill_horizontal”
android:orientation=”horizontal”
android:padding=”5dp”>
<Button
android:text=”Annuler”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1”/>
<Button
android:text=”OK”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_weight=”1”/>
</LinearLayout>
</GridLayout>
Notez qu’il n’est pas nécessaire de fixer les paramètres largeur et hauteur des éléments
du layout en grille. Chaque élément enveloppe son contenu par défaut et l’attribut
layout_gravity sert à déterminer dans quelle direction chaque élément doit s’étendre.
Optimiser les layouts
La désérialisation de layouts est un processus coûteux. Chaque layout et vue imbriqué
peut avoir un impact dramatique sur la performance et la transparence de vos applications.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 109
03/08/12 07:26
110

Android 4
C’est en général une bonne pratique de garder vos layouts aussi simples que possible,
mais également d’éviter de désérialiser un layout entier pour quelques changements
mineurs dans un autre.
Les conteneurs de layouts redondants sont inutiles
Un layout linéraire dans un layout frame, tous les deux avec match_parent, ne fait
qu’augmenter le temps de désérialisation. Recherchez les layouts redondants, surtout
si vous avez effectué beaucoup de modifications dans un layout existant ou que vous
lui ayez ajouté des layout fils.
Il n’y a pas de limite à l’imbrication des layouts : il est donc facile de créer des
hiérarchies complexes et fortement imbriquées. Malgré cette absence de limite, il
est conseillé de ne pas dépasser dix niveaux.
Un exemple classique d’imbrication inutile est l’utilisation d’un layout frame pour
créer l’unique nœud racine d’un layout, comme l’illustre l’extrait suivant :
<?xml version=”1.0” encoding=”utf-8”?>
<FrameLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<ImageView
android:id=”@+id/myImageView”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:src=”@drawable/myimage”
/>
<TextView
android:id=”@+id/myTextView”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:text=”@string/hello”
android:gravity=”center_horizontal”
android:layout_gravity=”bottom”
/>
</FrameLayout>
Ici, le layout frame deviendra redondant s’il est ajouté à un parent. Une meilleure
solution consiste à utiliser la balise merge :
<?xml version=”1.0” encoding=”utf-8”?>
<merge xmlns:android=”http://schemas.android.com/apk/res/android”>
<ImageView
android:id=”@+id/myImageView”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:src=”@drawable/myimage”
/>
<TextView
android:id=”@+id/myTextView”
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 110
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
111
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:text=”@string/hello”
android:gravity=”center_horizontal”
android:layout_gravity=”bottom”
/>
</merge>
Lorsqu’un layout contenant une balise merge est ajouté à un autre layout, le nœud
merge est supprimé et ses vues filles sont ajoutées directement au nouveau parent.
Cette balise est tout particulièrement pratique lorsqu’elle est combinée à la balise
include, qui permet d’insérer le contenu d’un layout dans un autre :
<?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”>
<include android:id=”@+id/my_action_bar”
layout=”@layout/actionbar”/>
<include android:id=”@+id/my_image_text_layout”
layout=”@layout/image_text_layout”/>
</LinearLayout>
La combinaison des balises merge et include permet de créer des définitions de layouts
simples et réutilisables, qui ne créent pas de hiérarchies lourdement imbriquées.
Éviter les vues trop nombreuses
Toute vue supplémentaire prend du temps et des ressources pour se désérialiser.
Pour optimiser la vitesse et la réactivité de votre application, aucun de ses layouts ne
devrait inclure plus de 80 vues. Passé cette limite, le temps pris par la désérialisation
du layout devient non négligeable.
Pour minimiser le nombre de vues dans un layout complexe, utilisez un ViewStub.
Une ébauche de vue (view stub) fonctionne comme une inclusion paresseuse – c’est
une ébauche qui représente les vues filles indiquées dans le layout parent, mais cette
ébauche est désérialisée uniquement de façon explicite, par un appel à inflate ou
lorsqu’elle devient visible.
// Recherche de l’ébauche
View stub = findViewById(R.id. download_progress_panel_stub);
// On la rend visible, ce qui force la désérialisation du layout fils
stub.setVisibility(View.VISIBLE);
// Recherche du nœud racine du layout désérialisé
View downloadProgressPanel = findViewById(R.id.download_progress_panel);
Les vues contenues dans le layout fils ne seront donc créées que si on en a besoin,
ce qui réduit le coût en termes de temps et de ressources lorsque l’on désérialise des
interfaces utilisateur complexes.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 111
03/08/12 07:26
112

Android 4
Lorsque vous ajoutez un ViewStub à votre layout, vous pouvez redéfinir les paramètres
id et layout de la vue racine du layout qu’elle représente :
<?xml version=”1.0” encoding=”utf-8”?>
<FrameLayout “xmlns:android=http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<ListView
android:id=”@+id/myListView”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
/>
<ViewStub
android:id=”@+id/download_progress_panel_stub”
android:layout=”@layout/progress_overlay_panel”
android:inflatedId=”@+id/download_progress_panel”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_gravity=”bottom”
/>
</FrameLayout>
Cet extrait modifie la largeur, la hauteur et l’ancrage du layout importé afin que ces
valeurs correspondent aux exigences du layout parent. Cette souplesse permet de
créer et de réutiliser le même layout fils générique dans plusieurs layouts parents.
Les attributs id et inflatedId indiquent, respectivement, l’identifiant du StubView et
du groupe de vues qu’il deviendra lorsqu’il sera désérialisé.
Info
Lorsque l’ébauche de vue est désérialisée, elle est supprimée de la hiérarchie des vues
et remplacée par le nœud racine de la vue qu’elle importe. Pour modifier la visibilité
des vues importées, vous devez soit utiliser la référence à leur nœud racine (renvoyé
par l’appel à inflate), soit trouver la vue à l’aide de findViewById, en utilisant l’identifiant
de layout qui lui est affecté dans le nœud ViewStub correspondant.
Utiliser Lint pour analyser les layouts
Pour vous aider à optimiser vos hiérarchies de layout, le SDK inclut lint, un outil
puissant permettant de détecter les problèmes dans vos applications, notamment ceux
concernant les performances des layouts.
Cet outil est disponible en ligne de commande ou, comme le montre la Figure 4.1,
dans une fenêtre Eclipse puisqu’il fait partie du plugin ADT.
Outre la détection des problèmes d’optimisation décrits dans cette section, lint permet également de repérer les traductions manquantes, les ressources inutilisées,
les tailles de tableau incohérentes, les problèmes d’accessibilité et d’internationalisation, les images dupliquées ou absentes, les problèmes liés à l’utilisation et les
erreurs du manifeste.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 112
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
113
Lint est un outil en perpétuelle évolution et de nouvelles règles lui sont ajoutées régulièrement. La liste complète des tests qu’il effectue se trouve à l’URL
http://tools.android.com/tips/lint-checks.
Figure 4.1
Exemple de liste de tâches
Dans cet exemple, vous allez créer une nouvelle application Android à partir de zéro
en utilisant les vues et les layouts fournis par Android.
Info
Ne vous inquiétez pas si vous ne comprenez pas tout ce qui est décrit dans cet
exemple. Certaines fonctionnalités utilisées comme les ArrayAdapters, les ListViews et
les KeyListeners ne seront introduites que dans les chapitres suivants, où elles seront
expliquées en détail. Vous reviendrez également plus tard à cet exemple pour y ajouter
de nouvelles fonctionnalités au fur et à mesure de votre progression.
1. Commencez par créer un nouveau projet Android. Sous Eclipse, sélectionnez
File > New > Project…, puis choisissez Android Project sous le nœud Android
(voir la Figure 4.2) et cliquez sur Next.
2. Entrez les détails de votre nouveau projet.
2.1 Indiquez un nom de projet (voir la Figure 4.3), puis cliquez sur Next.
2.2 Choisissez la cible. Sélectionnez la plateforme la plus récente, comme le
montre la Figure 4.4, puis cliquez sur Next.
2.3 Entrez les détails du projet (voir la Figure 4.4). Application name sera le
nom visible de votre application et le champ Create Activity vous permettra
de nommer votre activité. Une fois ces informations entrées, cliquez sur
Finish pour créer votre nouveau projet.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 113
03/08/12 07:26
114

Android 4
Figure 4.2
Figure 4.3
Figure 4.4
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 114
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
115
Figure 4.5
3. Avant de créer vos configurations de débogage et d’exécution, créez un appareil
virtuel qui vous permettra de tester vos applications :
3.1. Sélectionnez Window > AVD Manager. Dans la boîte de dialogue (voir la
Figure 4.6), cliquez sur le bouton New…
Figure 4.6
3.2 Dans le dialogue qui s’ouvre (voir la Figure 4.7), entrez un nom pour votre
appareil et choisissez le SDK cible (le même que celui que vous avez choisi
pour votre projet à l’étape 2.2 et la résolution d’écran. Choisissez une taille
de carte SD supérieure à 8 Mo, cochez la case snapshot, puis cliquez sur
Create AVD.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 115
03/08/12 07:26
116

Android 4
Figure 4.7
4. Créez maintenant vos configurations de débogage et d’exécution. Sélectionnez
Run > Debug Configurations puis Run > Run Configurations… afin d’en créer
une de chaque pour le projet TodoList et sélectionnez l’appareil virtuel que
vous avez créé à l’étape 3. Vous pouvez également choisir un terminal physique
s’il est connecté et que le débogage soit activé. Vous pouvez conserver Launch
Defaut Activity comme action de lancement ou choisir explicitement de lancer
la nouvelle activité ToDoListActivity.
5. Vous devez maintenant décider ce que vous allez montrer aux utilisateurs et
quelles actions ils devront effectuer. Concevez une interface qui rende ces actions
aussi intuitives que possible.
Dans cet exemple, nous voulons présenter aux utilisateurs une liste de tâches
et une zone de saisie permettant d’en ajouter de nouvelles. Les bibliothèques
Android proposent les contrôles liste et zone de saisie pour ce faire (plus loin
dans ce chapitre, vous en saurez plus sur les vues disponibles sous Android et
sur la façon d’en créer de nouvelles).
La meilleure méthode pour créer la présentation de notre interface utilisateur est
d’utiliser un fichier de ressource de layout. Ouvrez le fichier main.xml, situé dans
le dossier res/layout du projet et modifiez-le en lui ajoutant une ListView et un
EditText dans le LinearLayout. Il est important d’attribuer un ID à l’EditText
et à la ListView afin de pouvoir les référencer dans votre code.
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”vertical”
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 116
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
117
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<EditText
android:id=”@+id/myEditText”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:hint=”@string/addItemHint”
android:contentDescription=”@string/addItemContentDescription”
/>
<ListView
android:id=”@+id/myListView”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
/>
</LinearLayout>
6. Dans le fichier strings.xml du dossier res/values, vous devrez également ajouter
les ressources chaînes qui contiennent les messages de l’étape 5. Profitez-en pour
supprimer la chaîne "hello" qui est présente par défaut :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Liste de tâches</string>
<string name="addItemHint">Nouvelle tâche</string>
<string name="addItemContentDescription">Nouvelle tâche</string>
</resources>
7. Une fois votre interface utilisateur définie, ouvrez l’activité ToDoListActivity
dans le dossier src de votre projet. Commencez par désérialiser votre interface
utilisateur à l’aide de setContentView puis récupérez les références de ListView
et EditText à l’aide de findViewById.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Désérialisation de la vue.
setContentView(R.layout.main);
// Récupération des références aux widgets de l’interface.
ListView myListView = (ListView)findViewById(R.id.myListView);
final EditText myEditText = (EditText)findViewById(R.id.myEditText);
}
Info
Lorsque vous ajouterez le code de l’étape 7 dans ToDoListActivity ou lorsque que vous
tenterez de compiler votre projet, Eclipse ou le compilateur se plaindront que les classes
ListView et EditText ne peuvent pas être résolues comme des types.
Vous devez ajouter des instructions d’import à votre classe, afin d’inclure les bibliothèques
qui contiennent ces vues (ici, android.widget.EditText et android.widget.ListView). Pour
que les codes et les exemples de ce livre restent courts et lisibles, nous n’inclurons
pas tous ces imports (mais ils sont présents dans les codes sources que vous pouvez
télécharger).
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 117
03/08/12 07:26
118

Android 4
Si vous utilisez Eclipse, les classes pour lesquelles il manque des instructions import sont
soulignées en rouge. En cliquant sur ces classes, une liste vous proposera d’inclure pour
vous les instructions d’import manquantes.
Eclipse propose également un raccourci pratique (Ctrl-Maj-o) qui tentera de créer
automatiquement toutes les instructions import pour les classes utilisées dans votre code.
8. Toujours dans la méthode onCreate, définissez une ArrayList de chaînes pour
stocker chaque élément de la liste. Vous pouvez lier une ListView à une ArrayList
en utilisant un ArrayAdapter. Créez une nouvelle instance d’ArrayAdapter pour
lier le tableau des items à la ListView.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView myListView = (ListView)findViewById(R.id.myListView);
final EditText myEditText = (EditText)findViewById(R.id.myEditText);
// Création de la liste de tâches
final ArrayList<String> todoItems = new ArrayList<String>();
// Création de l’array adapter pour lier l’ArrayList à la ListView.
final ArrayAdapter<String> aa;
aa = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
todoItems);
// Liaison de l’ArrayAdapter à la ListView.
myListView.setAdapter(aa);
}
9. La dernière étape pour rendre cette liste fonctionnelle est de permettre aux utilisateurs d’y ajouter de nouveaux éléments. Ajoutez à l’EditText un onKeyListener,
qui sera à l’écoute des clics sur un Dpad ou de la touche Entrée. Chacune de ces
actions doit ajouter le contenu de l’EditText au tableau de la liste et informer
l’ArrayAdapter des modifications. Enfin, nettoyez l’EditText pour le préparer à
l’élément suivant.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView myListView = (ListView)findViewById(R.id.myListView);
final EditText myEditText = (EditText)findViewById(R.id.myEditText);
final ArrayList<String> todoItems = new ArrayList<String>();
final ArrayAdapter<String> aa;
aa = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
todoItems);
myListView.setAdapter(aa);
myEditText.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN)
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER) ||
(keyCode == KeyEvent.KEYCODE_ENTER)) {
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 118
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
119
todoItems.add(0, myEditText.getText().toString());
aa.notifyDataSetChanged ();
myEditText.setText("");
return true;
}
return false;
}
});
}
10. Exécutez ou déboguez l’application et vous verrez apparaître un champ de saisie
au-dessus d’une liste, comme dans la Figure 4.8.
11. Vous avez maintenant terminé votre première "vraie" application Android.
Ajoutez des points d’arrêt au code pour tester le débogueur et essayez la
perspective DDMS.
Info
Tous les extraits de code de cet exemple font partie du projet Chapitre 2 To-do List
disponible en téléchargement sur le site de l’éditeur, pearson.fr, à la page consacrée à
cet ouvrage.
Figure 4.8
Telle quelle, cette application n’est pas extraordinairement utile. Les entrées de la liste
ne sont pas sauvegardées entre les sessions, vous ne pouvez modifier ou supprimer une
entrée et les indications classiques d’une liste de tâches comme les dates d’échéance
ou les priorités ne sont ni enregistrées ni affichées. De plus, elle ne respecte presque
aucune des règles de conception d’une bonne application mobile vues jusqu’à présent.
Vous corrigerez quelques-uns de ces défauts lorsque vous reviendrez sur cet exemple
au cours des chapitres suivants.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 119
03/08/12 07:26
120

Android 4
Introduction aux fragments
Les fragments permettent de diviser vos activités en composants réutilisables,
totalement autonomes, chacun avec son propre cycle de vie et sa propre interface
utilisateur.
Le principal avantage des fragments est la facilité avec laquelle vous pouvez créer
des interfaces dynamiques et souples pouvant s’adapter à un grand nombre de tailles
d’écrans – des plus petits smartphones aux tablettes.
Chaque fragment est un module indépendant qui est intimement lié à l’activité dans
laquelle il se trouve. Les fragments peuvent être réutilisés dans plusieurs activités,
agencés selon un grand nombre de combinaisons pour s’adapter aux interfaces multipanneaux des tablettes et ajoutés, supprimés et échangés dans une activité en cours
afin de pouvoir construire aisément des interfaces utilisateur dynamiques.
Ils permettent de présenter une interface cohérente, optimisée pour un large éventail
de types de terminaux, de tailles et de densités d’écrans.
Bien qu’il ne soit pas nécessaire de diviser vos activités (et leurs layouts associés) en
fragments, le faire améliore énormément la souplesse de votre interface utilisateur
et vous facilitera la tâche lorsqu’il s’agira de l’adapter à de nouvelles configurations
de terminaux.
Info
Les fragments ont été introduits par Android 3.0 Honeycomb (API level 11). Ils font
désormais également partie de la bibliothèque support d’Android, ce qui permet de
les utiliser sur les plateformes à partir d’Android 1.6.
Pour utiliser les fragments fournis par la bibliothèque support, votre activité doit étendre
la classe FragmentActivity :
public class MyActivity extends FragmentActivity
Si vous utilisez cette bibliothèque dans un projet ayant pour cible une version au moins
supérieure à l’API level 11, vérifiez bien que tous les imports liés aux fragments et les
références de classes n’utilisent que les classes de la bibliothèque support. Les fragments
de la bibliothèque native et de la bibliothèque support sont étroitement liés, mais leurs
classes ne sont pas interchangeables.
Créer de nouveaux fragments
Pour créer un nouveau fragment, créez une classe fille de la classe Fragment en
définissant (éventuellement) l’interface utilisateur et en implémentant la fonctionnalité
qu’elle encapsule.
Dans la plupart des cas, vous affecterez une interface à votre fragment. Vous pouvez
aussi créer un fragment sans interface, fournissant à la place un comportement
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 120
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
121
d’arrière-plan pour une activité – nous y reviendrons plus en détail, plus loin dans
ce chapitre.
Si votre fragment a besoin d’une interface utilisateur, redéfinissez la méthode
onCreateView pour qu’elle désérialise et renvoie la hiérarchie de vues voulue, comme
le montre le squelette de code du Listing 4.4.
Listing 4.4 : Squelette de création d’un fragment
package com.paad.fragments;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MySkeletonFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// Crée ou désérialise, puis renvoie l’interface du fragment
// Renvoie null si le fragment n’a pas d’interface.
return inflater.inflate(R.layout.my_fragment, container, false);
}
}
Vous pouvez créer le layout d’un fragment directement dans votre code, en vous
servant des groupes de vues ; cependant, comme pour les activités, il est préférable
de concevoir les layouts des fragments en désérialisant une ressource XML.
À la différence des activités, les fragments n’ont pas besoin d’être enregistrés dans
le manifeste car ils ne peuvent exister que s’ils sont intégrés dans une activité ; leurs
cycles de vie dépendent de celui de l’activité à laquelle ils ont été ajoutés.
Le cycle de vie des fragments
Les événements du cycle de vie d’un fragment reflètent ceux de son activité parente.
Cependant, lorsque l’activité conteneur est dans son état actif – ou qu’elle a repris –,
ajouter ou supprimer un fragment affectera son cycle de vie de façon indépendante.
Les fragments incluent une série de gestionnaires d’événements qui imitent ceux de
la classe Activity. Ils sont déclenchés lorsque le fragment est créé, lancé, repris, en
pause, arrêté et détruit. Ils contiennent également un certain nombre de fonctions de
rappel supplémentaires qui signalent la liaison du fragment à son activité parente et
son détachement de celle-ci, la création (et la destruction) de la hiérarchie de vues
du fragment et la fin de la création de l’activité parente.
La Figure 4.9 résume ce cycle de vie.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 121
03/08/12 07:26
122

Android 4
Créé
Visible
Fragment.onAttach
Fragment.onCreate
Fragment.onCreateView
Fragment.onActivityCreated
Actif
Fragment.onStart
Fragment.onResume
Fragment.onStop
Fragment.onPause
Retour au layout
Fragment.onDetach
Fragment.onDestroy
Fragment.onDestroyView
Figure 4.9
Le squelette de code du Listing 4.5 montre les ébauches des gestionnaires du cycle
de vie d’un fragment. Dans chaque ébauche, les commentaires décrivent les actions
que vous devriez entreprendre à chaque changement d’état.
Info
Vous devez appeler les méthodes de la superclasse lorsque vous redéfinissez la plupart
de ces gestionnaires d ’événements.
Listing 4.5 : Gestionnaires d’événements du cycle de vie d’un fragment
package com.paad.fragments;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MySkeletonFragment extends Fragment {
// Appelée lorsque le fragment est attaché à son activité parente.
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Récupération d’une référence à l’activité parente.
}
// Appelée pour la création initiale du fragment.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialisation du fragment.
}
// Appelée lorsque le fragment a été créé, afin qu’il crée son interface
// utilisateur.
@Override
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 122
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
123
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// Création ou désérialisation, plus renvoi de l’interface du fragment
// Renvoie null si le fragment n’a pas d’interface.
return inflater.inflate(R.layout.my_fragment, container, false);
}
// Appelée lorsque les interfaces de l’activité parente et du fragment
// ont été créées.
@Override
public void onActivityCreated(Bundle savedInstanceState)
super.onActivityCreated(savedInstanceState);
// Terminer l’initialisation du fragment – notamment tout ce qui
// a besoin que l’activité parente ait été initialisée ou que
// la vue du fragment soit totalement désérialisée.
}
// Appelée au début de l’état visible.
@Override
public void onStart(){
super.onStart();
// Appliquer les modifications d’interface nécessaires maintenant
// que le fragment est visible.
}
// Appelée au début de l’état actif.
@Override
public void onResume(){
super.onResume();
// Reprendre les mises à jour de l’interface, les threads ou les
// processus suspendus, requis par le fragment mais suspendus
// lorsqu’il est devenu inactif.
}
// Appelée à la fin de l’état actif.
@Override
public void onPause(){
// Suspendre les mises à jour de l’interface, les threads ou
// les processus gourmands en CPU qui n’ont pas besoin d’être modifés
// lorsque l’activité n’est pas l’activité active de premier plan.
// Sauvegarder toutes les modifications ou les changements d’état,
// car le processus risque d’être tué après cet appel.
super.onPause();
}
// Appelée pour sauvegarder les changements d’état de l’interface
// à la fin de l’état actif .
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Sauvegarder les changements d’état de l’interface dans
// savedInstanceState. Ce bundle sera passé à onCreate, onCreateView
// et onCreateView si l’activité parente est tuée et relancée.
super.onSaveInstanceState(savedInstanceState);
}
// Appelée à la fin de l’état visible.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 123
03/08/12 07:26
124

Android 4
@Override
public void onStop(){
// Suspendre les mises à jour restantes de l’interface, les
// threads et les processus qui ne sont pas nécessaires lorsque le
// fragment n’est pas visible.
super.onStop();
}
// Appelée lorsque la vue du fragment a été détachée.
@Override
public void onDestroyView() {
// Nettoyage des ressources liées à la vue.
super.onDestroyView();
}
// Appelée à la fin du cycle de vie complet.
@Override
public void onDestroy(){
// Nettoyage de toutes les ressources et fin des threads,
// fermeture des connexions aux bases de données, etc.
super.onDestroy();
}
// Appelée lorsque le fragment a été détaché de son activité parente.
@Override
public void onDetach() {
super.onDetach();
}
}
Événements du cycle de vie spécifiques aux fragments
La plupart des événements du cycle de vie des fragments correspondent à leurs
équivalents pour la classe Activity, qui ont été présentés en détail au Chapitre 3.
Les autres sont spécifiques aux fragments et à la façon dont ils sont insérés dans
leur activité parente.
Attachement d’un fragment à son activité parente et détachement de
celle-ci
Le cycle de vie complet d’un fragment commence lorsqu’il est lié à son activité parente
et se termine lorsqu’il s’en détache. Ces événements sont représentés, respectivement,
par les appels à onAttach et onDetach.
Comme tout gestionnaire invoqué après qu’un fragment ou une activité passe en
pause, il est possible que onDetach ne soit pas appelé si le processus de l’activité
parente se termine sans finir son cycle de vie complet.
L’événement onAttach est déclenché avant la création de l’interface utilisateur du
fragment, avant que le fragment lui-même ou son activité parente n’aient terminé
leur initialisation. Généralement, il sert à obtenir une référence à l’activité parente
en préparation des futures tâches d’initialisation.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 124
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
125
Création et suppression d’un fragment
Un fragment est dans l’état "créé" entre le premier appel à onCreate et l’appel final
à onDestroy. Comme il est assez fréquent que le processus d’une activité se termine
sans appel à la méthode onDestroy correspondante, un fragment ne peut pas se fier
au déclenchement de son gestionnaire onDestroy.
Comme pour les activités, vous devez utiliser onCreate pour initialiser votre fragment.
Il est conseillé d’y créer tous les objets ayant la classe pour portée afin de s’assurer
qu’ils ne seront créés qu’une seule fois au cours de la vie du fragment.
Info
À la différence des activités, l’interface utilisateur d’un fragment n’est pas initialisée
dans onCreate.
Création et suppression des interfaces utilisateur
L’interface utilisateur d’un fragment est initialisée (et supprimée) par, respectivement,
un appel à onCreateView et un appel à onDestroyView.
Utilisez onCreateView pour initialiser le fragment : désérialisez l’interface, récupérez
les références aux vues qu’elle contient (et liez-leur des données), puis créez les
services et timers nécessaires.
Renvoyez la hiérarchie de vues une fois qu’elle a été désérialisée :
return inflater.inflate(R.layout.my_fragment, container, false);
Si votre fragment doit interagir avec l’interface utilisateur de son activité parente,
attendez le déclenchement de l’événement onActivityCreated, qui signifie que l’activité
conteneur a terminé son initialisation et que son interface est totalement construite.
États d’un fragment
Le sort d’un fragment est inextricablement lié à celui de l’activité à laquelle il appartient. En conséquence, les transitions d’état d’un fragment sont intimement liées aux
transitions d’état de l’activité correspondante.
Comme les activités, les fragments sont actifs lorsqu’ils appartiennent à une activité
qui a le focus et qui est au premier plan. Lorsqu’une activité est mise en pause ou
stoppée, les fragments qu’elle contient sont également mis en pause et stoppés ; les
fragments contenus par une activité inactive sont eux aussi inactifs. Lorsqu’une
activité est finalement détruite, tous les fragments qu’elle contient le sont également.
Le gestionnaire mémoire d’Android fermant les applications de façon non déterministe lorsqu’il a besoin de libérer des ressources, les fragments de ces activités sont
également détruits.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 125
03/08/12 07:26
126

Android 4
Bien que fragments et activités soient fortement liés, l’un des avantages d’utiliser
des fragments pour composer l’interface utilisateur de votre application est que vous
pouvez ajouter dynamiquement des fragments à une activité active, ou en supprimer.
Chaque fragment peut donc progresser plusieurs fois dans son cycle de vie complet,
visible et actif, au cours de la vie de son activité parente.
Quel que soit le déclencheur d’une transition d’un fragment au cours de son cycle
de vie, il est essentiel de gérer ses transitions d’état pour assurer le meilleur confort
possible à l’utilisateur. On ne devrait ressentir aucune différence lorsqu’un fragment
passe de l’état en pause, stoppé ou inactif à l’état actif et il est donc important de
sauvegarder l’état de l’interface et toutes les données lorsqu’un fragment est mis en
pause ou stoppé. Comme une activité, un fragment doit restaurer cet état sauvegardé
lorsqu’il redevient actif.
Introduction au gestionnaire de fragments
Chaque activité contient un gestionnaire de fragments pour gérer les fragments
qu’elle contient. Vous pouvez y accéder à l’aide de la méthode getFragmentManager :
FragmentManager fragmentManager = getFragmentManager();
Le gestionnaire de fragments fournit des méthodes pour accéder aux fragments
ajoutés à l’activité et pour effectuer les transactions permettant d’ajouter, supprimer
et remplacer des fragments.
Ajouter des fragments aux activités
Le meilleur moyen d’ajouter un fragment à une activité consiste à l’inclure dans le
layout de l’activité à l’aide de la balise fragment, comme dans le Listing 4.6.
Listing 4.6 : Ajout de fragments à une activité à l’aide d’un layout XML
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”horizontal”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<fragment android:name=”com.paad.weatherstation.MyListFragment”
android:id=”@+id/my_list_fragment”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_weight=”1”
/>
<fragment android:name=”com.paad.weatherstation.DetailsFragment”
android:id=”@+id/details_fragment”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_weight=”3”
/>
</LinearLayout>
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 126
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
127
Le fragment devient un groupe de vues lorsqu’il est désérialisé, affichant et gérant
son interface dans l’activité.
Cette technique fonctionne bien lorsque l’on utilise les fragments pour définir un
ensemble de layouts statiques en fonction des différentes tailles d’écrans. Si vous
comptez modifier dynamiquement vos layouts en ajoutant, supprimant ou remplaçant
des fragments en cours d’exécution, une meilleure approche consiste à créer des
layouts qui utilisent des vues conteneurs dans lesquelles vous pourrez placer vos
fragments au cours de l’exécution, en fonction de l’état de l’application courante.
Le Listing 4.7 est un extrait XML dont vous pouvez partir pour implémenter cette
approche.
Listing 4.7 : Layouts de fragments utilisant des vues conteneurs
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”horizontal”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<FrameLayout
android:id=”@+id/ui_container”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_weight=”1”
/>
<FrameLayout
android:id=”@+id/details_container”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_weight=”3”
/>
</LinearLayout>
Vous devez ensuite créer et ajouter les fragments correspondants à leur conteneurs
parents dans le gestionnaire onCreate de l’activité, en utilisant les transactions de
fragments, décrites dans la section qui suit.
Utilisation des transactions de fragments
Les transactions de fragments permettent d’ajouter, de supprimer et de remplacer
des fragments dans une activité au cours de l’exécution. Grâce à elles, vous pouvez
rendre vos layouts dynamiques – ils s’adapteront et se modifieront en fonction des
actions de l’utilisateur et de l’état de l’application.
Chaque transaction de fragment peut inclure une combinaison quelconque de certaines
actions, comme l’ajout, la suppression ou le remplacement d’un fragment. Vous
pouvez également indiquer les animations de transition à afficher et s’il faut inclure
la transaction sur la pile de retour.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 127
03/08/12 07:26
128

Android 4
Une transaction de fragment est créée par la méthode beginTransaction du gestionnaire de fragments de l’activité. Pour modifier le layout, utilisez les méthodes add,
remove et replace, en fonction de vos besoins, avant de configurer les animations à
afficher et le comportement de la pile de retour. Lorsque vous êtes prêt à exécuter
les modifications, appelez la méthode commit pour ajouter la transaction à la file
d’attente de l’interface utilisateur.
FragmentTransaction fragmentTransaction = fragmentManager.
beginTransaction();
// Ajout, suppression et/ou remplacement de fragments.
// Spécifcation des animations.
// Ajout à la pile de retour si nécessaire.
fragmentTransaction.commit();
Tous les types de transactions, avec leurs options, seront étudiés dans les sections
qui suivent.
Ajout, suppression et remplacement de fragments
Pour ajouter un nouveau fragment d’interface utilisateur, indiquez l’instance de
fragment à ajouter ainsi que la vue conteneur dans laquelle le placer. Vous pouvez
également indiquer un marqueur qui pourra servir plus tard à retrouver ce fragment
par un appel à findFragmentByTag :
FragmentTransaction fragmentTransaction = fragmentManager.
beginTransaction();
fragmentTransaction.add(R.id.ui_container, new MyListFragment());
fragmentTransaction.commit();
Pour supprimer un fragment, vous devez d’abord retrouver sa référence, en utilisant
généralement la méthode findFragmentById ou findFragmentByTag du gestionnaire
de fragments. Puis passez cette référence en paramètre à la méthode remove d’une
transaction de fragments :
FragmentTransaction fragmentTransaction = fragmentManager.
beginTransaction();
Fragment fragment = fragmentManager.findFragmentById(R.id.details_fragment);
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
Vous pouvez également remplacer un fragment par un autre. Appelez la méthode
replace en lui passant l’identifiant du conteneur contenant le fragment à remplacer,
le fragment par lequel le remplacer et (éventuellement) un marqueur pour identifier
le nouveau fragment inséré :
FragmentTransaction fragmentTransaction = fragmentManager.
beginTransaction();
fragmentTransaction.replace(R.id.details_fragment,
new DetailFragment(selected_index));
fragmentTransaction.commit();
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 128
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
129
Utilisation du gestionnaire de fragments pour trouver des fragments
Pour retrouver les fragments d’une activité, utilisez la méthode findFragmentById du
gestionnaire de fragments. Si vous avez ajouté votre fragment au layout de l’activité
en XML, vous pouvez vous servir de son identificateur :
MyFragment myFragment =
(MyFragment)fragmentManager.findFragmentById(R.id.MyFragment);
Si vous l’avez ajouté à l’aide d’une transaction, vous devriez indiquer l’identifiant
de ressource de sa vue conteneur. Vous pouvez également utiliser la méthode
findFragmentByTag pour retrouver le fragment à partir du marqueur que vous avez
fourni dans la transaction :
MyFragment myFragment =
(MyFragment)fragmentManager.findFragmentByTag(MY_FRAGMENT_TAG);
Plus loin dans ce chapitre, nous présenterons les fragments qui n’ont pas d’interface
utilisateur. En ce cas, la méthode findFragmentByTag est essentielle pour interagir
avec eux : comme ils ne font pas partie de la hiérarchie des vues de l’activité, ils
n’ont pas d’identifiant de ressource, ni d’identifiant de conteneur, et vous ne pouvez
donc pas utiliser la méthode findFragmentById.
Remplir des layouts dynamiques d’activité avec des fragments
Si vous modifiez dynamiquement la composition et la disposition de vos fragments
en cours d’exécution, il est conseillé de ne définir que les conteneurs parents dans
le layout XML et de ne le remplir qu’avec des transactions lors de l’exécution : cela
garantit une certaine cohérence lorsque les changements de configuration (lors des
rotations d’écran, par exemple) imposent de recréer l’interface utilisateur.
Le Listing 4.8 montre le squelette de code utilisé pour remplir en cours d’exécution
le layout d’une activité avec des fragments.
Listing 4.8 : Remplissage de vues conteneurs avec des fragments
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Désérialisation du layout contenant les conteneurs de fragments.
setContentView(R.layout.fragment_container_layout);
FragmentManager fm = getFragmentManager();
// Teste si la pile de retour a été remplie
// Si non, on crée et on remplit le layout.
DetailsFragment detailsFragment =
(DetailsFragment)fm.findFragmentById(R.id.details_container);
if (detailsFragment == null) {
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.details_container, new DetailsFragment());
ft.add(R.id.ui_container, new MyListFragment());
ft.commit();
}
}
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 129
03/08/12 07:26
130

Android 4
Vous devez d’abord vérifier si l’interface utilisateur a déjà été remplie, en vous servant
de l’état précédent. Pour garantir un confort d’utilisation cohérent, Android mémorise
le layout du fragment et la pile de retour lorsque l’activité est redémarrée à cause
d’un changement de configuration.
Pour la même raison, lorsque vous créez des layouts alternatifs pour les modifications
de configuration en cours d’exécution, il est conseillé d’inclure chaque conteneur
impliqué dans les transactions dans toutes les variantes du layout. Si vous ne le
faites pas, le gestionnaire de fragments tentera de restaurer des fragments dans des
conteneurs qui n’existent pas dans le nouveau layout.
Pour supprimer un conteneur de fragment dans le layout d’une orientation donnée,
il suffit de mettre son attribut de visbilité à " gone " dans la définition du layout,
comme le montre le Listing 4.9.
Listing 4.9 : Masquage de fragments dans des variantes du layout
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”horizontal”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<FrameLayout
android:id=”@+id/ui_container”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_weight=”1”
/>
<FrameLayout
android:id=”@+id/details_container”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:layout_weight=”3”
android:visibility=”gone”
/>
</LinearLayout>
Les fragments et la pile de retour
Au Chapitre 3, nous avons présenté les piles d’activités – l’empilage logique des
activités qui ne sont plus visibles – qui permettent aux utilisateur de revenir aux
écrans précédents à l’aide du bouton de retour en arrière.
Les fragments vous permettent de créer des layouts d’activité dynamiques, qui peuvent
être modifiés pour représenter les changements importants dans les interfaces utilisateur. Dans certains cas, ces modifications peuvent être considérées comme un
nouvel écran – auquel cas, l’utilisateur peut raisonnablement supposer que le bouton
de retour arrière le fera revenir à l’écran précédent. Ceci implique d’effectuer à l’envers
les transactions de fragments déjà exécutées.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 130
03/08/12 07:26
Chapitre 4
Créer des interfaces utilisateur
131
Android fournit une technique commode – une pile de retour – pour assurer ce
traitement. Pour ajouter le fragment à cette pile, il suffit d’appeler la méthode
addToBackStack de la transaction avant d’appeler commit.
FragmentTransaction fragmentTransaction = fragmentManager.
beginTransaction();
fragmentTransaction.add(R.id.ui_container, new MyListFragment());
Fragment fragment = fragmentManager.findFragmentById(R.id.details_fragment);
fragmentTransaction.remove(fragment);
String tag = null;
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commit();
Presser le bouton de retour en arrière reprendra la transaction de fragment précédente
et l’interface utilisateur reviendra à son layout antérieur.
Lorsque la transaction ci-dessus est validée par l’appel à commit, le fragment Details est
stoppé et placé sur la pile de retour au lieu d’être simplement détruit. Si la transaction
est reprise, le fragment List est supprimé et le fragment Details est relancé.
Animation des transactions de fragments
Pour appliquer l’une des animations de transition par défaut, utilisez la méthode
setTransition d’une transaction de fragment, en lui passant l’une des constantes
FragmentTransaction.TRANSIT_FRAGMENT_*
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) ;
Vous pouvez également appliquer des animations personnelles aux transactions en
utilisant la méthode setCustomAnimations, qui prend en paramètres deux ressources
d’animation en XML : une pour les fragments ajoutés au layout par cette transaction,
l’autre pour les fragments supprimés :
fragmentTransaction.setCustomAnimations(R.animator.slide_in_left,
R.animator.slide_out_right);
Cette approche permet d’ajouter des transitions dynamiques lorsque vous remplacez
des fragments dans votre layout.
Info
Avec l’introduction de la classe Animator, les bibliothèques d’animation d’Android ont
été beaucoup améliorées à partir d’Android 3.0 (API level 11). Ceci implique que la
ressource d’animation passée à la méthode setCustomAnimations est différente pour les
applications construites avec la bibliothèque support.
En effet, les applications construites pour les terminaux sous API level 11 et supérieur
devraient désormais utiliser des ressources Animator, alors que celles qui utilisent la
bibliothèque de compatibilité avec les versions antérieures doivent continuer d’utiliser
les anciennes ressources Animation.
Le Chapitre 11 reviendra en détail sur la création de ressources Animator et Animation
personnalisées.
© 2012 Pearson France – Android 4 – Reto Meier
Livre ANDROID4.indb 131
03/08/12 07:26
Téléchargement