Les bases de l'interface utilisateur
Introduction
Les applications Android proposent, pour la majorité d’entre elles, une interface utilisateur. Celle-ci va permettre une interaction à double sens entre l’utilisateur et l’application de façon visuelle, tactile et sonore.
Ce chapitre présente les bases de la construction d’interface utilisateur. Nous commencerons par détailler le concept de layout d’activité et analyser les différentes options pour construire une interface utilisateur. Nous verrons ensuite en détail les fondements de la construction graphique des interfaces, et étudierons quelques composants parmi les plus utilisés dans les écrans d’une application. Enfin, nous nous pencherons sur les concepts propres aux activités sous Android (cycle de vie des activités, sauvegarde et restauration de l’état), et terminerons par une présentation de la gestion des piles d’activités, notion indispensable pour l’optimisation de la navigation dans les applications les plus complexes.
Il est d’usage de parler de clic pour désigner l’action de pression et de relâchement rapide d’un bouton d’un dispositif de pointage comme une souris par exemple. Bien que la plupart des terminaux Android se passent avantageusement de souris, le terme de clic sera utilisé pour décrire le contact du doigt sur l’écran...
Concepts de base
1. Activités et Layout
Dans l’univers Android, les activités (Activity en anglais) font partie des objets les plus utilisés. Chaque écran que voit et manipule l’utilisateur est, en effet, implémenté par une classe qui hérite de la classe Activity. Il est donc primordial de parfaitement comprendre tous les concepts apportés par cette classe.
public class MonActivitePrincipale extend Activity {
[...]
}
À quelques exceptions près (principalement les services), une application comporte au minimum une classe héritant de Activity et peut, bien sûr, en comporter plusieurs. Une et une seule activité est lancée au démarrage d’une application.
Chaque activité, pour être lancée, doit impérativement être déclarée dans le fichier Manifest.xml, dans une balise <activity>, balise enfant de la balise <application>.
Syntaxe
<activity android:icon="ressource drawable"
android:label="ressource texte"
android:name="chaîne de caractères"
... >
...
</activity>
L’activité de démarrage, celle qui porte le premier écran affiché par l’application, doit être signalée au système. Pour cela, il faut adjoindre une balise enfant <intent-filter> à la balise <activity>, balise qui doit elle-même contenir les balises suivantes :
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
La déclaration de l’activité MonActivitePrincipale comme point d’entrée de l’application aura donc la syntaxe suivante :
[...]
<application android:icon="@mipmap/icon"
android:label="@string/app_name">
<activity android:name=".MonActivitePrincipale"
android:label="@string/app_name">
<intent-filter>
<action...
Layouts
Les layouts sont des conteneurs de vues (ViewGroup) prédéfinis fournis par Android. Chaque layout propose un style de mise en page différent permettant aux vues de se positionner les unes par rapport aux autres ou par rapport au conteneur parent.
Les principaux layouts prédéfinis sont FrameLayout, LinearLayout, RelativeLayout et TableLayout. Ils offrent respectivement une structure de positionnement des vues par plan, linéaire, relative et sous forme de tableau.
Attention de ne pas faire l’amalgame entre les fichiers de layout, définis dans le répertoire /res/layout/, et ces conteneurs de vues : les fichiers de layout sont les fichiers permettant de composer l’interface visuelle d’une activité en mode déclaratif. Les conteneurs de vue, FrameLayout, LinearLayout et autres, sont les objets qui structurent la mise en page. Typiquement, un fichier de layout contient, en premier élément, un conteneur de vues (que ce soit un FrameLayout, un TableLayout ou plus souvent, un LinearLayout ou un RelativeLayout).
FrameLayout
Conteneur réduit à sa plus simple expression. Tout ce qu’il contient sera dessiné à partir du coin en haut à gauche. Les derniers éléments enfants ajoutés seront dessinés par-dessus les plus anciens.
Exemple
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#ff0000"/>
<View
android:layout_width="75dp"
android:layout_height="25dp"
android:background="#0000ff"/>
</FrameLayout>

LinearLayout
Conteneur dont les éléments enfants sont disposés, suivant la valeur de l’attribut orientation, soit :
-
Verticalement, les uns sous les autres, un seul élément par ligne. Le mot-clé correspondant est vertical.
-
Horizontalement...
Widgets
Un widget est un composant de l’interface graphique qui peut ou non permettre l’interaction avec l’utilisateur.
De base, Android fournit un ensemble de composants permettant la création d’interfaces graphiques plus ou moins complexes.
1. Déclaration
La déclaration de ces composants est aisée dans les fichiers de layouts au format XML. La syntaxe de leur déclaration est la suivante :
Syntaxe
<Type_de_widget
android:id="@[+][paquetage:]id/nom_ressource"
android:layout_width="dimension"
android:layout_height="dimension"
...
/>
La propriété android:id permet d’associer un identifiant unique au composant. C’est une propriété optionnelle mais cet identifiant est indispensable pour pouvoir manipuler le composant dans le code Java de l’application.
La valeur de l’identifiant unique est de la forme @+id/nom_ressource. Le signe plus indique qu’il s’agit d’un nouvel identifiant. Celui-ci se verra donc assigner une nouvelle valeur de façon automatique lors de la génération du fichier R.java, dans la sous-classe R.id.
2. Utilisation
Pour pouvoir utiliser le composant depuis le code Java de l’application, il suffit d’utiliser la méthode findViewById et de lui spécifier en paramètre l’identifiant unique du composant concerné. Cette méthode retourne un objet de type View qu’il faut ensuite convertir dans le type de la classe concernée.
Syntaxe
public View findViewById (int id)
Exemple
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_principal);
Button btn = (Button) findViewById(R.id.button_cliquez_moi);
}
Cette possibilité d’obtenir dans le code Java une référence d’un widget défini dans un fichier de layout donne une solution efficace pour la construction et les manipulations de l’interface utilisateur : la création de l’interface est faite à l’aide de fichiers XML, les manipulations sont, elles, opérées dans le code Java.
Exemple
public void onCreate(Bundle savedInstanceState) { ...
Activités
Nous l’avons vu précédemment, une activité est un composant applicatif indépendant qui possède la plupart du temps une interface utilisateur. Dans le cas le plus représentatif, une activité présente un écran à l’utilisateur afin d’interagir avec lui. Une application très simple peut n’avoir qu’une seule activité alors qu’une plus complexe peut en posséder plusieurs. Cependant, une seule activité de l’application est active à la fois, l’écran qu’elle gère étant celui que voit l’utilisateur.
Rappelons que pour définir une activité, il faut créer une classe qui hérite de la classe Activity et implémenter, le cas échéant, les méthodes héritées.
L’exécution d’une activité s’opère dans le processus léger, ou thread, principal du processus de l’application. Ce thread est également appelé thread de l’interface utilisateur (UIThread) car lui seul permet de modifier l’interface utilisateur. Toute modification de l’interface depuis un thread concurrent génère une erreur.
Afin de préserver l’expérience utilisateur, une activité ne doit pas bloquer son thread principal plus de quelques secondes (cf. chapitre Concurrence, sécurité et réseau - Programmation concurrente).
1. Déclaration
Pour être utilisée, une activité doit être déclarée au système via le manifeste.
La balise activity contient les informations propres à une activité. Comme la plupart des balises, cette balise propose une multitude d’attributs. Nous en étudierons certains au fur et à mesure de la découverte des fonctionnalités concernées.
Une activité acceptant de recevoir des intentions implicites doit obligatoirement déclarer la catégorie android.intent.category.DEFAULT dans ses filtres d’intention. Cette catégorie est automatiquement ajoutée à l’objet intent lorsque cet objet est passé à la méthode startActivity ou à la méthode startActivityForResult.
Rappelons la syntaxe de cette balise...