• Aucun résultat trouvé

Encore plus de widgets et de conteneurs

N/A
N/A
Protected

Academic year: 2022

Partager "Encore plus de widgets et de conteneurs"

Copied!
24
0
0

Texte intégral

(1)

14

Encore plus de widgets et de conteneurs

Nous avons déjà présenté un certain nombre de widgets et de conteneurs, et ce chapitre sera le dernier qui leur sera exclusivement consacré : il présente plusieurs widgets souvent utilisés dans les applications, comme les widgets pour la date et l’heure, les onglets, etc. Après ce chapitre, nous découvrirons encore de nouveaux widgets, mais uniquement dans le contexte d’un autre sujet, comme la présentation de ProgressBar au Chapitre 20 (qui est consacré aux threads).

Choisir

Avec des terminaux ayant des capacités de saisie limitées comme les téléphones, il est très utile de disposer de widgets et de boîtes de dialogue capables d’anticiper ce que l’utilisateur veut taper. Cela minimise le nombre de frappes au clavier et de touches à l’écran et réduit les risques d’erreur (la saisie d’une lettre à la place d’un chiffre, par exemple).

Comme on l’a mentionné au Chapitre 9, EditText possède des variantes permet- tant de saisir des nombres ou du texte. Android dispose également de widgets (DatePicker, TimePicker) et de dialogues (DatePickerDialog, TimePickerDialog) facilitant la saisie des dates et des heures.

DatePicker et DatePickerDialog permettent de fixer une date de départ, sous la forme d’une année, d’un mois et d’un jour. Les mois vont de 0 (janvier) à 11 (décembre).

Tous les deux permettent de préciser un écouteur (OnDateChangedListener ou

OnDateSetListener) pour vous informer qu’une nouvelle date a été choisie. Il vous appartient de stocker cette date quelque part, notamment si vous utilisez la boîte de dialogue, car vous n’aurez pas d’autre moyen de la récupérer ensuite.

De même, TimePicker et TimePickerDialog permettent :

De fixer l’heure initiale, que l’utilisateur peut ensuite ajuster sous la forme d’une heure (de 0 à 23) et de minutes (de 0 à 59).

(2)

D’indiquer si le format de la date choisi utilise le mode sur 12 heures avec un  indicateur AM/PM ou le mode 24  heures (ce qui, aux États-Unis, est appelé "temps militaire" et qui est le mode utilisé partout ailleurs dans le monde).

De fournir un écouteur (OnTimeChangedListener ou OnTimeSetListener) pour être prévenu du choix d’une nouvelle heure, qui vous sera fournie sous la forme d’une heure et de minutes.

Le projet Fancy/Chrono, par exemple, utilise une disposition très simple, formée d’un label et de deux boutons qui feront surgir les boîtes de dialogue pour choisir une date et une heure :

<?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"

>

<TextView android:id="@+id/dateAndTime"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

/>

<Button android:id="@+id/dateBtn"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Choisir une date"

android:onClick="chooseDate"

/>

<Button android:id="@+id/timeBtn"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Choisir une heure"

android:onClick="chooseTime"

/>

</LinearLayout>

La partie intéressante se trouve dans le code Java :

package com.commonsware.android.chrono;

import android.app.Activity;

import android.os.Bundle;

import android.app.DatePickerDialog;

import android.app.TimePickerDialog;

import android.view.View;

import android.widget.DatePicker;

import android.widget.TimePicker;

import android.widget.TextView;

(3)

import java.text.DateFormat;

import java.util.Calendar;

public class ChronoDemo extends Activity {

DateFormat fmtDateAndTime=DateFormat.getDateTimeInstance();

TextView dateAndTimeLabel;

Calendar dateAndTime=Calendar.getInstance();

@Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.main);

dateAndTimeLabel=(TextView)findViewById(R.id.dateAndTime);

updateLabel();

}

public void chooseDate(View v) {

new DatePickerDialog(ChronoDemo.this, d,

dateAndTime.get(Calendar.YEAR), dateAndTime.get(Calendar.MONTH), dateAndTime.get(Calendar.DAY_OF_MONTH)) .show();

}

public void chooseTime(View v) {

new TimePickerDialog(ChronoDemo.this, t,

dateAndTime.get(Calendar.HOUR_OF_DAY), dateAndTime.get(Calendar.MINUTE), true)

.show();

}

private void updateLabel() {

dateAndTimeLabel.setText(fmtDateAndTime

.format(dateAndTime.getTime()));

}

DatePickerDialog.OnDateSetListener d=

new DatePickerDialog.OnDateSetListener() {

public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

dateAndTime.set(Calendar.YEAR, year);

dateAndTime.set(Calendar.MONTH, monthOfYear);

dateAndTime.set(Calendar.DAY_OF_MONTH, dayOfMonth);

updateLabel();

} };

(4)

TimePickerDialog.OnTimeSetListener t=

new TimePickerDialog.OnTimeSetListener() {

public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

dateAndTime.set(Calendar.HOUR_OF_DAY, hourOfDay);

dateAndTime.set(Calendar.MINUTE, minute);

updateLabel();

} };

}

Le "modèle" de cette activité est simplement une instance de Calendar initialisée avec la date et l’heure courantes et placée dans la vue via un formateur DateFormat. La méthode updateLabel() prend le Calendar courant, le formate et le place dans le TextView correspondant au label.

Chaque bouton est associé à une méthode qui s’exécutera lorsque l’utilisateur cliquera dessus (chooseDate() et chooseTime()). Lorsqu’on clique sur le bouton, une boîte de dialogue DatePickerDialog ou TimePickerDialog s’affiche. Dans le cas de DatePickerDialog, on lui passe un écouteur OnDateSetListener pour mettre à jour le Calendar avec la nouvelle date (année, mois, jour). On lui passe également la dernière date choisie, en récupérant les valeurs qui se trouvent dans le Calendar.

TimePickerDialog, quant à lui, reçoit un écouteur OnTimeSetListener pour mettre à jour la portion horaire du Calendar ; on lui passe également la dernière heure choisie et true pour indiquer que l’on veut un format sur 24 heures.

Le résultat de cette activité est présenté aux Figures 14.1, 14.2 et 14.3.

Figure 14.1 L’application

ChronoDemo lors de son lancement.

(5)

Figure 14.2

La même application, montrant le dialogue de choix de la date.

Figure 14.3

La même application, montrant le dialogue de choix de l’heure.

(6)

Le temps s’écoule comme un fleuve

Pour afficher l’heure sans autoriser les utilisateurs à la modifier, utilisez les widgets

DigitalClock ou AnalogClock. Il suffit simplement de les placer dans votre layout et de les laisser travailler.

Le fichier main.xml du projet Fancy/Clocks contient ces deux widgets :

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<AnalogClock android:id="@+id/analog"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:layout_alignParentTop="true"

/>

<DigitalClock android:id="@+id/digital"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:layout_below="@id/analog"

/>

</RelativeLayout>

Sans avoir besoin de modifier quoi que ce soit au squelette de code Java produit automatiquement, on obtient l’application présentée à la Figure 14.4.

Figure 14.4 L’application

ClocksDemo.

(7)

Si vous recherchez plutôt un chronomètre, le widget Chronometer est fait pour vous car il permet de mémoriser le temps écoulé à partir d’un point de départ : il suffit de lui dire quand démarrer (start()), s’arrêter (stop()) et, éventuellement, de redéfinir le format de la chaîne de texte affichée. La Figure 14.5 montre le résultat obtenu.

Figure 14.5 La démo de l’API

Views/Chronometer

du SDK Android 2.0.

Prendre la bonne résolution

La classe SeekBar est un widget qui permet à l’utilisateur de saisir une valeur prise dans un intervalle donné. La Figure 14.6 montre ce qu’affiche l’exemple fourni avec le SDK d’Android 2.0.

Figure 14.6 La démo de l’API

Views/SeekBar du SDK Android 2.0.

(8)

L’utilisateur peut faire glisser le curseur ou cliquer sur l’un des deux côtés pour le repositionner. Ce curseur désigne en réalité une valeur dans un intervalle déter- miné – 0 à 100 par défaut, mais vous pouvez modifier la borne supérieure par un appel à setMax(). La méthode getProgress() renvoie la position courante et l’enre- gistrement d’un écouteur à l’aide de setOnSeekBarChangeListener() permet d’être prévenu de toute modification du curseur.

L’exemple RatingBar que nous avons étudié au Chapitre 13 était une variante de ce widget.

Utilisation d’onglets

La philosophie générale d’Android consiste à faire en sorte que les activités soient courtes et agréables. Lorsqu’il y a plus d’informations que ne peut raisonnablement en contenir l’écran (bien que l’on puisse utiliser des barres de défilement), il peut être préférable de produire ces informations supplémentaires par une autre activité, lancée via une Intent, comme on l’explique au Chapitre 22. Cependant, ceci peut être assez compliqué à mettre en place. En outre, il arrive parfois que l’on doive recueillir et traiter beaucoup d’informations en une seule opération.

Dans une interface graphique classique, nous pourrions utiliser des onglets à cette fin, comme un JTabbedPane en Java/Swing. Avec Android, on dispose désormais d’un conteneur TabHost qui fonctionne exactement de la même façon – une por- tion de ce qu’affiche l’activité est liée à des onglets qui, lorsque l’on clique dessus, permettent de passer d’une partie de la vue à une autre. Une activité utilisera par exemple un onglet pour la saisie d’un emplacement et un autre pour afficher cet emplacement sur une carte.

Certains kits de développement considèrent simplement les onglets comme des objets cliquables, permettant de passer d’une vue à l’autre. D’autres les considèrent comme une combinaison de l’élément cliquable et du contenu qui apparaît lorsque l’on clique dessus. Avec Android, les boutons et les contenus des onglets étant des entités distinctes, nous emploierons les termes "bouton de l’onglet" et "contenu de l’onglet" dans cette section.

Les pièces du puzzle

Pour mettre en place des onglets dans une vue, vous avez besoin des widgets et des conteneurs suivants :

TabHost est le conteneur général pour les boutons et les contenus des onglets.

TabWidget implémente la ligne des boutons des onglets, qui contient les labels et, éventuellement, des icônes.

(9)

FrameLayout est le conteneur des contenus des onglets : chaque contenu d’onglet est un fils du FrameLayout.

Cette approche ressemble à celle de XUL de Mozilla : les éléments tabbox, tabs

et tabpanels de XUL correspondent respectivement à TabHost, TabWidget et

FrameLayout.

Voici le fichier de description d’une activité utilisant des onglets, tiré du projet

Fancy/Tab :

<?xml version="1.0" encoding="utf-8"?>

<TabHost xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/tabhost"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<LinearLayout

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<TabWidget android:id="@android:id/tabs"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

/>

<FrameLayout android:id="@android:id/tabcontent"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<AnalogClock android:id="@+id/tab1"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

<Button android:id="@+id/tab2"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:text="Bouton presque aléatoire"

/>

</FrameLayout>

</LinearLayout>

</TabHost>

Vous remarquerez que les éléments TabWidget et FrameLayout sont des fils indirects de TabHost et que l’élément FrameLayout a lui-même un fils représentant les diffé- rents onglets. Ici, il y en a deux : une horloge et un bouton. Dans un scénario plus compliqué, les onglets seraient regroupés dans un conteneur (un LinearLayout, par exemple) avec leurs propres contenus.

Tout câbler

Vous pouvez placer ces widgets dans une Activity normale ou dans une

TabActivity. Cette dernière, comme ListActivity, enveloppe un patron d’inter- face graphique classique (une activité composée entièrement d’onglets) dans une

(10)

sous-classe d’activité. Pour utiliser TabActivity, vous devez donner au TabHost

l’identifiant @android:id/tabhost. Inversement, si vous ne souhaitez pas l’utiliser, vous devez obtenir le TabHost via un appel à findViewById() puis appeler setup()

sur le TabHost avant de faire quoi que ce soit d’autre.

Le reste du code Java doit indiquer au TabHost quelles sont les vues qui représentent les contenus des onglets et à quoi doivent ressembler les boutons de ces onglets. Tout ceci est encapsulé dans des objets TabSpec. On récupère une instance de TabSpec

via la méthode newTabSpec() du TabHost, on la remplit puis on l’ajoute au TabHost

dans le bon ordre.

Les deux méthodes essentielles de TabSpec sont les suivantes :

setContent(), qui permet d’indiquer le contenu de cet onglet. Généralement, il s’agit de l’identifiant android:id de la vue que l’on veut montrer lorsque l’onglet est choisi.

setIndicator(), qui permet de fournir le titre du bouton de l’onglet. Cette méthode est surchargée pour permettre de fournir également un objet Drawable

représentant l’icône de l’onglet.

Notez que les "indicateurs" des onglets peuvent, en fait, être eux-mêmes des vues, ce qui permet de faire mieux qu’un simple label et une icône facultative.

Notez également que vous devez appeler la méthode setup() de l’objet TabHost

avant de configurer les objets TabSpec. Cet appel n’est pas nécessaire si votre acti- vité dérive de la classe de base TabActivity.

Voici, par exemple, le code Java permettant de faire fonctionner les onglets de la section précédente :

package com.commonsware.android.fancy;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TabHost;

public class TabDemo extends Activity { @Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.main);

TabHost tabs=(TabHost)findViewById(R.id.tabhost);

tabs.setup();

TabHost.TabSpec spec=tabs.newTabSpec("tag1");

(11)

spec.setContent(R.id.tab1);

spec.setIndicator("Heure");

tabs.addTab(spec);

spec=tabs.newTabSpec("tag2");

spec.setContent(R.id.tab2);

spec.setIndicator("Bouton");

tabs.addTab(spec);

tabs.setCurrentTab(0);

} }

On retrouve notre TabHost via un appel à la méthode findViewById(), puis on appelle sa méthode setup(). On crée ensuite une instance de TabSpec via un appel à newTabSpec() auquel on passe un marqueur dont le but est encore inconnu. On appelle les méthodes setContent() et setIndicator() de cette instance, puis la méthode addTab() de l’objet TabHost pour lui ajouter le TabSpec. Enfin, on choi- sit l’onglet qui s’affichera initialement, à l’aide de la méthode setCurrentTab() (la valeur 0 désigne le premier onglet).

Les Figures 14.7 et 14.8 montrent ce qu’affiche cette application.

Figure 14.7

L’application TabDemo

affichant son premier onglet.

(12)

Figure 14.8 La même application affichant son second onglet.

Ajouts dynamiques

TabWidget est configuré pour simplifier la définition des onglets au moment de la compilation. Cependant, vous voudrez parfois créer des onglets au cours de l’exécu- tion de votre activité. Imaginons, par exemple, un client de courrier où les différents e-mails s’ouvrent dans leurs propres onglets, afin de faciliter le passage d’un mes- sage à l’autre. Dans cette situation, vous ne pouvez pas savoir à l’avance le nombre d’onglets ni leur contenu : vous devez attendre que l’utilisateur ouvre un message de courrier. Heureusement, Android permet également d’ajouter dynamiquement des onglets en cours d’exécution.

Cet ajout fonctionne exactement comme on vient de le voir, sauf qu’il faut utiliser une autre variante de setContent() qui prend en paramètre une instance de TabHost,

TabContentFactory, qui est simplement une méthode de rappel qui sera appelée automatiquement : il suffit de fournir une implémentation de createTabContent()

et de l’utiliser pour construire et renvoyer la vue qui deviendra le contenu de l’onglet.

Cette approche est présentée dans le projet Fancy/DynamicTab.

La description de l’interface de l’activité met en place les onglets et n’en définit qu’un seul, contenant un simple bouton :

<?xml version="1.0" encoding="utf-8"?>

<TabHost xmlns:android="http://schemas.android.com/apk/res/android"

(13)

android:id="@+id/tabhost"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<LinearLayout

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<TabWidget android:id="@android:id/tabs"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

/>

<FrameLayout android:id="@android:id/tabcontent"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<Button android:id="@+id/buttontab"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:text="Bouton presque aléatoire"

android:onClick="addTab"

/>

</FrameLayout>

</LinearLayout>

</TabHost>

Nous voulons maintenant ajouter de nouveaux onglets à mesure que l’on clique sur ce bouton, ce qui se réalise en quelques lignes de code :

package com.commonsware.android.dynamictab;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.AnalogClock;

import android.widget.TabHost;

public class DynamicTabDemo extends Activity { private TabHost tabs=null;

@Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.main);

tabs=(TabHost)findViewById(R.id.tabhost);

tabs.setup();

TabHost.TabSpec spec=tabs.newTabSpec("buttontab");

spec.setContent(R.id.buttontab);

spec.setIndicator("Bouton");

tabs.addTab(spec);

}

(14)

public void addTab(View v) {

TabHost.TabSpec spec=tabs.newTabSpec("tag1");

spec.setContent(new TabHost.TabContentFactory() { public View createTabContent(String tag) { return(new AnalogClock(DynamicTabDemo.this));

} });

spec.setIndicator("Heure");

tabs.addTab(spec);

} });

} }

On crée un objet TabHost.TabSpec dans la méthode de rappel addTab() de notre bouton en lui passant en paramètre une fabrique TabHost.TabContentFactory ano- nyme. Cette fabrique, à son tour, renvoie la vue qui sera utilisée pour l’onglet – ici un AnalogClock. Le code de construction de cette vue pourrait être bien plus éla- boré et utiliser, par exemple, un LayoutInflater pour créer une vue à partir d’un fichier de description XML.

La Figure  14.9 montre que l’activité n’affiche qu’un seul onglet lorsqu’elle est lancée. La Figure 14.10 montre trois onglets, créés en cours d’exécution.

Figure 14.9 L’application

DynamicTabDemo avec son unique onglet initial.

(15)

Figure 14.10 La même application, après la création de trois onglets en cours d’exécution.

Tout faire basculer

Parfois, on souhaite bénéficier de l’avantage des onglets (ne voir que certaines vues à la fois) sans pour autant utiliser leur présentation graphique (parce que, par exemple, les onglets prennent trop de place à l’écran). On peut ainsi préférer passer d’une vue à l’autre par un mouvement du doigt sur l’écran ou en secouant le terminal.

La bonne nouvelle est que le mécanisme interne des onglets pour basculer entre les vues est disponible dans le conteneur ViewFlipper, qui peut être utilisé différemment d’un onglet traditionnel.

ViewFlipper hérite de FrameLayout, que nous avons utilisé plus haut pour décrire le fonctionnement interne d’un TabWidget. Cependant, il ne montre que la première vue fille au départ : c’est à vous qu’il appartient de mettre en place le basculement entre les vues, soit manuellement par une action de l’utilisateur, soit automatiquement par un timer.

Voici, par exemple, le fichier de description du projet Fancy/Flipper1, qui utilise un

Button et un ViewFlipper :

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

(16)

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<Button android:id="@+id/flip_me"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Bascule–moi !"

android:onClick="flip"

/>

<ViewFlipper android:id="@+id/details"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:textStyle="bold"

android:textColor="#FF00FF00"

android:text="Premier panneau"

/>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:textStyle="bold"

android:textColor="#FFFF0000"

android:text="Second panneau"

/>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:textStyle="bold"

android:textColor="#FFFFFF00"

android:text="Troisieme panneau"

/>

</ViewFlipper>

</LinearLayout>

Ce layout définit trois vues filles de ViewFlipper, chacune étant un TextView

contenant un simple message. Vous pourriez évidemment choisir des vues plus complexes.

Pour basculer manuellement entre les vues, nous devons ajouter un écouteur au bouton pour que le basculement ait lieu lorsqu’on clique dessus :

package com.commonsware.android.flipper1;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.ViewFlipper;

(17)

public class FlipperDemo extends Activity { ViewFlipper flipper;

@Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.main);

flipper=(ViewFlipper)findViewById(R.id.details);

}

public void flip(View v) { flipper.showNext();

} }

Il s’agit simplement d’appeler la méthode showNext() de ViewFlipper, comme pour n’importe quelle classe ViewAnimator. Le résultat est une activité très simple : un clic sur le bouton fait apparaître le TextView suivant, en rebouclant sur le premier lorsqu’ils se sont tous affichés (voir Figures 14.11 et 14.12).

Figure 14.11

L’application Flipper1

montant le premier panneau.

(18)

Figure 14.12 La même application, après basculement vers le second panneau.

Nous pourrions bien sûr gérer tout cela plus simplement en utilisant un seul TextView

et en modifiant son texte et sa couleur à chaque clic. Cependant, vous pouvez imagi- ner que le contenu du ViewFlipper pourrait être bien plus compliqué – inclure, par exemple, tout ce que l’on peut mettre dans un TabView.

Comme pour un TabWidget, le contenu d’un ViewFlipper peut ne pas être connu lors de la compilation et, comme pour un TabWidget, il est relativement simple d’ajouter du contenu à la volée.

Voici, par exemple, le layout d’une autre activité, celle du projet Fancy/Flipper2 :

<?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"

>

<ViewFlipper android:id="@+id/details"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

</ViewFlipper>

</LinearLayout>

(19)

Vous remarquerez que l’élément ViewFlipper n’a aucun contenu au moment de la compilation. Notez également qu’il n’y a pas de bouton pour basculer entre les contenus – nous reviendrons sur ce point dans un instant.

Pour le contenu du ViewFlipper, nous créerons de gros boutons contenant, chacun, un ensemble de mots quelconques. Nous configurerons également le ViewFlipper

pour qu’il boucle automatiquement sur ces widgets Button : package com.commonsware.android.flipper2;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.ViewFlipper;

public class FlipperDemo2 extends Activity {

static String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus"};

ViewFlipper flipper;

@Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.main);

flipper=(ViewFlipper)findViewById(R.id.details);

for (String item : items) { Button btn=new Button(this);

btn.setText(item);

flipper.addView(btn,

new ViewGroup.LayoutParams(

ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));

}

flipper.setFlipInterval(2000);

flipper.startFlipping();

} }

(20)

Après avoir parcouru tous les mots en les transformant en autant de boutons fils de l’objet ViewFlipper, nous configurons ce dernier pour qu’il bascule auto- matiquement entre ses fils (flipper.setFlipInterval(2000);) et nous lançons le basculement (flipper.startFlipping();).

Le résultat est une suite sans fin de boutons qui apparaissent puis sont remplacés à chaque fois par le bouton suivant de la séquence après un délai de 2 secondes (voir Figure 14.13). L’ensemble revient au premier bouton après la disparition du dernier.

Figure 14.13

L’application Flipper2.

Ce basculement automatique est utile pour les panneaux d’information ou les autres situations dans lesquelles vous voulez afficher beaucoup d’informations dans un espace réduit. Ces différentes vues basculant automatiquement de l’une à l’autre, il serait risqué de demander aux utilisateurs d’interagir avec elles – une vue pourrait disparaître au milieu d’une interaction.

Fouiller dans les tiroirs

Depuis longtemps, les développeurs Android réclamaient un conteneur de type tiroir, fonctionnant comme celui de l’écran d’accueil, qui contient les icônes pour lancer les applications. L’implémentation officielle existait dans le code open-source mais n’était pas intégrée dans le SDK... jusqu’à Android 1.5, qui a fourni le widget

SlidingDrawer.

(21)

À la différence de la plupart des autres conteneurs, SlidingDrawer change d’aspect puisqu’il passe d’une position fermée à une position ouverte. Cette caractéris- tique implique quelques restrictions sur le conteneur dans lequel peut se trouver le SlidingDrawer puisqu’il doit permettre à plusieurs widgets de cohabiter les uns au-dessus des autres. RelativeLayout et FrameLayout satisfont cette exigence –

FrameLayout est un conteneur conçu spécialement pour empiler les widgets les uns sur les autres. LinearLayout, en revanche, ne permet pas d’empiler des widgets (ils sont placés les uns après les autres, en ligne ou en colonne) –, c’est la raison pour laquelle un SlidingDrawer ne doit pas être un fils direct d’un élément LinearLayout. Voici un exemple tiré du projet Fancy/DrawerDemo, avec un SlidingDrawer placé dans un FrameLayout :

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="#FF4444CC"

>

<SlidingDrawer

android:id="@+id/drawer"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:handle="@+id/handle"

android:content="@+id/content">

<ImageView

android:id="@id/handle"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:src="@drawable/tray_handle_normal"

/>

<Button

android:id="@id/content"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:text="Je suis dedans !"

/>

</SlidingDrawer>

</FrameLayout>

Le SlidingDrawer doit contenir :

une poignée – le plus souvent un ImageView, comme ici ;

le contenu du tiroir lui-même – généralement un conteneur, bien qu’ici nous utilisions un bouton.

En outre, SlidingDrawer doit connaître les valeurs android:id de la poignée et du contenu en les stockant, respectivement, dans ses attributs android:handle

et android:content. Ceci permet au tiroir de savoir comment s’animer lorsqu’il s’ouvre ou se ferme.

(22)

La Figure  14.14 montre l’aspect du tiroir fermé, avec la poignée qu’on lui a f ournie. La Figure 14.15 montre le tiroir ouvert, avec son contenu.

Figure 14.14 L’application

DrawerDemo avec son tiroir fermé.

Figure 14.15 La même application avec le tiroir ouvert.

Comme on pourrait s’y attendre, on peut ouvrir et refermer le tiroir à partir du code Java ou par des événements de "touchés" utilisateurs. Il existe deux groupes de

(23)

méthodes : les premières agissent instantanément (open(), close() et toggle()), les autres utilisent une animation (animateOpen(), animateClose() et animateToggle()).

Le tiroir se verrouille avec lock() et se déverrouille avec unlock() ; lorsqu’il est verrouillé, le tiroir ne répond pas aux touchés sur l’écran.

Vous pouvez également, si vous le souhaitez, enregistrer trois types de méthodes de rappel :

un écouteur qui sera appelé lors de l’ouverture du tiroir ;

un écouteur qui sera appelé lors de la fermeture du tiroir ;

un écouteur qui sera appelé lorsque le tiroir "défile" (c’est-à-dire lorsque l’utili- sateur tire ou repousse la poignée).

Le SlidingDrawer du lanceur, par exemple, change l’icône de sa poignée pour qu’elle signifie "ouvrir", "fermer" ou "supprimer" (lorsque l’on touche pendant un certain temps une icône du bureau). Pour ce faire, il utilise notamment des méthodes de rappel comme celles que nous venons de citer.

SlidingDrawer peut être vertical ou horizontal. Cependant, cette orientation reste identique quelle que soit celle de l’écran : en d’autres termes, si vous faites pivoter le terminal ou l’émulateur pendant qu’il exécute DrawerDemo, le tiroir s’ouvrira tou- jours en partant du bas – il ne "colle" pas toujours au bord par rapport à celui auquel il s’est ouvert. Pour que le tiroir s’ouvre toujours du même côté, comme le lanceur, vous aurez besoin de layouts différents pour le mode portrait et le mode paysage – un sujet que nous aborderons au Chapitre 23.

Autres conteneurs intéressants

Android fournit également le conteneur AbsoluteLayout, dont le contenu est dis- posé en fonction de coordonnées spécifiques – on lui indique où placer un fils en précisant ses coordonnées X, Y, et Android le positionne à cet endroit sans poser de question.

Ceci a l’avantage de fournir un positionnement précis ; en revanche, cela signifie également que les vues n’auront un aspect correct que sur des écrans d’une cer- taine dimension, à moins d’écrire beaucoup de code pour ajuster les coordonnées en fonction de la taille de l’écran. Les écrans Android pouvant avoir n’importe quelle taille et ces tailles évoluant continuellement, l’utilisation d’AbsoluteLayout risque de devenir assez problématique.

Note

Officiellement, AbsoluteLayout est considéré comme obsolète : bien qu’il reste dispo- nible, il est désormais déconseillé de l’utiliser.

(24)

Android dispose également d’une nouvelle variante de liste, ExpandableListView, qui fournit une représentation arborescente simplifiée autorisant deux niveaux de profondeur : les groupes et les fils. Les groupes contiennent les fils, qui sont les

"feuilles" de l’arbre. Cette liste nécessite un nouvel ensemble d’adaptateurs car la famille ListAdapter ne fournit aucune information de groupage pour les éléments d’une liste.

Voici quelques autres widgets disponibles :

CheckedTextView, un TextView pouvant avoir à côté de lui une case à cocher ou un bouton radio. Il est utilisé dans les listes à choix unique ou à choix multiples.

MultiAutoCompleteTextView. Comme AutoCompleteTextView, mais l’utilisateur a le choix entre plusieurs propositions apparaissant dans une liste déroulante.

QuickContactBadge. Affiche une liste d’icônes représentant les actions pouvant s’appliquer à un contact choisi dans le carnet d’adresses de l’utilisateur (appel téléphonique, envoi d’un SMS, envoi d’un e-mail, etc.).

ToggleButton. Bouton qui a deux états indiqués par une "lumière" et les mots

"ON"/"OFF".

ViewSwitcher (et ses sous-classes ImageSwitcher et TextSwitcher). ViewFlipper

simplifié pour basculer entre deux vues.

Références

Documents relatifs

☐ Il n'a pas été repéré d'anomalie ☒ Il a été repéré une ou des anomalies : Un des équipements n’est ne sont pas raccordés ☒ Des eaux usées se déversent dans le

Elopak veut croquer SIG pour rivaliser avec Tetra Pak P.38 MACHINISMEAGRICOLE. Le

En utilisant la notion de densité, écrire un texte court pour expliquer pourquoi il est possible d’utiliser un matériau qui coule habituellement pour construire

NOTA : Cas de « stop » : Prospero stopped to smoke (he stopped in order to smoke ; il s'est arrêté dans le but de/pour fumer) # Prospero stopped smoking (il s'est arrêté de

A chaque commande de Staltolight ® , un plan de pose détaillé ainsi qu'un guide de pose vous seront transmis.

Kobayashi et ses collaborateurs (5-6) utilisent avec succès des sérums de cobaye dilués en solution d’Alsever comme complément de l’épreuve de lyse, et c’est à la suite

Le pavage obtenu page précédente à l’aide de rotations peut être interprété comme un pavage obtenu en utilisant des symétries

Ces fondamentaux acquis, Renault poursuit ses efforts et les intensifie pour améliorer encore la qualité perçue de Nouveau Kangoo.. MCA (Maubeuge Construction Automobile), une usine