Vous êtes sur la page 1sur 391

Lart du dveloppement

Android

Mark Murphy
customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page I Dimanche, 8. novembre 2009 12:23 12

L E

P R O G R A M M E U R

Lart
du dveloppement
Android
Mark L. Murphy

Traduit par ric Jacoboni,


avec la contribution d'Arnaud Farine

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page II Dimanche, 8. novembre 2009 12:23 12

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces
personnes qui pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions
thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle.
Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices
ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou
programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.
Le logo reproduit en page de couverture et sur les ouvertures de chapitres a t cr par Irina
Blok pour le compte de Google sous la licence Creative Commons 3.0 Attribution License
http://creativecommons.org/licenses/by/3.0/deed.fr.

Publi par Pearson Education France


47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00
www.pearson.fr

Titre original : Beginning Android

Traduit par ric Jacoboni,


avec la contribution d'Arnaud Farine

Mise en pages : TyPAO


ISBN : 978-2-7440-4094-8
Copyright 2009 Pearson Education France
Tous droits rservs

ISBN original : 978-1-4302-2419-8


Copyright 2009 by Mark L. Murphy
All rights reserved
dition originale publie par
Apress,
2855 Telegraph Avenue, Suite 600,
Berkeley, CA 94705 USA

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la
proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le
respect des modalits prvues larticle L. 122-10 dudit code.
No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means, electronic, mechanical,
photocopying, recording, or otherwise, without written permission from the publisher.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page III Dimanche, 8. novembre 2009 12:23 12

Sommaire

propos de lauteur ..............................

IX

14. Affichage de messages surgissant ...

155

Remerciements .......................................

XI

15. Utilisation des threads .....................

161

Prface ldition franaise ..................

XIII

Introduction ............................................

16. Gestion des vnements du cycle


de vie dune activit ........................

173

Partie I Concepts de base ...................

1. Tour dhorizon ...................................

Partie III Stockage de donnes,


services rseaux et API ..........................

177

2. Structure dun projet .........................

17. Utilisation des prfrences ..............

179

3. Contenu du manifeste ........................

13

18. Accs aux fichiers ............................

191

19. Utilisation des ressources ................

199

20. Accs et gestion des bases


de donnes locales ...........................

217

21. Tirer le meilleur parti


des bibliothques Java ....................

227

22. Communiquer via Internet .............

235

Partie II Les activits ..........................

19

4. Cration dun squelette dapplication

21

5. Utilisation des layouts XML .............

29

6. Utilisation des widgets de base .........

35

7. Conteneurs .........................................

45

8. Widgets de slection ...........................

65

Partie IV - Intentions (Intents) .............

241

9. Samuser avec les listes ......................

83

23. Cration de filtres dintentions .......

243

10. Utiliser de jolis widgets


et de beaux conteneurs ....................

107

24. Lancement dactivits


et de sous-activits ...........................

249

11. Utilisation des menus .......................

129

12. Polices de caractres ........................

141

25. Trouver les actions possibles grce


lintrospection ...............................

259

13. Intgrer le navigateur de WebKit ...

147

26. Gestion de la rotation ......................

265

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page IV Dimanche, 8. novembre 2009 12:23 12

IV

Lart du dveloppement Android

Partie V Fournisseurs
de contenus et services ..........................

277

27. Utilisation dun fournisseur


de contenu (content provider) .........

279

28. Construction dun fournisseur


de contenu ........................................

287

29. Demander et exiger des permissions

297

30. Cration dun service ......................

303

31. Appel dun service ...........................

309

32. Alerter les utilisateurs


avec des notifications ......................

313

Partie VI Autres fonctionnalits


dAndroid ...............................................

319

33. Accs aux services de localisation ...

321

34. Cartographie avec MapView


et MapActivity .................................

327

35. Gestion des appels tlphoniques ...

337

36. Recherches avec SearchManager ...

341

37. Outils de dveloppement .................

351

38. Pour aller plus loin ..........................

363

Index .......................................................

367

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page V Dimanche, 8. novembre 2009 12:23 12

Table des matires

Partie II Les activits .........................

19

4. Cration dun squelette dapplication

21

propos de lauteur ..............................

XI

Remerciements .......................................

XIII

Prface ldition franaise ..................

XV

Terminaux virtuels et cibles ...............

21

Introduction ............................................

Commencer par le dbut .....................

23

Bienvenue ! .........................................
Prrequis .............................................
ditions de ce livre .............................
Termes dutilisation du code source ...

1
1
2
2

Lactivit .............................................

24

Dissection de lactivit .......................

25

Compiler et lancer lactivit ...............

27

5. Utilisation des layouts XML .............

29

Partie I Concepts de base ...................

Quest-ce quun positionnement XML ?

29

1. Tour dhorizon ...................................

Pourquoi utiliser des layouts XML ? ..

30

Contenu dun programme Android .....


Fonctionnalits votre disposition .....

6
8

Contenu dun fichier layout ................

31

Identifiants des widgets ......................

32

2. Structure dun projet .........................

Contenu de la racine ..........................


la sueur de votre front .....................
La suite de lhistoire ...........................
Le fruit de votre travail .......................

9
10
11
11

Utilisation des widgets


dans le code Java ................................

32

Fin de lhistoire ..................................

33

6. Utilisation des widgets de base .........

35

Labels .................................................

35

3. Contenu du manifeste ........................

13

Boutons ...............................................

36

Au dbut, il y avait la racine ...............


Permissions, instrumentations
et applications .....................................
Que fait votre application ? .................
Faire le minimum ................................
Version = contrle ...............................

14

Images ................................................

37

Champs de saisie ................................

38

14
15
16
17

Cases cocher ....................................

40

Boutons radio .....................................

42

Rsum ...............................................

43

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page VI Dimanche, 8. novembre 2009 12:23 12

VI

Lart du dveloppement Android

7. Conteneurs .........................................

45

Chargement immdiat .........................

150

Penser de faon linaire .....................


Tout est relatif ....................................
Tabula Rasa ........................................
ScrollView ..........................................

46
52
57
61

Navigation au long cours ....................

151

Amuser le client ..................................

151

Rglages, prfrences et options ........

153

14. Affichage de messages surgissant ...

155

8. Widgets de slection ..........................

65

Les toasts ............................................

156

66
67
71
73
77
81

Les alertes ...........................................

156

Mise en uvre ....................................

157

15. Utilisation des threads .....................

161

Les handlers ........................................

162

Excution sur place .............................

165

O est pass le thread de mon interface


utilisateur ? .........................................

165

Dsynchronisation ..............................

166

viter les piges ..................................

172

16. Gestion des vnements du cycle


de vie dune activit ..........................

173

Sadapter aux circonstances ...............


Listes des bons et des mchants .........
Contrle du Spinner .........................
Mettez vos lions en cage ....................
Champs : conomisez 35 % de la frappe !
Galeries ..............................................
9. Samuser avec les listes .....................

83

Premires tapes .................................


Prsentation dynamique .....................
Mieux, plus robuste et plus rapide .....
Crer une liste... .................................
Et la vrifier deux fois ...................
Adapter dautres adaptateurs ..............

83
85
88
94
99
105

10. Utiliser de jolis widgets


et de beaux conteneurs ....................

107

Choisir ................................................
Le temps scoule comme un fleuve ..
Mesurer la progression .......................
Utilisation donglets ...........................
Tout faire basculer ..............................
Fouiller dans les tiroirs .......................
Autres conteneurs intressants ...........

107
111
112
113
120
125
128

Lactivit de Schroedinger ..................

174

Vie et mort dune activit ...................

174

Partie III Stockage de donnes,


services rseaux et API ..........................

177

17. Utilisation des prfrences ................

179

Obtenir ce que vous voulez .................

179

Dfinir vos prfrences .......................

180

Un mot sur le framework ....................

180

Laisser les utilisateurs choisir .............

181

Ajouter un peu de structure ................

185

Botes de dialogue ..............................

187

18. Accs aux fichiers .............................

191

Allons-y ! ............................................

191

11. Utilisation des menus ......................

129

Variantes de menus ............................


Les menus doptions ..........................
Menus contextuels .............................
Illustration rapide ...............................
Encore de linflation ...........................

130
130
131
132
137

Lire et crire .......................................

195

19. Utilisation des ressources ................

199

12. Polices de caractres .......................

141

Les diffrents types de ressources ......

199

Sachez apprcier ce que vous avez ....


Le problme des glyphes ...................

141
144

Thorie des chanes ............................

200

Vous voulez gagner une image ? ........

205

Les ressources XML ...........................

207

13. Intgrer le navigateur


de WebKit .........................................

147

Valeurs diverses ..................................

210

Un navigateur, et en vitesse ! .............

147

Grer la diffrence ..............................

212

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page VII Dimanche, 8. novembre 2009 12:23 12

Table des matires

20. Accs et gestion des bases


de donnes locales .............................

217

Prsentation rapide de SQLite ............

218

Commencer par le dbut .....................

219

Mettre la table .....................................

219

Ajouter des donnes ............................

220

Le retour de vos requtes ....................

221

Des donnes, des donnes,


encore des donnes .............................

224

21. Tirer le meilleur parti


des bibliothques Java ......................

227

Limites extrieures ..............................

228

Ant et JAR ..........................................

228

Suivre le script ....................................

229

Tout fonctionne... enfin, presque ........

233

Relecture des scripts ...........................

233

22. Communiquer via Internet .............

235

REST et relaxation ..............................

236

Partie IV Intentions (Intents) ............

241

23. Cration de filtres dintentions .......

243

Quelle est votre intention ? .................

244

Dclarer vos intentions .......................


Rcepteurs dintention ........................

Rotation maison ..................................


Forcer le destin ...................................
Tout comprendre .................................

272
274
276

Partie V Fournisseurs de contenus


et services ................................................

277

27. Utilisation dun fournisseur de contenu


(content provider) .............................. 279
Composantes dune Uri ....................
Obtention dun descripteur .................
Cration des requtes .........................
Sadapter aux circonstances ...............
Gestion manuelle des curseurs ...........
Insertions et suppressions ...................
Attention aux BLOB ! ........................

280
280
281
282
283
284
285

28. Construction dun


fournisseur de contenu .....................

287

245

Dabord, une petite dissection ............


Puis un peu de saisie ...........................
tape n 1 : crer une classe Provider
tape n 2 : fournir une Uri ..............
tape n 3 : dclarer les proprits .....
tape n 4 : modifier le manifeste ......
Avertissements en cas de modifications

288
288
289
294
295
295
296

247

29. Demander et exiger des permissions

297

Attention la pause ............................

247

24. Lancement dactivits


et de sous-activits .............................

249

Mre, puis-je ? ....................................


Halte ! Qui va l ? ...............................
Vos papiers, sil vous plat ! ...............

298
299
301

Activits paires et sous-activits .........

250

30. Cration dun service ......................

303

Dmarrage ..........................................

250

Navigation avec onglets ......................

255

25. Trouver les actions possibles grce


lintrospection ................................

259

Service avec classe .............................


Il ne peut en rester quun ! .................
Destine du manifeste ........................
Sauter la clture ..................................

304
305
306
306

Faites votre choix ................................

260

31. Appel dun service ...........................

309

Prfrez-vous le menu ? ......................

263

Demander lentourage ......................

264

Transmission manuelle .......................


Capture de lintention .........................

310
311

26. Gestion de la rotation ......................

265

Philosophie de la destruction ..............

265

32. Alerter les utilisateurs


avec des notifications ........................

313

Types davertissements .......................


Les avertissements en action ..............

313
315

Tout est pareil, juste diffrent .............

266

Il ny a pas de petites conomies ! ......

270

VII

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page VIII Dimanche, 8. novembre 2009 12:23 12

VIII

Lart du dveloppement Android

Partie VI Autres fonctionnalits


dAndroid ...............................................

319

33. Accs aux services


de localisation ...................................

321
322
322
324

Fournisseurs de localisation :
ils savent o vous vous cachez ...........
Se trouver soi-mme ..........................
Se dplacer .........................................
Est-on dj arriv ? Est-on dj arriv ?
Est-on dj arriv ? ............................
Tester... Tester... ..................................

325
326

34. Cartographie avec MapView


et MapActivity .................................

327

Termes dutilisation ...........................


Empilements ......................................
Les composants essentiels .................
Testez votre contrle ..........................
Terrain accident ................................
Couches sur couches ..........................

328
328
328
330
331
332

Moi et MyLocationOverlay ..............


La cl de tout ......................................

334
335

35. Gestion des appels tlphoniques ...

337

Le Manager .........................................
Appeler ...............................................

338
338

36. Recherches avec SearchManager ...

341

La chasse est ouverte ..........................


Recherches personnelles .....................
Effectuer une recherche ......................

342
343
349

37. Outils de dveloppement .................

351

Gestion hirarchique ........................... 351


DDMS (Dalvik Debug Monitor Service) 356
Gestion des cartes amovibles .............. 362
38. Pour aller plus loin ..........................

363

Questions avec, parfois, des rponses .


Aller la source ..................................
Lire les journaux .................................

363
364
365

Index .......................................................

367

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page XI Dimanche, 8. novembre 2009 12:23 12

propos de lauteur

Mark Murphy est le fondateur de CommonsWare et lauteur de The Busy Coders Guide
to Android Development. Son exprience professionnelle va du conseil pour les dveloppements open-source et collaboratifs de trs grosses socits au dveloppement dapplications sur peu prs tout ce qui est plus petit quun mainframe. Il programme depuis
plus de vingt-cinq ans et a travaill sur des plates-formes allant du TRS-80 aux derniers
modles de terminaux mobiles. En tant quorateur averti, Mark intervient galement sur
un grand nombre de sujets dans de nombreuses confrences et sessions de formation
internationales.
Par ailleurs, Mark est le rdacteur des rubriques "Building Droids" dAndroidGuys et
"Android Angle" de NetworkWorld.
En dehors de CommonsWare, Mark sintresse beaucoup au rle que joue Internet dans
limplication des citoyens dans la politique. ce titre, il publie des articles dans la collection
Rebooting America et son blog personnel contient de nombreux articles sur la "dmocratie
cooprative".

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page XII Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page XIII Dimanche, 8. novembre 2009 12:23 12

Remerciements

Je voudrais remercier lquipe dAndroid ; non seulement pour avoir cr un bon produit,
mais galement pour laide inestimable quelle fournit dans les groupes Google consacrs
ce systme. Merci notamment Romain Guy, Justin Mattson, Dianne Hackborn,
Jean-Baptiste Queru, Jeff Sharkey et Xavier Ducrohet.
Les icnes utilises dans les exemples de ce livre proviennent du jeu dicnes Nuvola 1.

1. http://www.icon-king.com/?p=15.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page XIV Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page XV Dimanche, 8. novembre 2009 12:23 12

Prface
ldition franaise

Novembre 2007, Google, le gant amricain de l'Internet annonce qu'il vient de crer un
nouveau systme d'exploitation pour appareil mobile. Nomm Android, ce systme est
totalement gratuit et open-source, pour les dveloppeurs d'applications, mais galement
pour les constructeurs d'appareils mobiles (tlphones, smartphones, MID [Multimedia
Interface Device], GPS...).
Un an auparavant, Apple avait lanc un pav dans la mare en s'introduisant avec brio dans
le march des systmes d'exploitation mobiles, dtenus plus de 50 % par Symbian, grce
l'apport de nombreuses fonctionnalits multimdia et de localisation dans une interface
utilisateur trs intuitive.
Android apporte galement son lot de nouvelles fonctionnalits, mais avec des atouts
supplmentaires : gratuit, code ouvert, langage de programmation approuv et connu de
millions de dveloppeurs travers le monde. Autour d'une alliance d'une quarantaine
de constructeurs, diteurs logiciels et socits spcialises dans les applications mobiles
(Open Handset Alliance), Google a de quoi inquiter les systmes d'exploitation propritaires tels que Windows, Palm, Samsung, RIM, Symbian.
Certains dveloppeurs ont immdiatement cru Android, ds son annonce par Google, et
des blogs, des sites Internet et des tutoriaux ont rapidement vu le jour de par le monde. Ce
phnomne a t encourag par un concours de dveloppement l'initiative de Google
avec plus de 1 million de dollars de lots. Le but tait bien entendu de faire parler de son
nouveau systme : pari russi ! En 4 mois, alors que la documentation de la plateforme
tait quasiment inexistante, les juges du concours ont reu pas moins de 1 700 applications !
Ceux qui ont cru en Android ne se sont pas tromps sur son potentiel ni sur celui des
nouveaux services mobiles mergents ! Un an plus tard, tous les grands constructeurs de
tlphonie mobile annonaient un ou plusieurs smartphones quips de cette plateforme,

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page XVI Dimanche, 8. novembre 2009 12:23 12

XVI

Lart du dveloppement Android

sans compter sur l'arrive de nouveaux constructeurs. Aujourd'hui, le portail de vente de


Google ddi aux applications Android (Android Market) dispose d'environ 7 000 applications (alors mme qu'il n'est toujours pas disponible sur tous les continents). Enfin, plus
d'une vingtaine d'appareils quips d'Android sont attendus avant la fin de l'anne 2009
(Sony, Samsung, Motorola, HTC...), et davantage encore en 2010.
Mark Murphy, auteur du prsent ouvrage, est l'un de ces dveloppeurs qui a cru Android
ds ses premires heures. Prsent sur les groupes de discussions Google, il apportait son
aide aux programmeurs en qute d'informations. C'est d'ailleurs dans ce contexte que j'ai
fait sa connaissance. Convaincu du potentiel de la plateforme, il s'est attel la rdaction
d'un livre de rfrence sur le dveloppement Android. Le faisant voluer au fur et mesure
des nouvelles versions (4 en 2008), Mark a rellement construit un ouvrage de qualit.
Toutes les bases de la programmation sous Android y sont dcrites, mais pas uniquement.
Cet ouvrage regorge d'astuces et de conseils qui vous permettront de pouvoir raliser vos
premires applications Android mais aussi, pour des dveloppements avancs, de trouver
facilement le bout de code qui vous intresse. Un ouvrage ddi aux dbutants comme aux
dveloppeurs plus avancs.
Arnaud Farine
eXpertise @ndroid
http://www.expertiseandroid.com/

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 1 Dimanche, 8. novembre 2009 12:23 12

Introduction

Bienvenue !
Merci de votre intrt pour le dveloppement dapplications Android ! De plus en plus de
personnes accdent dsormais aux services Internet via des moyens "non traditionnels"
comme les terminaux mobiles, et ce nombre ne peut que crotre. Bien quAndroid soit
rcent ses premiers terminaux sont apparus la fin de 2008 , il ne fait aucun doute quil
se rpandra rapidement grce linfluence et limportance de lOpen Handset Alliance.
Merci surtout de votre intrt pour ce livre ! Jespre sincrement que vous le trouverez
utile, voire divertissant par moments.

Prrequis
Pour programmer des applications Android, vous devez au moins connatre les bases de
Java. En effet, la programmation Android utilise la syntaxe de ce langage, plus une bibliothque de classes sapparentant un sous-ensemble de la bibliothque de Java SE (avec
des extensions spcifiques). Si vous navez jamais programm en Java, initiez-vous ce
langage avant de vous plonger dans la programmation Android.
Ce livre nexplique pas comment tlcharger ou installer les outils de dveloppement
Android, que ce soit le plugin Eclipse ou les outils ddis. Vous trouverez toutes ces informations sur le site web dAndroid1. Tout ce qui est expliqu dans cet ouvrage ne tient pas
compte de lenvironnement que vous utilisez et fonctionnera dans tous les cas. Nous vous
conseillons de tlcharger, dinstaller et de tester ces outils de dveloppement avant
dessayer les exemples de ce livre.
1. http://code.google.com/android/index.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 2 Dimanche, 8. novembre 2009 12:23 12

Lart du dveloppement Android

ditions de ce livre
Cet ouvrage est le fruit dune collaboration entre Apress et CommonsWare. La version que
vous tes en train de lire est la traduction de ldition dApress, qui est disponible sous
forme imprime ou numrique.
De son ct, CommonsWare met continuellement jour le contenu original et le met
disposition des membres de son programme Warescription sous le titre The Busy Coders
Guide to Android Development.
La page http://commonsware.com/apress contient une FAQ concernant ce partenariat
avec Apress.

Termes dutilisation du code source


Le code source des exemples de ce livre est disponible partir du site web de Pearson
(www.pearson.fr), sur la page consacre cet ouvrage. Tous les projets Android sont
placs sous les termes de la licence Apache 2.01, que nous vous invitons lire avant de
rutiliser ces codes.

1. http://www.apache.org/licenses/LICENSE-2.0.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 3 Dimanche, 8. novembre 2009 12:23 12

Partie

Concepts de base
CHAPITRE 1.

Tour dhorizon

CHAPITRE 2.

Structure dun projet

CHAPITRE 3.

Contenu du manifeste

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 4 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 5 Dimanche, 8. novembre 2009 12:23 12

1
Tour dhorizon
Les terminaux Android seront essentiellement des tlphones mobiles, bien quil soit
question dutiliser cette technologie sur dautres plates-formes (comme les tablettes PC).
Pour les dveloppeurs, ceci a des avantages et des inconvnients.
Du ct des avantages, les tlphones Android qui arrivent sur le march sont assez jolis.
Si loffre de services Internet pour les mobiles remonte au milieu des annes 1990, avec
HDML (Handheld Device Markup Language), ce nest que depuis ces dernires annes
que lon dispose de tlphones capables dexploiter pleinement laccs Internet. Grce
aux SMS et des produits comme liPhone dApple, les tlphones qui peuvent servir de
terminaux Internet deviennent de plus en plus courants. Travailler sur des applications
Android permet donc dacqurir une exprience non ngligeable dans une technologie
moderne (Android) et dans un segment de march qui crot rapidement (celui des terminaux
mobiles pour Internet).
Le problme intervient lorsquil sagit de programmer. Quiconque a dj crit des
programmes pour des PDA (Personal Digital Assistant) ou des tlphones portables sest
heurt aux inconvnients de leur miniaturisation :

Les crans sont sous-dimensionns.

Les claviers, quand ils existent, sont minuscules.

Les dispositifs de pointage, quand il y en a, sont peu pratiques (qui na pas dj perdu
son stylet ?) ou imprcis (les gros doigts se marient mal avec les crans tactiles).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 6 Dimanche, 8. novembre 2009 12:23 12

Lart du dveloppement Android

La vitesse du processeur et la taille de la mmoire sont ridicules par rapport celles


des machines de bureau et des serveurs auxquels nous sommes habitus.

On peut utiliser le langage de programmation et le framework de dveloppement que


lon souhaite, condition que ce soit celui qua choisi le constructeur du terminal.

Etc.

En outre, les applications qui sexcutent sur un tlphone portable doivent grer le fait
quil sagit justement dun tlphone.
Les utilisateurs sont gnralement assez irrits lorsque leur mobile ne fonctionne pas, et
cest la raison pour laquelle la campagne publicitaire "Can you hear me now?" de Verizon
a si bien fonctionn ces dernires annes. De mme, ces mmes personnes seront trs
mcontentes si votre programme perturbe leur tlphone, pour les raisons suivantes :

Il occupe tellement le processeur quelles ne peuvent plus recevoir dappels.

Il ne sintgre pas correctement au systme dexploitation de leur mobile, de sorte que


lapplication ne passe pas en arrire-plan lorsquelles reoivent ou doivent effectuer un
appel.

Il provoque un plantage de leur tlphone cause dune fuite de mmoire.

Le dveloppement de programmes pour un tlphone portable est donc diffrent de lcriture dapplications pour des machines de bureau, du dveloppement de sites web ou de la
cration de programmes serveurs. Vous finirez par utiliser des outils et des frameworks
diffrents et vos programmes auront des limites auxquelles vous ntes pas habitu.
Android essaie de vous faciliter les choses :

Il fournit un langage de programmation connu (Java), avec des bibliothques relativement classiques (certaines API dApache, par exemple), ainsi quun support pour les
outils auxquels vous tes peut-tre habitu (Eclipse, notamment).

Il vous offre un framework suffisamment rigide et tanche pour que vos programmes
sexcutent "correctement" sur le tlphone, sans interfrer avec les autres applications
ou le systme dexploitation lui-mme.

Comme vous vous en doutez srement, lessentiel de ce livre sintresse ce framework et


la faon dcrire des programmes qui fonctionnent dans son cadre et tirent parti de ses
possibilits.

Contenu dun programme Android


Le dveloppeur dune application classique est "le seul matre bord". Il peut ouvrir la
fentre principale de son programme, ses fentres filles les botes de dialogue, par exemple comme il le souhaite. De son point de vue, il est seul au monde ; il tire parti des fonctionnalits du systme dexploitation mais ne soccupe pas des autres programmes

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 7 Dimanche, 8. novembre 2009 12:23 12

Chapitre 1

Tour dhorizon

susceptibles de sexcuter en mme temps que son programme. Sil doit interagir avec
dautres applications, il passe gnralement par une API, comme JDBC (ou les frameworks qui reposent sur lui) pour communiquer avec MySQL ou un autre SGBDR.
Android utilise les mmes concepts, mais proposs de faon diffrente, avec une structure
permettant de mieux protger le fonctionnement des tlphones.

Activity (Activit)
La brique de base de linterface utilisateur sappelle activity (activit). Vous pouvez la
considrer comme lquivalent Android de la fentre ou de la bote de dialogue dune
application classique.
Bien que des activits puissent ne pas avoir dinterface utilisateur, un code "invisible" sera
dlivr le plus souvent sous la forme de fournisseurs de contenus (content provider) ou de
services.

Content providers (fournisseurs de contenus)


Les fournisseurs de contenus offrent un niveau dabstraction pour toutes les donnes stockes sur le terminal et accessibles aux diffrentes applications. Le modle de dveloppement
Android encourage la mise disposition de ses propres donnes aux autres programmes
construire un content provider permet dobtenir ce rsultat tout en gardant un contrle
total sur la faon dont on accdera aux donnes.

Intents (intentions)
Les intentions sont des messages systme. Elles sont mises par le terminal pour prvenir
les applications de la survenue de diffrents vnements, que ce soit une modification
matrielle (comme linsertion dune carte SD) ou larrive de donnes (telle la rception
dun SMS), en passant par les vnements des applications elles-mmes (votre activit a
t lance partir du menu principal du terminal, par exemple). Vous pouvez non seulement rpondre aux intentions, mais galement crer les vtres afin de lancer dautres activits ou pour vous prvenir quune situation particulire a lieu (vous pouvez, par exemple,
mettre lintention X lorsque lutilisateur est moins de 100 mtres dun emplacement Y).

Services
Les activits, les fournisseurs de contenus et les rcepteurs dintentions ont une dure de
vie limite et peuvent tre teints tout moment. Les services sont en revanche conus
pour durer et, si ncessaire, indpendamment de toute activit. Vous pouvez, par exemple,
utiliser un service pour vrifier les mises jour dun flux RSS ou pour jouer de la musique,
mme si lactivit de contrle nest plus en cours dexcution.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 8 Dimanche, 8. novembre 2009 12:23 12

Lart du dveloppement Android

Fonctionnalits votre disposition


Android fournit un certain nombre de fonctionnalits pour vous aider dvelopper des
applications.

Stockage
Vous pouvez empaqueter (packager) des fichiers de donnes dans une application, pour y
stocker ce qui ne changera jamais les icnes ou les fichiers daide, par exemple. Vous
pouvez galement rserver un petit emplacement sur le terminal lui-mme, pour y stocker
une base de donnes ou des fichiers contenant des informations ncessaires votre application et saisies par lutilisateur ou rcupres partir dune autre source. Si lutilisateur
fournit un espace de stockage comme une carte SD, celui-ci peut galement tre lu et crit
en fonction des besoins.

Rseau
Les terminaux Android sont gnralement conus pour tre utiliss avec Internet, via un
support de communication quelconque. Vous pouvez tirer parti de cet accs Internet
nimporte quel niveau, des sockets brutes de Java un widget de navigateur web intgr
que vous pouvez intgrer dans votre application.

Multimdia
Les terminaux Android permettent denregistrer et de jouer de la musique et de la vido.
Bien que les caractristiques spcifiques varient en fonction des modles, vous pouvez
connatre celles qui sont disponibles et tirer parti des fonctionnalits multimdias offertes,
que ce soit pour couter de la musique, prendre des photos ou enregistrer des mmos
vocaux.

GPS
Les fournisseurs de positionnement, comme GPS, permettent dindiquer aux applications
o se trouve le terminal. Il vous est alors possible dafficher des cartes ou dutiliser ces
donnes gographiques pour retrouver la trace du terminal sil a t vol, par exemple.

Services tlphoniques
videmment, les terminaux Android sont gnralement des tlphones, ce qui permet
vos programmes de passer des appels, denvoyer et de recevoir des SMS et de raliser tout
ce que vous tes en droit dattendre dune technologie tlphonique moderne.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 9 Dimanche, 8. novembre 2009 12:23 12

2
Structure dun projet
Le systme de construction dun programme Android est organis sous la forme dune
arborescence de rpertoires spcifique un projet, exactement comme nimporte quel
projet Java. Les dtails, cependant, sont spcifiques Android et sa prparation de
lapplication qui sexcutera sur le terminal ou lmulateur. Voici un rapide tour dhorizon
de la structure dun projet, qui vous aidera mieux comprendre les exemples de code
utiliss dans ce livre et que vous pouvez tlcharger sur le site web de Pearson,
www.pearson.fr, sur la page consacre cet ouvrage.

Contenu de la racine
La cration dun projet Android (avec la commande android create project ou via un
environnement de programmation adapt Android) place plusieurs lments dans le
rpertoire racine du projet :

AndroidManifest.xml est un fichier XML qui dcrit lapplication construire et les


composants activits, services, etc. fournis par celle-ci.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 10 Dimanche, 8. novembre 2009 12:23 12

10

Lart du dveloppement Android

build.xml est un script Ant1 permettant de compiler lapplication et de linstaller sur


le terminal (ce fichier nest pas prsent avec un environnement de programmation
adapt, tel Eclipse).

default.properties et local.properties sont des fichiers de proprits utiliss


par le script prcdent.

bin/ contient lapplication compile.

gen/ contient le code source produit par les outils de compilation dAndroid.

libs/ contient les fichiers JAR extrieurs ncessaires lapplication.

src/ contient le code source Java de lapplication.

res/ contient les ressources icnes, descriptions des lments de linterface graphique
(layouts), etc. empaquetes avec le code Java compil.

tests/ contient un projet Android entirement distinct, utilis pour tester celui que
vous avez cr.

assets/ contient les autres fichiers statiques fournis avec lapplication pour son
dploiement sur le terminal.

la sueur de votre front


Lors de la cration dun projet Android (avec android create project, par exemple),
vous devez fournir le nom de classe ainsi que le chemin complet (paquetage) de lactivit
"principale" de lapplication (com.commonsware.android.UneDemo, par exemple). Vous
constaterez alors que larborescence src/ de ce projet contient la hirarchie des rpertoires
dfinis par le paquetage ainsi quun squelette dune sous-classe dActivity reprsentant
lactivit principale (src/com/commonsware/android/UneDemo.java). Vous pouvez bien
sr modifier ce fichier et en ajouter dautres larborescence src/ selon les besoins de
votre application.
La premire fois que vous compilerez le projet (avec ant, par exemple), la chane de
production dAndroid crera le fichier R.java dans le paquetage de lactivit "principale".
Ce fichier contient un certain nombre de dfinitions de constantes lies aux diffrentes
ressources de larborescence res/. Il est dconseill de le modifier manuellement : laissez
les outils dAndroid sen occuper. Vous rencontrerez de nombreuses rfrences R.java
dans les exemples de ce livre (par exemple, on dsignera lidentifiant dun layout par
R.layout.main).

1. http://ant.apache.org/.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 11 Dimanche, 8. novembre 2009 12:23 12

Chapitre 2

Structure dun projet

11

La suite de lhistoire
Comme on la dj indiqu, larborescence res/ contient les ressources, cest--dire des
fichiers statiques fournis avec lapplication, soit sous leur forme initiale soit, parfois, sous
une forme prtraite. Parmi les sous-rpertoires de res/, citons :

res/drawable/ pour les images (PNG, JPEG, etc.) ;

res/layout/ pour les descriptions XML de la composition de linterface graphique ;

res/menu/ pour les descriptions XML des menus ;

res/raw/ pour les fichiers gnraux (un fichier CSV contenant les informations dun
compte, par exemple) ;

res/values/ pour les messages, les dimensions, etc. ;

res/xml/ pour les autres fichiers XML gnraux que vous souhaitez fournir.

Nous prsenterons tous ces rpertoires, et bien dautres, dans la suite de ce livre, notamment
au Chapitre 19.

Le fruit de votre travail


Lorsque vous compilez un projet (avec ant ou un IDE), le rsultat est plac dans le rpertoire bin/, sous la racine de larborescence du projet :

bin/classes/ contient les classes Java compiles.

bin/classes.dex contient lexcutable cr partir de ces classes compiles.

bin/votreapp.ap_ (o votreapp est le nom de lapplication) contient les ressources


de celle-ci, sous la forme dun fichier ZIP.

bin/votreapp-debug.apk ou bin/votreapp-unsigned.apk est la vritable application


Android.

Le fichier .apk est une archive ZIP contenant le fichier .dex, la version compile des
ressources (resources.arsc), les ventuelles ressources non compiles (celles qui se
trouvent sous res/raw/, par exemple) et le fichier AndroidManifest.xml. Cette archive
est signe : la partie -debug du nom de fichier indique quelle la t laide dune cl de
dbogage qui fonctionne avec lmulateur alors que -unsigned prcise que lapplication a
t construite pour tre dploye (ant release) : larchive APK doit alors tre signe
laide de jarsigner et dune cl officielle.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 12 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 13 Dimanche, 8. novembre 2009 12:23 12

3
Contenu du manifeste
Le point de dpart de toute application Android est son fichier manifeste, AndroidManifest.xml, qui se trouve la racine du projet. Cest dans ce fichier que lon dclare ce que
contiendra lapplication les activits, les services, etc. On y indique galement la faon
dont ces composants seront relis au systme Android lui-mme en prcisant, par exemple, lactivit (ou les activits) qui doivent apparatre dans le menu principal du terminal
(ce menu est galement appel "lanceur" ou "launcher").
La cration dune application produit automatiquement un manifeste de base. Pour une
application simple, qui offre uniquement une activit, ce manifeste automatique conviendra srement ou ncessitera ventuellement quelques modifications mineures. En revanche, le manifeste de lapplication de dmonstration Android fournie contient plus de
1 000 lignes. Vos applications se situeront probablement entre ces deux extrmits.
La plupart des parties intressantes du manifeste seront dcrites en dtail dans les chapitres consacrs aux fonctionnalits dAndroid qui leur sont associes llment service,
par exemple, est dtaill au Chapitre 30. Pour le moment, il vous suffit de comprendre le
rle de ce fichier et la faon dont il est construit.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 14 Dimanche, 8. novembre 2009 12:23 12

14

Lart du dveloppement Android

Au dbut, il y avait la racine


Tous les fichiers manifestes ont pour racine un lment manifest :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.search">
...
</manifest>

Cet lment comprend la dclaration de lespace de noms android. Curieusement, les


manifestes produits nappliquent cet espace quaux attributs, pas aux lments (on crit
donc manifest, pas android:manifest). Nous vous conseillons de conserver cette
convention, sauf si Android la modifie dans une version future.

Info

Lessentiel des informations que vous devez fournir cet lment est lattribut
package (qui nutilise pas non plus lespace de noms). Cest l que vous pouvez
indiquer le nom du paquetage Java qui sera considr comme la "base" de
votre application. Dans la suite du fichier, vous pourrez alors simplement
utiliser le symbole point pour dsigner ce paquetage : si vous devez, par exemple, faire rfrence com.commonsware.android.search.Snicklefritz
dans le manifeste de cet exemple, il suffira dcrire .Snicklefritz puisque
com.commonsware.android.search est dfini comme le paquetage de lapplication.

Permissions, instrumentations et applications


Sous llment manifest, vous trouverez les lments suivants :

Des lments uses-permission indiquant les permissions dont a besoin votre application
pour fonctionner correctement (voir Chapitre 19 pour plus de dtails).

Des lments permission dclarant les permissions que les activits ou les services
peuvent exiger des autres applications pour utiliser les donnes et le code de lapplication
(voir galement Chapitre 19).

Des lments instrumentation qui indiquent le code qui devrait tre appel pour les
vnements systme essentiels, comme le lancement des activits. Ces lments sont
utiliss pour la journalisation ou la surveillance.

Des lments uses-library pour relier les composants facultatifs dAndroid, comme
les services de golocalisation.

ventuellement, un lment uses-sdk indiquant la version du SDK Android avec


laquelle a t construite lapplication.

Un lment application qui dfinit le cur de lapplication dcrite par le manifeste.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 15 Dimanche, 8. novembre 2009 12:23 12

Chapitre 3

Contenu du manifeste

15

Le manifeste de lexemple qui suit contient des lments uses-permission qui indiquent
certaines fonctionnalits du terminal dont lapplication a besoin ici, lapplication doit
avoir le droit de dterminer sa position gographique courante. Llment application
dcrit les activits, les services et tout ce qui constitue lapplication elle-mme.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android">
<uses-permission
android:name="android.permission.ACCESS_LOCATION" />
<uses-permission
android:name="android.permission.ACCESS_GPS" />
<uses-permission
android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission
android:name="android.permission.ACCESS_CELL_ID" />
<application>
...
</application>
</manifest>

Que fait votre application ?


Le plat de rsistance du fichier manifeste est dcrit par les fils de llment application.
Par dfaut, la cration dun nouveau projet Android nindique quun seul lment activity :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.skeleton">
<application>
<activity android:name=".Now" android:label="Now">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Cet lment fournit android:name pour la classe qui implmente lactivit,


android:label pour le nom affich de lactivit et (souvent) un lment fils intentfilter dcrivant les conditions sous lesquelles cette activit saffichera. Llment activity de base configure votre activit pour quelle apparaisse dans le lanceur et que les
utilisateurs puissent lexcuter. Comme nous le verrons plus tard, un mme projet peut
dfinir plusieurs activits.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 16 Dimanche, 8. novembre 2009 12:23 12

16

Lart du dveloppement Android

Il peut galement y avoir un ou plusieurs lments receiver dcrivant les non-activits


qui devraient se dclencher sous certaines conditions la rception dun SMS, par exemple. On les appelle rcepteurs dintentions (intent receivers) et ils sont dcrits au Chapitre 23.
De mme, un ou plusieurs lments provider peuvent tre prsents afin dindiquer les
fournisseurs de contenus (content providers) les composants qui fournissent les donnes
vos activits et, avec votre permission, aux activits dautres applications du terminal.
Ces lments enveloppent les bases de donnes ou les autres stockages de donnes en une
API unique que toute application peut ensuite utiliser. Nous verrons plus loin comment
crer des fournisseurs de contenus et comment utiliser les fournisseurs que vous ou
dautres ont crs.
Enfin, il peut y avoir un ou plusieurs lments service dcrivant les services, cest--dire
les parties de code qui peuvent fonctionner indpendamment de toute activit et en permanence. Lexemple classique est celui du lecteur MP3, qui permet de continuer couter de
la musique, mme si lutilisateur ouvre dautres activits et que linterface utilisateur nest
pas affiche au premier plan. La cration et lutilisation des services sont dcrites aux
Chapitres 30 et 31.

Faire le minimum
Android, comme la plupart des systmes dexploitation, est rgulirement amlior, ce qui
donne lieu de nouvelles versions du systme. Certains de ces changements affectent le
SDK car de nouveaux paramtres, classes ou mthodes apparaissent, qui nexistaient pas
dans les versions prcdentes.
Pour tre sr que votre application ne sexcutera que pour une version prcise (ou suprieure) dun environnement Android, ajoutez un lment uses-sdk comme fils de
llment manifest du fichier AndroidManifest.xml. uses-sdk na quun seul attribut,
minSdkVersion, qui indique la version minimale du SDK exige par votre application :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.search">
<uses-sdk minSdkVersion="2" />
...
</manifest>

lheure o ce livre est crit, minSdkVersion peut prendre trois valeurs :

1 pour indiquer le premier SDK dAndroid, la version 1.0 ;

2 pour indiquer la version 1.1 du SDK dAndroid ;

3 pour indiquer la version 1.5.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 17 Dimanche, 8. novembre 2009 12:23 12

Chapitre 3

Contenu du manifeste

17

Labsence dun lment uses-sdk revient utiliser 1 comme valeur de minSdkVersion.


Cela dit, la boutique Android semble tenir ce que lon indique explicitement la valeur de
minSdkVersion : faites en sorte dutiliser un lment uses-sdk si vous comptez distribuer
votre programme par ce biais.
Si cet lment est prsent, lapplication ne pourra sinstaller que sur les terminaux compatibles. Vous navez pas besoin de prciser la dernire version du SDK mais, si vous en
choisissez une plus ancienne, cest vous de vrifier que votre application fonctionnera
sur toutes les versions avec lesquelles elle prtend tre compatible. Si, par exemple, vous
omettez uses-sdk, cela revient annoncer que votre application fonctionnera sur toutes
les versions existantes du SDK et vous devrez videmment tester que cest bien le cas.

Version = contrle
Si vous comptez distribuer votre application via la boutique Android ou dautres supports,
vous devrez srement ajouter les attributs android:versionCode et android:versionName llment manifest afin de faciliter le processus de mise jour des applications.
android:versionName est une chane lisible reprsentant le nom ou le numro de version
de votre application. Vous pouvez, par exemple, utiliser des valeurs comme "3.0" ou
"System V", en fonction de vos prfrences.
android:versionCode est un entier cens reprsenter le numro de version de lapplication. Le systme lutilise pour savoir si votre version est plus rcente quune autre
"rcent" tant dfini par "la valeur dandroid:versionCode est plus leve". Pour obtenir
cette valeur, vous pouvez convertir le contenu dandroid:versionName en nombre ou
simplement incrmenter la valeur chaque nouvelle version.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 18 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 19 Dimanche, 8. novembre 2009 12:23 12

Partie

II

Les activits
CHAPITRE 4.

Cration dun squelette dapplication

CHAPITRE 5.

Utilisation des layouts XML

CHAPITRE 6.

Utilisation des widgets de base

CHAPITRE 7.

Conteneurs

CHAPITRE 8.

Widgets de slection

CHAPITRE 9.

Samuser avec les listes

CHAPITRE 10.

Utiliser de jolis widgets et de beaux conteneurs

CHAPITRE 11.

Utilisation des menus

CHAPITRE 12.

Polices de caractres

CHAPITRE 13.

Intgrer le navigateur de WebKit

CHAPITRE 14.

Affichage de messages surgissant

CHAPITRE 15.

Utilisation des threads

CHAPITRE 16.

Gestion des vnements du cycle de vie dune activit

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 20 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 21 Dimanche, 8. novembre 2009 12:23 12

4
Cration dun squelette
dapplication
Tous les livres consacrs un langage ou un environnement de programmation commencent par prsenter un programme de dmonstration de type "Bonjour tous !" : il permet
de montrer que lon peut construire quelque chose tout en restant suffisamment concis
pour ne pas noyer le lecteur dans les dtails. Cependant, le programme "Bonjour tous !"
classique nest absolument pas interactif (il se contente dcrire ces mots sur la console) et
est donc assez peu stimulant. Ce chapitre prsentera donc un projet qui utilisera malgr
tout un bouton et lheure courante pour montrer le fonctionnement dune activit Android
simple.

Terminaux virtuels et cibles


Pour construire les projets, nous supposons que vous avez tlcharg le SDK (et, ventuellement, le plugin ADT dEclipse). Avant de commencer, nous devons prsenter la notion
de "cible" car elle risque de vous surprendre et elle est relativement importante dans le
processus de dveloppement.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 22 Dimanche, 8. novembre 2009 12:23 12

22

Lart du dveloppement Android

Comme son nom lindique, un AVD (Android Virtual Device), est un terminal virtuel
par opposition aux vrais terminaux Android comme le G1 ou le Magic de HTC. Les
AVD sont utiliss par lmulateur fourni avec le SDK et vous permettent de tester vos
programmes avant de les dployer sur les vritables terminaux. Vous devez indiquer
lmulateur un terminal virtuel afin quil puisse prtendre quil est bien le terminal dcrit
par cet AVD.

Cration dun AVD


Pour crer un AVD, vous pouvez lancer la commande android create avd ou utiliser
Eclipse ; dans les deux cas, vous devez indiquer une cible qui prcise la classe de terminaux qui sera simule par lAVD. lheure o cette dition est publie, il existe trois
cibles :

une qui dsigne un terminal Android 1.1, comme un G1 HTC de base qui naurait pas
t mis jour ;

une deuxime qui dsigne un terminal Android 1.5, sans le support de Google Maps ;

une troisime qui dsigne un terminal Android 1.5 disposant du support de Google
Maps, ce qui est le cas de la majorit des terminaux Android actuels.

Si vous dveloppez des applications utilisant Google Maps, vous devez donc utiliser un
ADV ayant la cible 3. Sinon la cible 2 conviendra parfaitement. Actuellement, la plupart
des G1 ayant t mis jour avec Android 1.5, la cible 1 nest plus trs utile.
Vous pouvez crer autant dAVD que vous le souhaitez du moment que vous avez
assez despace disque disponible sur votre environnement de dveloppement : si vous
avez besoin dun pour chacune des trois cibles, libre vous ! Noubliez pas, cependant,
que linstallation dune application sur un AVD naffecte pas les autres AVD que vous avez
crs.

Choix dune cible


Lorsque vous crez un projet (avec android create project ou partir dEclipse), vous
devez galement indiquer la classe de terminal vise par celui-ci. Les valeurs possibles
tant les mmes que ci-dessus, crer un projet avec une cible 3 donne les indications
suivantes :

Vous avez besoin dAndroid 1.5.

Vous avez besoin de Google Maps.

Lapplication finale ne sinstallera donc pas sur les terminaux qui ne correspondent pas
ces critres.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 23 Dimanche, 8. novembre 2009 12:23 12

Chapitre 4

Cration dun squelette dapplication

23

Voici quelques rgles pour vous aider grer les cibles :

Demandez-vous ce dont vous avez rellement besoin. Ne crez un projet avec une
cible 3 que si vous utilisez Google Maps, notamment. Sinon une cible 2 est prfrable
vous exigerez toujours Android 1.5, mais votre application pourra sexcuter sur des
terminaux qui ne disposent pas de Google Maps.

Testez autant de cibles que possible. Vous pourriez tre tent par la cible 1 pour
viser le plus grand nombre de terminaux Android ; cependant, vous devrez alors
tester votre application sur un AVD ayant une cible 1 et un AVD ayant une cible 2
(et il serait galement souhaitable de la tester avec un AVD ayant une cible 3, au cas
o).

Vrifiez quune nouvelle cible na pas t ajoute par une nouvelle version
dAndroid. Il devrait y avoir quelques nouvelles valeurs avec chaque version majeure
(2.0 ou 1.6, par exemple), voire pour les versions intermdiaires (1.5r1 ou 1.5r2).
Assurez-vous de tester votre application sur ces nouvelles cibles chaque fois que
cela est possible car certains peuvent utiliser ces nouvelles versions de terminaux ds
quelles sortent.

Le fait de tester avec des AVD, quelle que soit la cible, ne peut pas se substituer aux
tests sur du vrai matriel. Les AVD sont conus pour vous fournir des "environnements
jetables", permettant de tester un grand nombre denvironnements, mme ceux qui
nexistent pas encore rellement. Cependant, vous devez mettre votre application
lpreuve dau moins un terminal Android. En outre, la vitesse de votre mulateur peut
ne pas correspondre celle du terminal selon votre systme, elle peut tre plus rapide
ou plus lente.

Commencer par le dbut


Avec Android, tout commence par la cration dun projet. En Java classique, vous pouvez,
si vous le souhaitez, vous contenter dcrire un programme sous la forme dun unique
fichier, le compiler avec javac puis lexcuter avec Java. Android est plus complexe mais,
pour faciliter les choses, Google a fourni des outils daide la cration dun projet. Si vous
utilisez un IDE compatible avec Android, comme Eclipse et le plugin Android (fourni avec
le SDK), vous pouvez crer un projet directement partir de cet IDE (menu Fichier >
Nouveau > Projet, puis choisissez Android > Android Project).
Si vous vous servez doutils non Android, vous pouvez utiliser le script android, qui se
trouve dans le rpertoire tools/ de linstallation du SDK en lui indiquant que vous
souhaitez crer un projet (create project). Il faut alors lui indiquer la version de la
cible, le rpertoire racine du projet, le nom de lactivit et celui du paquetage o tout devra
se trouver :

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 24 Dimanche, 8. novembre 2009 12:23 12

24

Lart du dveloppement Android

android create project \


--target 2 \
--path chemin/vers/mon/projet \
--activity Now \
--package com.commonsware.android.skeleton

Cette commande cre automatiquement les fichiers que nous avons dcrits au Chapitre 2 et
que nous utiliserons dans le reste de ce chapitre.
Vous pouvez galement tlcharger les rpertoires des projets exemples de ce livre sous la
forme de fichiers ZIP partir du site web de Pearson1. Ces projets sont prts tre utiliss :
vous navez donc pas besoin dutiliser android create project lorsque vous aurez
dcompress ces exemples.

Lactivit
Le rpertoire src/ de votre projet contient une arborescence de rpertoires Java classique,
cre daprs le paquetage Java que vous avez utilis pour crer le projet
(com.commonsware.android.skeleton produit donc src/com/commonsware/android/
skeleton). Dans le rpertoire le plus bas, vous trouverez un fichier source nomm
Now.java, dans lequel sera stock le code de votre premire activit. Celle-ci sera constitue dun unique bouton qui affichera lheure laquelle on a appuy dessus pour le
dernire fois (ou lheure de lancement de lapplication si on na pas encore appuy sur ce
bouton).
Ouvrez Now.java dans un diteur de texte et copiez-y le code suivant :
package com.commonsware.android.skeleton;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.util.Date;
public class Now extends Activity implements View.OnClickListener {
Button btn;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
btn = new Button(this);
btn.setOnClickListener(this);
updateTime();
setContentView(btn);
}

1. http://pearson.fr.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 25 Dimanche, 8. novembre 2009 12:23 12

Chapitre 4

Cration dun squelette dapplication

25

public void onClick(View view) {


updateTime();
}
private void updateTime() {
btn.setText(new Date().toString());
}
}

Si vous avez tlcharg les fichiers partir du site web de Pearson, vous pouvez vous
contenter dutiliser directement le projet Skeleton/Now.
Examinons maintenant chacune des parties de ce code.

Dissection de lactivit
La dclaration de paquetage doit tre identique celle que vous avez utilise pour crer le
projet.
Comme pour tout projet Java, vous devez importer les classes auxquelles vous faites rfrence. La plupart de celles qui sont spcifiques Android se trouvent dans le paquetage
android :
package com.commonsware.android.skeleton;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.util.Date;

Notez bien que toutes les classes de Java SE ne sont pas utilisables par les programmes
Android. Consultez le guide de rfrence des classes Android1 pour savoir celles qui sont
disponibles et celles qui ne le sont pas.
Les activits sont des classes publiques hrites de la classe de base android.app.Activity. Ici, lactivit contient un bouton (btn) :
public class Now extends Activity implements View.OnClickListener {
Button btn;

Info

Un bouton, comme vous pouvez le constater daprs le nom du paquetage, est un


widget Android. Les widgets sont des lments dinterface graphique que vous
pouvez utiliser dans une application.

1. http://code.google.com/android/reference/packages.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 26 Dimanche, 8. novembre 2009 12:23 12

26

Lart du dveloppement Android

Pour rester simple, nous voulons capturer tous les clics de bouton dans lactivit ellemme : cest la raison pour laquelle la classe de notre activit implmente galement
OnClickListener.
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
btn = new Button(this);
btn.setOnClickListener(this);
updateTime();
setContentView(btn);
}

La mthode onCreate() est appele lorsque lactivit est lance. La premire chose
faire est dtablir un chanage vers la superclasse afin de raliser linitialisation de lactivit Android de base.
Nous crons ensuite linstance du bouton (avec new Button(this)) et demandons
denvoyer tous les clics sur ce bouton linstance de lactivit (avec setOnClickListener()). Nous appelons ensuite la mthode prive updateTime() (qui sera prsente
plus loin), puis nous configurons la vue du contenu de lactivit pour que ce soit le bouton
lui-mme (avec setContentView()).

Info

Tous les widgets drivent de la classe de base View. Bien que lon construise gnralement linterface graphique partir dune hirarchie de vues, nous nutiliserons
ici quune seule vue.

Nous prsenterons ce Bundle icicle magique au Chapitre 16. Pour linstant, considrons-le comme un gestionnaire opaque, que toutes les activits reoivent lors de leur cration.
public void onClick(View view) {
updateTime();
}

Avec Swing, un clic sur un JButton dclenche un ActionEvent qui est transmis
lActionListener configur pour ce bouton. Avec Android, en revanche, un clic sur un
bouton provoque lappel de la mthode onClick() sur linstance OnClickListener
configure pour le bouton. Lcouteur reoit la vue qui a dclench le clic (ici, il sagit du
bouton). Dans notre cas, nous nous contentons dappeler la mthode prive updateTime() :
private void updateTime() {
btn.setText(new Date().toString());
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 27 Dimanche, 8. novembre 2009 12:23 12

Chapitre 4

Cration dun squelette dapplication

27

Louverture de lactivit (onCreate()) ou un clic sur le bouton (onClick()) doit provoquer la mise jour du label du bouton avec la date courante. On utilise pour cela la
mthode setText(), qui fonctionne exactement comme avec les JButton de Swing.

Compiler et lancer lactivit


Pour compiler lactivit, utilisez loutil de cration de paquetage Android intgr votre
IDE ou lancez ant debug depuis le rpertoire racine de votre projet. Puis lancez lactivit
en suivant les tapes suivantes :
1. Lancez lmulateur (avec tools/emulator dans votre installation du SDK Android),
pour obtenir une figure analogue celle de la Figure 4.1. Noubliez pas de prciser un
AVD avec loption -avd.
Figure 4.1
Lcran daccueil
dAndroid.

2. Installez le paquetage (avec tools/adb install /racine/projet/bin/Now-debug


.apk).
3. Consultez la liste des applications installes sur lmulateur et recherchez celle qui
sappelle Now (voir Figure 4.2).
4. Ouvrez cette application.
Vous devriez voir apparatre un cran dactivit comme celui de la Figure 4.3.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 28 Dimanche, 8. novembre 2009 12:23 12

28

Lart du dveloppement Android

Figure 4.2
Le lanceur dapplications
dAndroid.

Figure 4.3
Dmonstration
de lactivit Now.

En cliquant sur le bouton en dautres termes, quasiment nimporte o sur lcran du tlphone , lheure courante saffichera sur le label du bouton.
Vous remarquerez que ce label est centr horizontalement et verticalement car cest le
style par dfaut. Nous verrons au Chapitre 6 que nous pouvons videmment contrler ce
formatage.
Une fois repu de cette technologie poustouflante des boutons, vous pouvez cliquer sur le
bouton de retour en arrire de lmulateur pour revenir au lanceur.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 29 Dimanche, 8. novembre 2009 12:23 12

5
Utilisation des layouts
XML
Bien quil soit techniquement possible de crer et dattacher des composants widgets une
activit en utilisant uniquement du code Java comme nous lavons fait au Chapitre 4,
on prfre gnralement employer un fichier de positionnement (layout) cod en
XML. Linstanciation dynamique des widgets est rserve aux scnarios plus complexes, o les widgets ne sont pas connus au moment de la compilation (lorsquil faut, par
exemple, remplir une colonne de boutons radio en fonction de donnes rcupres sur
Internet).
Examinons maintenant ce code XML pour savoir comment sont agences les vues des
activits Android lorsque lon utilise cette approche.

Quest-ce quun positionnement XML ?


Comme son nom lindique, un positionnement XML est une spcification des relations
existant entre les composants widgets et avec leurs conteneurs (voir Chapitre 7) exprime sous la forme dun document XML. Plus prcisment, Android considre les layouts
XML comme des ressources stockes dans le rpertoire res/layout du projet.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 30 Dimanche, 8. novembre 2009 12:23 12

30

Lart du dveloppement Android

Chaque fichier XML contient une arborescence dlments prcisant le layout des widgets
et les conteneurs qui composent une View. Les attributs de ces lments sont des proprits
qui dcrivent laspect dun widget ou le comportement dun conteneur. Un lment
Button avec un attribut android:textStyle = "bold", par exemple, signifie que le texte
apparaissant sur ce bouton sera en gras.
Loutil aapt du SDK dAndroid utilise ces layouts ; il est appel automatiquement par la
chane de production du projet (que ce soit via Eclipse ou par le traitement du fichier
build.xml de Ant). Cest aapt qui produit le fichier source R.java du projet, qui vous
permet daccder directement aux layouts et leurs composants widgets depuis votre code
Java, comme nous le verrons bientt.

Pourquoi utiliser des layouts XML ?


La plupart de ce qui peut tre fait avec des fichiers de positionnement XML peut galement tre ralis avec du code Java. Vous pourriez, par exemple, utiliser setTypeface()
pour quun bouton affiche son texte en gras au lieu dutiliser une proprit dans un fichier
XML. Ces fichiers sajoutant tous ceux que vous devez dj grer, il faut donc quil y ait
une bonne raison de les utiliser.
La principale est probablement le fait quils permettent de crer des outils de dfinition des
vues : le constructeur dinterfaces graphiques dun IDE comme Eclipse ou un assistant
ddi la cration des interfaces graphiques dAndroid, comme DroidDraw 1, par exemple.
Ces logiciels pourraient, en principe, produire du code Java au lieu dun document XML,
mais il est bien plus simple de relire la dfinition dune interface graphique afin de la
modifier lorsque cette dfinition est exprime dans un format structur comme XML au
lieu dtre code dans un langage de programmation. En outre, sparer ces dfinitions
XML du code Java rduit les risques quune modification du code source perturbe lapplication. XML est un bon compromis entre les besoins des concepteurs doutils et ceux des
programmeurs.
En outre, lutilisation de XML pour la dfinition des interfaces graphiques est devenue
monnaie courante. XAML2 de Microsoft, Flex3 dAdobe et XUL4 de Mozilla utilisent
toutes une approche quivalente de celle dAndroid : placer les dtails des positionnements dans un fichier XML en permettant de les manipuler partir des codes sources (
laide de JavaScript pour XUL, par exemple). De nombreux frameworks graphiques moins
connus, comme ZK5, utilisent galement XML pour la dfinition de leurs vues. Bien que
1.
2.
3.
4.
5.

http://droiddraw.org/.
http://windowssdk.msdn.microsoft.com/en-us/library/ms752059.aspx .
http://www.adobe.com/products/flex/.
http://www.mozilla.org/projects/xul/.
http://www.zkoss.org/.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 31 Dimanche, 8. novembre 2009 12:23 12

Chapitre 5

Utilisation des layouts XML

31

"suivre le troupeau" ne soit pas toujours le meilleur choix, cette politique a lavantage de
faciliter la transition vers Android partir dun autre langage de description des vues
reposant sur XML.

Contenu dun fichier layout


Voici le bouton de lapplication du chapitre prcdent, converti en un fichier XML
que vous trouverez dans le rpertoire chap5/Layouts/NowRedux de larchive des
codes sources, tlchargeable sur le site www.pearson.fr, sur la page ddie cet
ouvrage. Pour faciliter la recherche des codes des exemples, cette archive est dcoupe
selon les chapitres du livre.
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button"
android:text=""
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

Ici, le nom de llment XML est celui de la classe du widget, Button. Ce dernier tant
fourni par Android, il suffit dutiliser simplement son nom de classe. Dans le cas dun
widget personnalis, driv dandroid.view.View, il faudrait utiliser un nom pleinement
qualifi, contenant le nom du paquetage (com.commonsware.android.MonWidget, par
exemple).
Llment racine doit dclarer lespace de noms XML dAndroid :
xmlns:android="http://schemas.android.com/apk/res/android"

Tous les autres lments sont des fils de la racine et hritent de cette dclaration.
Comme lon souhaite pouvoir faire rfrence ce bouton partir de notre code Java, il faut
lui associer un identifiant avec lattribut android:id. Nous expliquerons plus prcisment
ce mcanisme plus loin dans ce chapitre.
Les autres attributs sont les proprits de cette instance de Button :

android:text prcise le texte qui sera affich au dpart sur le bouton (ici, il sagit
dune chane vide).

android:layout_width et android:layout_height prcisent que la largeur et la


hauteur du bouton rempliront le "parent", cest--dire ici lcran entier ces attributs
seront prsents plus en dtail au Chapitre 7.

Ce widget tant le seul contenu de notre activit, nous navons besoin que de cet lment.
Les vues plus complexes ncessitent une arborescence dlments, afin de reprsenter les
widgets et les conteneurs qui contrlent leur positionnement. Dans la suite de ce livre,
nous utiliserons des positionnements XML chaque fois que cela est ncessaire : vous

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 32 Dimanche, 8. novembre 2009 12:23 12

32

Lart du dveloppement Android

trouverez donc des dizaines dexemples plus complexes que vous pourrez utiliser comme
point de dpart pour vos propres projets.

Identifiants des widgets


De nombreux widgets et conteneurs ne peuvent apparatre que dans le fichier de positionnement et ne seront pas utiliss par votre code Java. Le plus souvent, un label statique
(TextView), par exemple, na besoin dtre dans le fichier XML que pour indiquer
lemplacement o il doit apparatre dans linterface. Ce type dlment na donc pas
besoin dun attribut android:id pour lui donner un nom.
En revanche, tous les lments dont vous avez besoin dans votre source Java doivent
possder cet attribut.
La convention consiste utiliser le format @+id/nom_unique comme valeur didentifiant,
o nom_unique reprsente le nom local du widget, qui doit tre unique. Dans lexemple de
la section prcdente, lidentifiant du widget Button tait @+id/button.
Android utilise galement quelques valeurs android:id spcifiques, de la forme
@android:id/.... Nous les rencontrerons dans diffrents chapitres de ce livre, notamment
aux Chapitres 8 et 10.

Utilisation des widgets dans le code Java


Une fois que vous avez douloureusement configur les widgets et les conteneurs dans un
fichier de positionnement XML nomm main.xml et que vous lavez plac dans le rpertoire res/layout, vous navez plus qu ajouter une seule instruction dans la mthode
onCreate() de votre activit pour pouvoir utiliser ce positionnement :
setContentView(R.layout.main);

Il sagit du mme appel setContentView() que nous avions utilis prcdemment en lui
passant une instance dune sous-classe de View (un Button). La vue construite partir de
notre fichier XML est dsormais accessible partir de la classe R. Tous les positionnements dfinis se trouvent sous R.layout, indiqus par le nom de base du fichier
R.layout.main dsigne donc main.xml.
Pour accder nos widgets, nous utilisons ensuite la mthode findViewById(), en lui
passant lidentifiant numrique du widget concern. Cet identifiant a t produit par
Android dans la classe R et est de la forme R.id.qquechose (o qquechose est le widget
que vous recherchez). Ces widgets sont des sous-classes de View, exactement comme
linstance de Button que nous avions cre au Chapitre 4.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 33 Dimanche, 8. novembre 2009 12:23 12

Chapitre 5

Utilisation des layouts XML

33

Fin de lhistoire
Dans le premier exemple Now, le texte du bouton affichait lheure laquelle on avait
appuy dessus pour la dernire fois (ou lheure laquelle lactivit avait t lance).
Lessentiel du code fonctionne encore, mme dans cette nouvelle version (que nous appellerons NowRedux). Cependant, au lieu dinstancier le bouton dans la mthode onCreate(),
nous faisons rfrence celui qui est dcrit dans lagencement XML :
package com.commonsware.android.layouts;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.util.Date;
public class NowRedux extends Activity
implements View.OnClickListener {
Button btn;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
btn=(Button)findViewById(R.id.button);
btn.setOnClickListener(this);
updateTime();
}
public void onClick(View view) {
updateTime();
}
private void updateTime() {
btn.setText(new Date().toString());
}
}

La premire diffrence est quau lieu de crer la vue dans le code Java nous faisons rfrence au fichier XML de positionnement (avec setContentView(R.layout.main)). Le
fichier source R.java sera mis jour lorsque le projet sera recompil, afin dinclure une
rfrence au fichier de positionnement (main.xml du rpertoire res/layout).
La seconde diffrence est quil faut retrouver linstance de notre bouton en appelant la
mthode findViewById(). Comme lidentifiant de ce bouton est @+id/button, nous
pouvons dsigner son identifiant numrique par R.id.button. Il reste ensuite mettre en
place lcouteur dvnement et configurer son label.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 34 Dimanche, 8. novembre 2009 12:23 12

34

Lart du dveloppement Android

La Figure 5.1 montre que le rsultat est le mme que celui de lexemple Now prcdent.
Figure 5.1
Lactivit NowRedux.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 35 Dimanche, 8. novembre 2009 12:23 12

6
Utilisation des widgets
de base
Chaque kit de dveloppement graphique possde des widgets de base : champs de saisie,
labels, boutons, etc. Celui dAndroid ny fait pas exception et leur tude fournit une bonne
introduction au fonctionnement des widgets dans les activits Android.

Labels
Le label (TextView pour Android) est le widget le plus simple. Comme dans la plupart des
kits de dveloppement, les labels sont des chanes de textes non modifiables par les utilisateurs. Ils servent gnralement identifier les widgets qui leur sont adjacents ("Nom : ",
par exemple, plac ct dun champ de saisie).
En Java, un label est une instance de la classe TextView. Cependant, ils seront le plus
souvent crs dans les fichiers layout XML en ajoutant un lment TextView dot dune
proprit android:text pour dfinir le texte qui lui est associ. Si vous devez changer
des labels en fonction dun certain critre linternationalisation, par exemple , vous
pouvez utiliser la place une rfrence de ressource dans le code XML, comme nous

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 36 Dimanche, 8. novembre 2009 12:23 12

36

Lart du dveloppement Android

lexpliquerons au Chapitre 9. Un lment TextView possde de nombreux autres attributs,


notamment :

android:typeface pour dfinir le type de la police du label (monospace, par exemple) ;

android:textStyle pour indiquer si le texte doit tre en gras (bold), en italique


(italic) ou les deux (bold_italic) ;

android:textColor pour dfinir la couleur du texte du label, au format RGB hexadcimal (#FF0000 pour un texte rouge, par exemple).

Voici le contenu du fichier de positionnement du projet Basic/Label :


<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Vous vous attendiez quelque chose de plus profond ?"
/>

Comme le montre la Figure 6.1, ce fichier seul, avec le squelette Java fourni par la chane
de production dAndroid (android create project), produira lapplication voulue.
Figure 6.1
Lapplication LabelDemo.

Boutons
Nous avons dj utilis le widget Button au Chapitre 4. Button tant une sous-classe de
TextView, tout ce qui a t dit dans la section prcdente concernant le formatage du texte
sapplique galement au texte dun bouton.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 37 Dimanche, 8. novembre 2009 12:23 12

Chapitre 6

Utilisation des widgets de base

37

Images
Android dispose de deux widgets permettant dintgrer des images dans les activits :
ImageView et ImageButton. Comme leur nom lindique, il sagit, respectivement, des
quivalents images de TextView et Button.
Chacun deux possde un attribut android:src permettant de prciser limage utilise.
Cet attribut dsigne gnralement une ressource graphique (voir le chapitre consacr aux
ressources). Vous pouvez galement configurer le contenu de limage en utilisant une URI
dun fournisseur de contenu, via un appel setImageURI().
ImageButton, une sous-classe dImageView, lui ajoute les comportements dun Button
standard pour rpondre aux clics et autres actions.
Examinons, par exemple, le contenu du fichier main.xml du projet Basic/ImageView :
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/icon"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="true"
android:src="@drawable/molecule"
/>

Le rsultat, qui utilise simplement lactivit produite automatiquement, est prsent la


Figure 6.2.
Figure 6.2
Lapplication
ImageViewDemo.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 38 Dimanche, 8. novembre 2009 12:23 12

38

Lart du dveloppement Android

Champs de saisie
Outre les boutons et les labels, les champs de saisie forment le troisime pilier de la
plupart des outils de dveloppement graphiques. Avec Android, ils sont reprsents par le
widget EditText, qui est une sous-classe de TextView, dj vue pour les labels.
En plus des proprits standard de TextView (android:textStyle, par exemple), EditText possde de nombreuses autres proprits ddies la construction des champs.
Parmi elles, citons :

android:autoText pour indiquer si le champ doit fournir une correction automatique


de lorthographe.

android:capitalize pour demander que le champ mette automatiquement en


majuscule la premire lettre de son contenu.

android:digits pour indiquer que le champ nacceptera que certains chiffres.

android:singleLine pour indiquer si la saisie ne seffectue que sur une seule ou


plusieurs lignes (autrement dit, <Enter> vous place-t-il sur le widget suivant ou ajoutet-il une nouvelle ligne ?).

Outre ces proprits, vous pouvez configurer les champs pour quils utilisent des mthodes
de saisie spcialises, avec les attributs android:numeric pour imposer une saisie uniquement numrique, android:password pour masquer la saisie dun mot de passe et
android:phoneNumber pour la saisie des numros de tlphone. Pour crer une mthode
de saisie particulire (afin, par exemple, de saisir des codes postaux ou des numros de
scurit sociale), il faut implmenter linterface InputMethod puis configurer le champ
pour quil utilise cette mthode, laide de lattribut android:inputMethod.
Voici, par exemple, la description dun EditText tir du projet Basic/Field :
<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/field"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:singleLine="false"
/>

Vous remarquerez que la valeur dandroid:singleLine est false, ce qui signifie que les
utilisateurs pourront saisir plusieurs lignes de texte dans ce champ.
Le fichier FieldDemo.java de ce projet remplit le champ de saisie avec un peu de prose :
package com.commonsware.android.basic;
import android.app.Activity;
import android.os.Bundle;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 39 Dimanche, 8. novembre 2009 12:23 12

Chapitre 6

Utilisation des widgets de base

39

import android.widget.EditText;
public class FieldDemo extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
EditText fld=(EditText)findViewById(R.id.field);
fld.setText("Publie sous les termes de la licence Apache 2.0 " +
"(la \"Licence\") ; pour utiliser ce fichier " +
"vous devez respecter la Licence dont vous pouvez " +
"obtenir une copie a lURL " +
"http://www.apache.org/licenses/LICENSE-2.0");
}
}

La Figure 6.3 montre le rsultat obtenu.


Figure 6.3
Lapplication FieldDemo.

Info

Lmulateur dAndroid nautorise quune seule application dun mme paquetage


Java dans le lanceur (application Launcher). Comme tous les exemples de ce
chapitre appartiennent au paquetage com.commonsware.android.basic, il
napparatra quun seul exemple la fois dans le lanceur.

Certains champs offrent une compltion automatique afin de permettre lutilisateur


dentrer des informations sans taper lintgralit du texte. Avec Android, ces champs sont
des widgets AutoCompleteTextView ; ils seront prsents au Chapitre 8.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 40 Dimanche, 8. novembre 2009 12:23 12

40

Lart du dveloppement Android

Cases cocher
La case cocher classique peut tre dans deux tats : coche ou dcoche. Un clic sur la
case inverse son tat pour indiquer un choix ("Livrer ma commande en urgence", par
exemple). Le widget CheckBox dAndroid permet dobtenir ce comportement. Comme il
drive de la classe TextView, les proprits de celles-ci comme android:textColor
permettent galement de formater ce widget.
Dans votre code Java, vous pouvez utiliser les mthodes suivantes :

isChecked() pour savoir si la case est coche ;

setChecked() pour forcer la case dans ltat coch ou dcoch ;

toggle() pour inverser ltat de la case, comme si lutilisateur avait cliqu dessus.

Vous pouvez galement enregistrer un objet couteur (il sagira, ici, dune instance
dOnCheckedChangeListener) pour tre prvenu des changements dtat de la case.
Voici la dclaration XML dune case cocher, tire du projet Basic/CheckBox :
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cette case est: decochee" />

Le fichier CheckBoxDemo.java correspondant rcupre cette case cocher et configure


son comportement :
public class CheckBoxDemo extends Activity
implements CompoundButton.OnCheckedChangeListener {
CheckBox cb;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
cb=(CheckBox)findViewById(R.id.check);
cb.setOnCheckedChangeListener(this);
}
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (isChecked) {
cb.setText("Cette case est cochee");
}
else {
cb.setText("Cette case est decochee");
}
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 41 Dimanche, 8. novembre 2009 12:23 12

Chapitre 6

Utilisation des widgets de base

41

Vous remarquerez que cest lactivit qui sert dcouteur pour les changements dtat de la
case cocher (avec cb.setOnCheckedChangeListener(this)) car elle implmente
linterface OnCheckedChangeListener.
La mthode de rappel de lcouteur est onCheckedChanged() : elle reoit la case qui a
chang dtat et son nouvel tat. Ici, on se contente de modifier le texte de la case pour
reflter son tat courant.
Cliquer sur la case modifie donc immdiatement son texte, comme le montrent les
Figures 6.4 et 6.5.
Figure 6.4
Lapplication CheckBoxDemo avec la case
dcoche.

Figure 6.5
La mme application avec
la case coche.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 42 Dimanche, 8. novembre 2009 12:23 12

42

Lart du dveloppement Android

Boutons radio
Comme dans les autres outils de dveloppement, les boutons radio dAndroid ont deux
tats, telles les cases cocher, mais peuvent tre regroups de sorte quun seul bouton
radio puisse tre coch par groupe un instant donn.
Comme CheckBox, RadioButton hrite de la classe CompoundButton, qui drive ellemme de TextView. Toutes les proprits standard de TextView pour la police, le style, la
couleur, etc. sappliquent donc galement aux boutons radio. Vous pouvez par consquent
appeler isChecked() sur un RadioButton pour savoir sil est coch, toggle() pour le
slectionner, etc. exactement comme avec une CheckBox.
La plupart du temps, les widgets RadioButton sont placs dans un conteneur RadioGroup
qui permet de lier les tats des boutons quil regroupe afin quun seul puisse tre slectionn un instant donn. En affectant un identifiant android:id au RadioGroup dans le
fichier de description XML, ce groupe devient accessible au code Java, qui peut alors lui
appliquer les mthodes suivantes :

check() pour tester un bouton radio partir de son identifiant (avec groupe.check
(R.id.radio1)) ;

clearCheck() pour dcocher tous les boutons du groupe ;

getCheckedRadioButtonId() pour obtenir lidentifiant du bouton radio actuellement


coch (cette mthode renvoie 1 si aucun bouton nest coch).

Voici, par exemple, une description XML dun groupe de boutons radio, tire de lexemple
Basic/RadioButton :
<?xml version="1.0" encoding="utf-8"?>
<RadioGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RadioButton android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Caillou" />
<RadioButton android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ciseaux" />
<RadioButton android:id="@+id/radio3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Papier" />
</RadioGroup>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 43 Dimanche, 8. novembre 2009 12:23 12

Chapitre 6

Utilisation des widgets de base

43

La Figure 6.6 montre le rsultat obtenu en utilisant le projet Java de base, fourni par
Android.
Figure 6.6
Lapplication
RadioButtonDemo.

Vous remarquerez que, au dpart, aucun bouton du groupe nest coch. Pour que lapplication slectionne lun de ces boutons ds son lancement, il faut appeler soit la mthode
setChecked() sur le RadioButton concern, soit la mthode check() sur le RadioGroup
partir de la mthode onCreate() de lactivit.

Rsum
Tous les widgets que nous venons de prsenter drivent de la classe View et hritent donc
dun ensemble de proprits et de mthodes supplmentaires par rapport celles que nous
avons dj dcrites.

Proprits utiles
Parmi les proprits les plus utiles de View, citons :

Le contrle de la squence de focus :


android:nextFocusDown ;
android:nextFocusLeft ;
android:nextFocusRight ;
android:nextFocusUp.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 44 Dimanche, 8. novembre 2009 12:23 12

44

Lart du dveloppement Android

android:visibility, qui contrle la visibilit initiale du widget.

android:background, qui permet de fournir au widget une couleur de fond au format


RGB (#00FF00 pour vert, par exemple).

Mthodes utiles
La mthode setEnabled() permet de basculer entre ltat actif et inactif du widget, alors
que isEnabled() permet de tester si un widget est actif. On utilise souvent ces deux
mthodes pour dsactiver certains widgets en fonction de choix effectus laide de CheckBox ou de RadioButton.
La mthode requestFocus() donne le focus un widget et isFocused() permet de tester
sil a le focus. En utilisant les mthodes voques plus haut, on peut donc donner le focus
un widget prcis aprs une opration de dsactivation.
Les mthodes suivantes permettent de parcourir une arborescence de widgets et de conteneurs
composant la vue gnrale dune activit :

getParent() renvoie le widget ou le conteneur parent.

findViewById() permet de retrouver un widget fils daprs son identifiant.

getRootView() renvoie la racine de larborescence (celle que vous avez fournie


lactivit via un appel setContentView()).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 45 Dimanche, 8. novembre 2009 12:23 12

7
Conteneurs
Les conteneurs permettent de disposer un ensemble de widgets (et, ventuellement, des conteneurs fils) pour obtenir la prsentation de votre choix. Si, par exemple, vous prfrez placer
les labels gauche et les champs de saisie droite, vous aurez besoin dun conteneur. Si vous
voulez que les boutons OK et Annuler soient lun ct de lautre, en bas droite du formulaire, vous aurez galement besoin dun conteneur. Dun point de vue purement XML, si vous
manipulez plusieurs widgets (le cas des RadioButton dans un RadioGroup est particulier),
vous devrez utiliser un conteneur afin de disposer dun lment racine dans lequel les placer.
La plupart des kits de dveloppement graphiques utilisent des gestionnaires de disposition
des widgets (layout managers) qui sont, le plus souvent, organiss sous forme de conteneurs. Java Swing, par exemple, dispose du gestionnaire BoxLayout, qui est utilis avec
certains conteneurs (comme Box). Dautres kits de dveloppement, comme XUL et Flex,
sen tiennent strictement au modle des botes, qui permet de crer nimporte quelle disposition via une combinaison adquate de botes imbriques.
Avec LinearLayout, Android offre galement un modle de disposition en botes, mais il
fournit aussi un grand nombre de conteneurs autorisant dautres systmes de composition.
Dans ce chapitre, nous tudierons trois conteneurs parmi les plus courants : LinearLayout (le modle des botes), RelativeLayout (un modle de positionnement relatif)
et TableLayout (le modle en grille) ; nous prsenterons galement ScrollView, un
conteneur conu pour faciliter la mise en place des conteneurs avec barres de dfilement.
Le chapitre suivant prsentera dautres conteneurs plus sotriques.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 46 Dimanche, 8. novembre 2009 12:23 12

46

Lart du dveloppement Android

Penser de faon linaire


Comme on la dj mentionn, LinearLayout est un modle reposant sur des botes les
widgets ou les conteneurs fils sont aligns en colonnes ou en lignes, les uns aprs
les autres, exactement comme avec FlowLayout en Java Swing, et vbox et hbox en Flex et
XUL.
Avec Flex et XUL, la bote est lunit essentielle de disposition des widgets. Avec
Android, vous pouvez utiliser LinearLayout exactement de la mme faon, en vous
passant des autres conteneurs. Obtenir la disposition que vous souhaitez revient alors principalement identifier les imbrications et les proprits des diffrentes botes leur
alignement par rapport aux autres botes, par exemple.

Concepts et proprits
Pour configurer un LinearLayout, vous pouvez agir sur cinq paramtres : lorientation, le
modle de remplissage, le poids, la gravit et le remplissage.

Orientation
Lorientation prcise si le LinearLayout reprsente une ligne ou une colonne. Il suffit
dajouter la proprit android:orientation llment LinearLayout du fichier XML
en fixant sa valeur horizontal pour une ligne ou vertical pour une colonne.
Cette orientation peut tre modifie en cours dexcution en appelant la mthode setOrientation()et en lui fournissant en paramtre la constante HORIZONTAL ou VERTICAL.

Modle de remplissage
Supposons que nous ayons une ligne de widgets une paire de boutons radio, par exemple. Ces widgets ont une taille "naturelle" reposant sur celle de leur texte. Ces tailles
combines ne correspondent srement pas la largeur de lcran du terminal Android
notamment parce que les tailles des crans varient en fonction des modles. Il faut donc
savoir que faire de lespace restant.
Pour rsoudre ce problme, tous les widgets dun LinearLayout doivent fournir une
valeur pour les proprits android:layout_width et android:layout_height. Ces valeurs
peuvent sexprimer de trois faons diffrentes :

Une dimension prcise, comme 125 px, pour indiquer que le widget devra occuper
exactement 125 pixels.

wrap_content, pour demander que le widget occupe sa place naturelle sauf sil est
trop gros, auquel cas Android coupera le texte entre les mots pour quil puisse tenir.

fill_parent, pour demander que le widget occupe tout lespace disponible de son
conteneur aprs que les autres widgets eurent t placs.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 47 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

47

Les valeurs les plus utilises sont les deux dernires, car elles sont indpendantes de la
taille de lcran ; Android peut donc ajuster la disposition pour quelle tienne dans
lespace disponible.

Poids
Que se passera-t-il si deux widgets doivent se partager lespace disponible ? Supposons,
par exemple, que nous ayons deux champs de saisie multilignes en colonne et que nous
voulions quils occupent tout lespace disponible de la colonne aprs le placement de tous
les autres widgets.
Pour ce faire, en plus dinitialiser android:layout_width (pour les lignes) ou
android:layout_height (pour les colonnes) avec fill_parent, il faut galement
donner android:layout_weight, une valeur qui indique la proportion despace libre
qui sera affecte au widget. Si cette valeur est la mme pour les deux widgets (1, par
exemple), lespace libre sera partag quitablement entre eux. Si la valeur est 1 pour un
widget et 2 pour lautre, le second utilisera deux fois plus despace libre que le premier,
etc.

Gravit
Par dfaut, les widgets salignent partir de la gauche et du haut. Si vous crez une ligne
avec un LinearLayout horizontal, cette ligne commencera donc se remplir partir du
bord gauche de lcran.
Si ce nest pas ce que vous souhaitez, vous devez indiquer une gravit laide de la
proprit android:layout_gravity dun widget (ou en appelant la mthode setGravity() sur celui-ci) afin dindiquer au widget et son conteneur comment laligner par
rapport lcran.
Pour une colonne de widgets, les gravits les plus courantes sont left, center_horizontal et right pour, respectivement, aligner les widgets gauche, au centre ou droite.
Pour une ligne, le comportement par dfaut consiste placer les widgets de sorte que leur
texte soit align sur la ligne de base (la ligne invisible sur laquelle les lettres semblent
reposer), mais il est possible de prciser une gravit center_vertical pour centrer verticalement les widgets dans la ligne.

Remplissage
Les widgets sont, par dfaut, serrs les uns contre les autres. Vous pouvez augmenter
lespace intercalaire laide de la proprit android:padding (ou en appelant la mthode
setPadding() de lobjet Java correspondant au widget).
La valeur de remplissage prcise lespace situ entre le contour de la "cellule" du widget et
son contenu rel. Elle est analogue aux marges dun document dans un traitement de texte

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 48 Dimanche, 8. novembre 2009 12:23 12

48

Lart du dveloppement Android

la taille de page peut tre de 21 29,7 cm, mais des marges de 2 cm confinent le texte
dans une surface de 19 27,7 cm.
La proprit android:padding permet de prciser le mme remplissage pour les quatre
cts du widget ; son contenu tant alors centr dans la zone qui reste. Pour utiliser des
valeurs diffrentes en fonction des cts, utilisez les proprits android:paddingLeft,
android:paddingRight, android:paddingTop et android:paddingBottom (voir
Figure 7.1).
Figure 7.1
Relations entre
un widget, sa cellule
et ses valeurs
de remplissage.

La valeur de ces proprits est une dimension, comme 5px pour demander un remplissage
de 5 pixels.

Exemple
Voici le fichier de description XML de lexemple Containers/Linear, qui montre les
proprits de llment LinearLayout :
<?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"
>
<RadioGroup android:id="@+id/orientation"
android:orientation="horizontal"
android:layout_width="wrap_content"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 49 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

49

android:layout_height="wrap_content"
android:padding="5px">
<RadioButton
android:id="@+id/horizontal"
android:text="horizontal" />
<RadioButton
android:id="@+id/vertical"
android:text="vertical" />
</RadioGroup>
<RadioGroup android:id="@+id/gravity"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px">
<RadioButton
android:id="@+id/left"
android:text="gauche" />
<RadioButton
android:id="@+id/center"
android:text="centre" />
<RadioButton
android:id="@+id/right"
android:text="droite" />
</RadioGroup>
</LinearLayout>

Vous remarquerez que le conteneur LinearLayout enveloppe deux RadioGroup. RadioGroup tant une sous-classe de LinearLayout, notre exemple revient donc imbriquer
des conteneurs LinearLayout.
Le premier lment RadioGroup configure une ligne (android:orientation = "horizontal") de widgets RadioButton. Il utilise un remplissage de 5 pixels sur ses quatre
cts, afin de le sparer de lautre RadioGroup. Sa largeur et sa hauteur valent toutes les
deux wrap_content pour que les boutons radio noccupent que lespace dont ils ont
besoin.
Le deuxime RadioGroup est une colonne (android:orientation = "vertical") de
trois RadioButton. Il utilise galement un remplissage de 5 pixels sur tous ses cts
et sa hauteur est "naturelle" (android:layout_height = "wrap_content"). Cependant, sa
proprit android:layout_width vaut fill_parent, ce qui signifie que la colonne
de boutons radio "rclamera" toute la largeur de lcran.
Pour ajuster ces valeurs en cours dexcution en fonction de la saisie de lutilisateur, il faut
utiliser un peu de code Java :
package com.commonsware.android.containers;
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 50 Dimanche, 8. novembre 2009 12:23 12

50

Lart du dveloppement Android

import android.text.TextWatcher;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.EditText;
public class LinearLayoutDemo extends Activity
implements RadioGroup.OnCheckedChangeListener {
RadioGroup orientation;
RadioGroup gravity;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
orientation=(RadioGroup)findViewById(R.id.orientation);
orientation.setOnCheckedChangeListener(this);
gravity=(RadioGroup)findViewById(R.id.gravity);
gravity.setOnCheckedChangeListener(this);
}
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (group==orientation) {
if (checkedId==R.id.horizontal) {
orientation.setOrientation(LinearLayout.HORIZONTAL);
}
else {
orientation.setOrientation(LinearLayout.VERTICAL);
}
}
else if (group==gravity) {
if (checkedId==R.id.left) {
gravity.setGravity(Gravity.LEFT);
}
else if (checkedId==R.id.center) {
gravity.setGravity(Gravity.CENTER_HORIZONTAL);
}
else if (checkedId==R.id.right) {
gravity.setGravity(Gravity.RIGHT);
}
}
}
}

Dans onCreate(), nous recherchons nos deux conteneurs RadioGroup et nous enregistrons
un couteur pour chacun deux afin dtre prvenu du changement dtat des boutons radio
(setOnCheckedChangeListener(this)). Lactivit implmentant linterface OnCheckedChangeListener, elle se comporte elle-mme comme un couteur.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 51 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

51

Dans onCheckedChanged() (la mthode de rappel pour lcouteur), on recherche le


RadioGroup dont ltat a chang. Sil sagit du groupe orientation, on ajuste lorientation en fonction du choix de lutilisateur. Sil sagit du groupe gravity, on modifie la
gravit.
La Figure 7.2 montre ce quaffiche lapplication lorsquelle est lance dans lmulateur.
Figure 7.2
Lapplication LinearLayoutDemo lors
de son lancement.

Si lon clique sur le bouton vertical, le RadioGroup du haut sajuste en consquence


(voir Figure 7.3).
Figure 7.3
La mme application,
aprs avoir cliqu sur
le bouton vertical.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 52 Dimanche, 8. novembre 2009 12:23 12

52

Lart du dveloppement Android

Si lon clique sur les boutons centre ou droite, le RadioGroup du bas sajuste galement
(voir Figures 7.4 et 7.5).
Figure 7.4
La mme application,
avec les boutons
vertical et centre
cochs.

Figure 7.5
La mme application,
avec les boutons
vertical et droite
cochs.

Tout est relatif


Comme son nom lindique, le RelativeLayout place les widgets relativement aux autres
widgets du conteneur et de son conteneur parent. Vous pouvez ainsi placer le widget X en
dessous et gauche du widget Y ou faire en sorte que le bord infrieur du widget Z soit

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 53 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

53

align avec le bord infrieur du conteneur, etc. Ce gestionnaire de placement ressemble


donc au conteneur RelativeLayout1 de James Elliot pour Java Swing.

Concepts et proprits
Il faut pouvoir faire rfrence dautres widgets dans le fichier de description XML et
disposer dun moyen dindiquer leurs positions relatives.

Positions relatives un conteneur


Les relations les plus simples mettre en place sont celles qui lient la position dun widget
celle de son conteneur :

android:layout_alignParentTop prcise que le haut du widget doit tre align avec


celui du conteneur.

android:layout_alignParentBottom prcise que le bas du widget doit tre align


avec celui du conteneur.

android:layout_alignParentLeft prcise que le bord gauche du widget doit tre


align avec le bord gauche du conteneur.

android:layout_alignParentRight prcise que le bord droit du widget doit tre


align avec le bord droit du conteneur.

android:layout_centerHorizontal prcise que le widget doit tre centr horizontalement dans le conteneur.

android:layout_centerVertical prcise que le widget doit tre centr verticalement


dans le conteneur.

android:layout_centerInParent prcise que le widget doit tre centr horizontalement


et verticalement dans le conteneur.

Toutes ces proprits prennent soit la valeur true, soit la valeur false.
Info

Le remplissage du widget est pris en compte lors de ces alignements. Ceux-ci reposent sur la cellule globale du widget (cest--dire sur la combinaison de sa taille
naturelle et de son remplissage).

Notation relative dans les proprits


Les proprits restantes concernant RelativeLayout ont comme valeur lidentit dun
widget du conteneur. Pour ce faire :
1. Associez des identifiants (attributs android:id) tous les lments que vous aurez
besoin de dsigner, sous la forme @+id/....
1. http://www.onjava.com/pub/a/onjava/2002/09/18/relativelayout.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 54 Dimanche, 8. novembre 2009 12:23 12

54

Lart du dveloppement Android

2. Dsignez un widget en utilisant son identifiant, priv du signe plus (@id/...).


Si, par exemple, le widget A est identifi par @+id/widget_a, le widget B peut le dsigner
dans lune de ses proprits par @id/widget_a.

Positions relatives aux autres widgets


Quatre proprits permettent de contrler la position dun widget par rapport aux autres :

android:layout_above indique que le widget doit tre plac au-dessus de celui qui
est dsign dans cette proprit.

android:layout_below indique que le widget doit tre plac sous celui qui est dsign
dans cette proprit.

android:layout_toLeftOf indique que le widget doit tre plac gauche de celui


qui est dsign dans cette proprit.

android:layout_toRightOf indique que le widget doit tre plac droite de celui


qui est dsign dans cette proprit.

Cinq autres proprits permettent de contrler lalignement dun widget par rapport un
autre :

android:layout_alignTop indique que le haut du widget doit tre align avec le haut
du widget dsign dans cette proprit.

android:layout_alignBottom indique que le bas du widget doit tre align avec le


bas du widget dsign dans cette proprit.

android:layout_alignLeft indique que le bord gauche du widget doit tre align


avec le bord gauche du widget dsign dans cette proprit.

android:layout_alignRight indique que le bord droit du widget doit tre align


avec le bord droit du widget dsign dans cette proprit.

android:layout_alignBaseline indique que les lignes de base des deux widgets


doivent tre alignes.

La dernire proprit de cette liste permet daligner des labels et des champs afin que le
texte semble "naturel". En effet, les champs de saisie tant matrialiss par une bote,
contrairement aux labels, android:layout_alignTop alignerait le haut de la bote du
champ avec le haut du label, ce qui ferait apparatre le texte du label plus haut dans lcran
que le texte saisi dans le champ.
Si lon souhaite que le widget B soit plac droite du widget A, llment XML du
widget B doit donc contenir android:layout_toRight = "@id/widget_a" (o @id/
widget_a est lidentifiant du widget A).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 55 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

55

Ordre dvaluation
Lordre dvaluation complique encore les choses. En effet, Android ne lit quune seule
fois le fichier XML et calcule donc en squence la taille et la position de chaque widget.
Ceci a deux consquences :

On ne peut pas faire rfrence un widget qui na pas t dfini plus haut dans le fichier.

Il faut vrifier que lutilisation de la valeur fill_parent pour android:layout_width


ou android:layout_height ne "consomme" pas tout lespace alors que lon na pas
encore dfini tous les widgets.

Exemple
titre dexemple, tudions un "formulaire" classique, compos dun champ, dun label et
de deux boutons, OK et Annuler.
Voici le fichier de disposition XML du projet Containers/Relative :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5px">
<TextView android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="URL:"
android:paddingTop="15px"/>
<EditText
android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/label"
android:layout_alignBaseline="@id/label"/>
<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/entry"
android:layout_alignRight="@id/entry"
android:text="Ok" />
<Button
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Annuler" />
</RelativeLayout>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 56 Dimanche, 8. novembre 2009 12:23 12

56

Lart du dveloppement Android

Nous entrons dabord dans llment RelativeLayout. Ici, on veut utiliser toute la largeur
de lcran (android:layout_width = "fill_parent"), nutiliser que la hauteur ncessaire (android:layout_height = "wrap_content"), avec un remplissage de 5 pixels
entre les limites du conteneur et son contenu (android:padding = "5px").
Puis nous dfinissons un label assez basique, hormis son remplissage de 15 pixels
(android:padding = "15px"), que nous expliquerons plus loin.
Nous ajoutons ensuite le champ que nous voulons placer droite du label, avec sa ligne de
base aligne avec celle du label et nous faisons en sorte quil occupe le reste de cette
"ligne". Ces trois caractristiques sont gres par trois proprits :

android:layout_toRight = "@id/label" ;

android:layout_alignBaseline = "@id/label" ;

android:layout_width = "fill_parent".

Si nous navions pas utilis le remplissage de 15 pixels pour le label, le haut du champ
serait coup cause du remplissage de 5 pixels du conteneur lui-mme. En effet, la
proprit android: layout_alignBaseline = "@id/label" se contente daligner
les lignes de base du label et du champ. Par dfaut, le haut du label est align avec le haut
de son parent, or il est plus petit que le champ puisque ce dernier est entour dune bote.
Le champ dpendant de la position du label qui a dj t dfinie (puisquil apparat avant
lui dans le fichier XML), le champ serait trop haut et son bord suprieur serait rogn par le
remplissage du conteneur.
Vous rencontrerez probablement ce genre de problme lorsque vous voudrez mettre en
place des RelativeLayout pour quils apparaissent exactement comme vous le souhaitez.
La solution ce casse-tte consiste, comme nous lavons vu, ajouter 15 pixels de remplissage au-dessus du label, afin de le pousser suffisamment vers le bas pour que le champ ne
soit pas coup.
Voici quelques "solutions" qui ne marchent pas :

Vous ne pouvez pas utiliser android:layout_alignParentTop sur le champ car il


ne peut pas y avoir deux proprits qui tentent en mme temps de dfinir la position
verticale du champ. Ici, android:layout_alignParentTop entrerait en conflit avec la
proprit android:layout_alignBaseline = "@id/label", qui apparat plus bas, et
cest cette dernire qui lemporterait. Vous devez donc soit aligner le haut, soit aligner
les lignes de base, mais pas les deux.

Vous ne pouvez pas dabord dfinir le champ puis placer le label sa gauche car on ne
peut pas faire de "rfrence anticipe" vers un widget il doit avoir t dfini avant de
pouvoir y faire rfrence.

Revenons notre exemple. Le bouton OK est plac sous le champ (android:layout_below


= "@id/entry") et son bord droit est align avec le bord droit du champ

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 57 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

57

(android:layout_alignRight = "@id/entry"). Le bouton Annuler est plac gauche


du bouton OK (android:layout_toLeft = "@id/ok") et son bord suprieur est align
avec celui de son voisin (android:layout_alignTop = "@id/ok").
La Figure 7.6 montre le rsultat affich dans lmulateur lorsque lon se contente du code
Java produit automatiquement.
Figure 7.6
Lapplication RelativeLayoutDemo.

Tabula Rasa
Si vous aimez les tableaux HTML ou les feuilles de calcul, vous apprcierez le conteneur
TableLayout dAndroid car il vous permet de positionner les widgets dans une grille.
Vous pouvez ainsi dfinir le nombre de lignes et de colonnes, les colonnes qui peuvent se
rduire ou sagrandir en fonction de leur contenu, etc.
TableLayout fonctionne de concert avec le conteneur TableRow. Alors que TableLayout
contrle le comportement global du conteneur, les widgets eux-mmes sont placs dans un
ou plusieurs TableRow, raison dun par ligne de la grille.

Concepts et proprits
Pour utiliser ce conteneur, il faut savoir grer les widgets en lignes et en colonnes, et traiter
ceux qui sont placs lextrieur des lignes.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 58 Dimanche, 8. novembre 2009 12:23 12

58

Lart du dveloppement Android

Placement des cellules dans les lignes


Cest vous, le dveloppeur, qui dclarez les lignes en plaant les widgets comme des fils
dun lment TableRow, lui-mme fils dun TableLayout. Vous contrlez donc directement
la faon dont apparaissent les lignes dans le tableau.
Cest Android qui dtermine automatiquement le nombre de colonnes mais, en fait, vous
le contrlez de faon indirecte.
Il y aura au moins autant de colonnes quil y a de widgets dans la ligne la plus longue. Sil
y a trois lignes, par exemple une ligne avec deux widgets, une avec trois widgets et une
autre avec quatre widgets , il y aura donc au moins quatre colonnes.
Cependant, un widget peut occuper plusieurs colonnes si vous utilisez la proprit
android:layout_span en lui prcisant le nombre de colonnes sur lesquelles doit stendre le widget concern. Cette proprit ressemble donc lattribut colspan utilis dans les
tableaux HTML :
<TableRow>
<TextView android:text="URL:" />
<EditText
android:id="@+id/entry"
android:layout_span="3"/>
</TableRow>

Avec ce fragment XML, le champ stendra sur trois colonnes.


Gnralement, les widgets sont placs dans la premire colonne disponible. Dans lextrait
prcdent, par exemple, le label irait dans la premire colonne (la colonne 0 car leur numrotation commence 0) et le champ stendrait sur les trois colonnes suivantes (les colonnes 1 3). Vous pouvez galement placer un widget sur une colonne prcise en vous
servant de la proprit android:layout_column et en lui indiquant le numro de colonne
voulu :
<TableRow>
<Button
android:id="@+id/cancel"
android:layout_column="2"
android:text="Annuler" />
<Button android:id="@+id/ok" android:text="Ok" />
</TableRow>

Avec cet extrait, le bouton Annuler sera plac dans la troisime colonne (la colonne 2) et le
bouton OK, dans la colonne disponible suivante, cest--dire la quatrime.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 59 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

59

Fils de TableLayout qui ne sont pas des lignes


Gnralement, les seuls fils directs de TableLayout sont des lments TableRow. Cependant, vous pouvez galement placer des widgets entre les lignes. En ce cas, TableLayout
se comporte un peu comme un conteneur LinearLayout ayant une orientation verticale.
Les largeurs de ces widgets seront automatiquement fixes fill_parent pour remplir le
mme espace que la ligne la plus longue.
Un cas dutilisation de cette configuration consiste se servir dun widget View (<View
android:layout_height = "2px" android:background = "#0000FF" />) pour crer
une barre de sparation bleue de 2 pixels et de la mme largeur que le tableau.

Rduire, tirer et refermer


Par dfaut, la taille de chaque colonne sera la taille "naturelle" de son widget le plus large
(en tenant compte des widgets qui stendent sur plusieurs colonnes). Parfois, cependant,
cela ne donne pas le rsultat escompt et il faut alors intervenir plus prcisment sur le
comportement de la colonne.
Pour ce faire, vous pouvez utiliser la proprit android:stretchColumns de llment
TableLayout, dont la valeur peut tre un seul numro de colonne (dbutant zro) ou une
liste de numros de colonnes spars par des virgules. Ces colonnes seront alors tires
pour occuper tout lespace disponible de la ligne, ce qui est utile lorsque votre contenu est
plus troit que lespace restant.
Inversement, la proprit android:shrinkColumns de TableLayout, qui prend les
mmes valeurs, permet de rduire la largeur effective des colonnes en dcoupant leur
contenu en plusieurs lignes (par dfaut, le contenu des widgets nest pas dcoup).
Cela permet dviter quun contenu trop long pousse certaines colonnes droite de
lcran.
Vous pouvez galement tirer parti de la proprit android:collapseColumns de TableLayout, en indiquant l aussi un numro ou une liste de numros de colonnes. Celles-ci
seront alors initialement "refermes", ce qui signifie quelles napparatront pas, bien
quelles fassent partie du tableau. partir de votre programme, vous pouvez refermer ou
rouvrir les colonnes laide de la mthode setColumnCollapsed()du widget TableLayout. Ceci permet aux utilisateurs de contrler les colonnes importantes qui doivent
apparatre et celles qui peuvent tre caches car elles ne leur sont pas utiles.
En outre, les mthodes setColumnStretchable() et setColumnShrinkable() permettent
respectivement de contrler ltirement et la rduction des colonnes en cours dexcution.

Exemple
En combinant les fragments XML prsents dans cette section, on produit une grille de
widgets ayant la mme forme que celle de lexemple RelativeLayoutDemo, mais avec

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 60 Dimanche, 8. novembre 2009 12:23 12

60

Lart du dveloppement Android

une ligne de sparation entre la ligne label/champ et celle des boutons (ce projet se trouve
dans le rpertoire Containers/Table) :
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:text="URL:" />
<EditText android:id="@+id/entry"
android:layout_span="3"/>
</TableRow>
<View
android:layout_height="2px"
android:background="#0000FF" />
<TableRow>
<Button android:id="@+id/cancel"
android:layout_column="2"
android:text="Annuler" />
<Button android:id="@+id/ok"
android:text="Ok" />
</TableRow>
</TableLayout>

On obtient alors le rsultat montr la Figure 7.7.


Figure 7.7
Lapplication
TableLayoutDemo.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 61 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

61

ScrollView
Les crans des tlphones sont gnralement assez petits, ce qui oblige les dveloppeurs
employer quelques astuces pour prsenter beaucoup dinformations dans un espace rduit.
Lune de ces astuces consiste utiliser le dfilement, afin que seule une partie de linformation soit visible un instant donn, le reste tant disponible en faisant dfiler lcran
vers le haut ou vers le bas.
ScrollView est un conteneur qui fournit un dfilement son contenu. Vous pouvez donc
utiliser un gestionnaire de disposition qui peut produire un rsultat trop grand pour
certains crans et lenvelopper dans un ScrollView tout en continuant dutiliser la logique
de ce gestionnaire. Lutilisateur ne verra alors quune partie de votre prsentation et aura
accs au reste via des barres de dfilement.
Voici par exemple un lment ScrollView qui enveloppe un TableLayout (ce fichier
XML est extrait du rpertoire Containers/Scroll) :
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TableLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="0">
<TableRow>
<View
android:layout_height="80px"
android:background="#000000"/>
<TextView android:text="#000000"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#440000" />
<TextView android:text="#440000"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#884400" />

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 62 Dimanche, 8. novembre 2009 12:23 12

62

Lart du dveloppement Android

<TextView android:text="#884400"
android:paddingLeft="4px"
android:layout_gravity="center_vertical"
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#aa8844" />
<TextView android:text="#aa8844"
android:paddingLeft="4px"
android:layout_gravity="center_vertical"
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#ffaa88" />
<TextView android:text="#ffaa88"
android:paddingLeft="4px"
android:layout_gravity="center_vertical"
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#ffffaa" />
<TextView android:text="#ffffaa"
android:paddingLeft="4px"
android:layout_gravity="center_vertical"
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#ffffff" />
<TextView android:text="#ffffff"
android:paddingLeft="4px"
android:layout_gravity="center_vertical"
</TableRow>
</TableLayout>
</ScrollView>

/>

/>

/>

/>

/>

Sans le ScrollView, la grille occuperait au moins 560 pixels (sept lignes de 80 pixels
chacune, selon la dfinition de llment View). Certains terminaux peuvent avoir des
crans capables dafficher autant dinformations, mais la plupart seront plus petits. Le
ScrollView permet alors de conserver la grille tout en en prsentant quune partie la
fois.
La Figure 7.8 montre ce quaffichera lmulateur dAndroid au lancement de lactivit.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 63 Dimanche, 8. novembre 2009 12:23 12

Chapitre 7

Conteneurs

63

Figure 7.8
Lapplication ScrollViewDemo.

Vous remarquerez que lon ne voit que cinq lignes, ainsi quune partie de la sixime. En
pressant le bouton bas du pad directionnel, vous pouvez faire dfiler lcran afin de faire
apparatre les lignes restantes. Vous remarquerez galement que le bord droit du contenu
est masqu par la barre de dfilement pour viter ce problme, vous pourriez ajouter des
pixels de remplissage sur ce ct.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 64 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 65 Dimanche, 8. novembre 2009 12:23 12

8
Widgets de slection
Au Chapitre 6, nous avons vu que les champs pouvaient imposer des contraintes sur leur
contenu possible, afin de forcer une saisie uniquement numrique ou pour obliger saisir
un numro de tlphone, par exemple. Ce type de contrainte aide lutilisateur "faire ce
quil faut" lorsquil entre des informations, notamment lorsquil sagit dun terminal
mobile avec un clavier exigu.
La contrainte de saisie ultime consiste, videmment, ne proposer quune option possible
parmi un ensemble de choix, ce qui peut tre ralis laide des boutons radio que nous
avons dj prsents. Les kits de dveloppement graphiques disposent galement de listes
droulantes, et Android leur ajoute des widgets particulirement adapts aux dispositifs
mobiles (Gallery, par exemple, permet dexaminer les photographies stockes sur le
terminal).
En outre, Android permet de connatre aisment les choix qui sont proposs dans ces
widgets. Plus prcisment, il dispose dun ensemble dadaptateurs permettant de fournir
une interface commune toutes les listes de choix, que ce soient des tableaux statiques ou
des bases de donnes. Les vues de slection les widgets pour prsenter les listes de choix
sont transmises un adaptateur pour fournir les choix possibles.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 66 Dimanche, 8. novembre 2009 12:23 12

66

Lart du dveloppement Android

Sadapter aux circonstances


Dans labsolu, les adaptateurs offrent une interface commune pour diffrentes API. Plus
prcisment, dans le cas dAndroid, ils fournissent une interface commune au modle de
donnes sous-jacent dun widget de slection comme une liste droulante. Cette utilisation
des interfaces Java est assez classique (voir, par exemple, les adaptateurs de modles pour
JTable en Java/Swing), et Java nest pas le seul environnement fournir ce type
dabstraction (le framework XML de Flex accepte indiffremment du code XML en ligne
ou tlcharg partir dInternet).
Les adaptateurs dAndroid se chargent de fournir la liste des donnes dun widget de
slection et de convertir les diffrents lments en vues spcifiques pour quelles saffichent dans ce widget de slection. Ce dernier aspect des adaptateurs peut sembler un peu
curieux mais, en ralit, il nest pas si diffrent de ce que proposent les autres kits de dveloppement graphiques pour redfinir laffichage par dfaut. En Java/Swing, par exemple,
si vous souhaitez quune liste implmente par une JList soit, en ralit, une liste
cocher (o les diffrentes lignes sont composes dune case cocher et dun label et o les
clics modifient ltat de cette liste), vous finirez invitablement par appeler la mthode
setCellRenderer() pour disposer dun objet ListCellRenderer qui, son tour, permet
de convertir le contenu dune liste en widgets composites JCheckBox-plus-JLabel.

Utilisation dArrayAdapter
Ladaptateur le plus simple est ArrayAdapter puisquil suffit denvelopper un tableau ou
une instance de java.util.List pour disposer dun adaptateur prt fonctionner :
String[] items = {"ceci", "est", "une",
"liste", "vraiment", "stupide"};
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items);

Le constructeur dArrayAdapter attend trois paramtres :

le contexte dutilisation (gnralement, il sagit de linstance de lactivit) ;

lidentifiant de ressource de la vue utiliser ;

le tableau ou la liste dlments afficher.

Par dfaut, ArrayAdapter appellera la mthode toString() des objets de la liste et enveloppera chaque chane ainsi obtenue dans la vue dsigne par la ressource indique.
android.R.layout.simple_list_item_1 se contente de transformer ces chanes en
objets TextView qui, leur tour, safficheront dans la liste, le spinner ou tout widget qui
utilise cet ArrayAdapter. Vous pouvez confectionner vos propres vues en crant une sousclasse dArrayAdapter pour redfinir sa mthodegetView() :

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 67 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

67

public View getView(int position, View convertView,


ViewGroup parent) {
if (convertView==null) {
convertView=new TextView(this);
}
convertView.setText(buildStringFor(position));
return(convertView);
}

Ici, getView() reoit trois paramtres :

Lindice de llment du tableau que lon veut afficher dans la vue.

Une vue existante qui sera modifie avec les donnes cette position (si ce paramtre
vaut null, vous devrez crer votre propre instance).

Le widget qui contiendra cette vue, sil faut linstancier.

Dans lexemple prcdent, ladaptateur renvoie quand mme un objet TextView mais
utilise un comportement diffrent pour savoir quelle chane sera place dans la vue.
Le Chapitre 9 prsentera des ListView plus labores.

Autres adaptateurs essentiels


Voici dautres adaptateurs dont vous aurez certainement besoin :

CursorAdapter convertit un Cursor, gnralement fourni par un content provider, en


un objet pouvant safficher dans une vue de slection.

SimpleAdapter convertit les donnes trouves dans les ressources XML.

ActivityAdapter et ActivityIconAdapter fournissent les noms ou les icnes des


activits qui peuvent tre appeles lors dune intention particulire.

Listes des bons et des mchants


Le widget classique dAndroid pour les listes sappelle ListView. Pour disposer dune
liste compltement fonctionnelle, il suffit dinclure un objet ListView dans votre prsentation, dappeler setAdapter() pour fournir les donnes et les vues filles, puis dattacher
un couteur via setOnItemSelectedListener() pour tre prvenu de toute modification
de la slection.
Cependant, si votre activit est pilote par une seule liste, il peut tre prfrable que cette
activit soit une sous-classe de ListActivity plutt que de la classe de base Activity
traditionnelle. Si votre vue principale est uniquement constitue de la liste, vous navez
mme pas besoin de fournir de layout ListActivity construira pour vous une liste qui
occupera tout lcran. Vous pouvez toutefois personnaliser cette prsentation condition
didentifier cette ListView par @android:id/list, afin que ListActivity sache quelle
est la liste principale de lactivit.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 68 Dimanche, 8. novembre 2009 12:23 12

68

Lart du dveloppement Android

Voici, par exemple, le fichier de disposition du projet Selection/List :


<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
/>
</LinearLayout>

Comme vous pouvez le constater, il sagit simplement dune liste surmonte dun label qui
devra afficher en permanence la slection courante.
Le code Java permettant de configurer cette liste et de la connecter au label est le suivant :
public class ListViewDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items));
selection=(TextView)findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View v, int position,
long id) {
selection.setText(items[position]);
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 69 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

69

Vous pouvez configurer ladaptateur dune ListActivity par un appel setListAdapter()


ici, on fournit un ArrayAdapter qui enveloppe un tableau de chanes quelconques.
Pour tre prvenu des changements dans la liste de slection, on redfinit onListItemClick() pour quelle agisse de faon approprie en tenant compte de la vue fille et de la
position qui lui sont passes en paramtre (ici, elle crit dans le label le texte situ cette
position).
Le second paramtre de notre ArrayAdapter android.R.layout.simple_list_item_1
contrle laspect des lignes. La valeur utilise dans lexemple prcdent fournit une ligne
Android standard : grande police, remplissage important et texte en blanc.
Le rsultat est montr la Figure 8.1.
Figure 8.1
Lapplication
ListViewDemo.

Modes de slection
Par dfaut, ListView est simplement configure pour recevoir les clics sur les entres
de la liste. Cependant, on a parfois besoin quune liste mmorise un ou plusieurs choix
de lutilisateur ; ListView permet galement de le faire, au prix de quelques modifications.
Dans le code Java, vous devez dabord appeler la mthode setChoiceMode() de lobjet
ListView afin de configurer le mode de slection en lui passant en paramtre la constante
CHOICE_MODE_SINGLE ou CHOICE_MODE_MULTIPLE (pour obtenir lobjet ListView, il
suffit dappeler la mthode getListView() partir dune ListActivity).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 70 Dimanche, 8. novembre 2009 12:23 12

70

Lart du dveloppement Android

Puis, au lieu de passer en paramtre android.R.layout.simple_list_item_1 au


constructeur dArrayAdapter, vous devrez lui passer soit android.R.layout.simple_list_item_single_choice, soit android.R.layout.simple_list_item_multiple_choice pour mettre en place, respectivement, une liste choix unique ou
choix multiples.
Vous obtiendrez alors un rsultat comme celui des Figures 8.2 ou 8.3.
Figure 8.2
Liste choix unique.

Figure 8.3
Liste choix multiple.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 71 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

71

Pour connatre les choix de lutilisateur, utilisez la mthode getCheckedItemPositions()


de la ListView.

Contrle du Spinner
Le Spinner dAndroid est lquivalent des botes droulantes que lon trouve dans
certains kits de dveloppement (JComboBox en Java/Swing, par exemple). Appuyer sur le
bouton central du pad du terminal fait surgir une bote de slection permettant lutilisateur de faire son choix. On peut ainsi choisir dans une liste sans occuper tout lcran
comme avec une ListView, mais au prix dun clic supplmentaire ou dun pointage sur
lcran.
Comme pour ListView, on fournit ladaptateur pour les donnes et les vues filles via
setAdapter() et on accroche un couteur avec setOnItemSelectedListener().
Si lon souhaite personnaliser la vue daffichage de la bote droulante, il faut configurer
ladaptateur, pas le widget Spinner. Pour ce faire, on a donc besoin de la mthode
setDropDownViewResource() afin de fournir lidentifiant de la vue concerne.
Voici par exemple le fichier de disposition du projet Selection/Spinner, qui permet de
mettre en place une vue simple contenant un Spinner :
<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Spinner android:id="@+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true"
/>
</LinearLayout>

Il sagit de la mme vue que celle de la section prcdente, mais avec Spinner la place
de ListView. La proprit android:drawSelectorOnTop indique que la flche permettant de drouler la slection se trouvera droite du Spinner.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 72 Dimanche, 8. novembre 2009 12:23 12

72

Lart du dveloppement Android

Voici le code Java permettant de remplir et dutiliser le Spinner :


public class SpinnerDemo extends Activity
implements AdapterView.OnItemSelectedListener {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
Spinner spin=(Spinner)findViewById(R.id.spinner);
spin.setOnItemSelectedListener(this);
ArrayAdapter<String> aa=new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item,
items);
aa.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
spin.setAdapter(aa);
}
public void onItemSelected(AdapterView<?> parent,
View v, int position, long id) {
selection.setText(items[position]);
}
public void onNothingSelected(AdapterView<?> parent) {
selection.setText("");
}
}

Ici, cest lactivit elle-mme qui sert dcouteur de slection (spin.setOnItemSelectedListener(this)), ce qui est possible car elle implmente linterface OnItemSelectedListener.
On configure ladaptateur non seulement avec une liste de mots quelconques mais galement avec une ressource spcifique qui servira la vue droulante (via aa.setDropDownViewResource()). Vous remarquerez galement que lon utilise la vue prdfinie
android.R.layout.simple_spinner_item pour afficher les lments du Spinner.
Enfin, on implmente les mthodes de rappels ncessaires dOnItemSelectedListener
pour que le contenu du label volue en fonction du choix de lutilisateur.
On obtient ainsi le rsultat prsent aux Figures 8.4 et 8.5.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 73 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

73

Figure 8.4
Lapplication
SpinnerDemo lors
de son lancement.

Figure 8.5
La mme application
avec affichage de la liste
droulante du Spinner.

Mettez vos lions en cage


Comme son nom lindique, GridView vous offre une grille dans laquelle vous pouvez
disposer vos choix. Vous avez un contrle limit sur le nombre et la taille des colonnes ; le
nombre de lignes est dtermin dynamiquement en fonction du nombre dlments rendus
disponibles par ladaptateur fourni.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 74 Dimanche, 8. novembre 2009 12:23 12

74

Lart du dveloppement Android

Voici les proprits qui, combines, dterminent le nombre et la taille des colonnes :

android:numColumns indique le nombre de colonnes ; si sa valeur est auto_fit,


Android calculera ce nombre en fonction de lespace disponible et de la valeur des
autres proprits.

android:verticalSpacing et son homologue android:horizontalSpacing prcisent


lespace sparant les lments de la grille.

android:columnWidth indique la largeur de chaque colonne en pixels.

android:stretchMode indique, pour les grilles dont la valeur dandroid:numColumns est auto_fit, ce qui devra se passer lorsquil reste de lespace non occup par des
colonnes ou des espaces de sparation : si sa valeur est columnWidth, cet espace disponible sera pris par les colonnes ; si elle vaut spacingWidth, il sera absorb par lespacement entre les colonnes. Supposons, par exemple, que lcran fasse 320 pixels de
large, que la valeur dandroid:columnWidth soit de 100px et celle dandroid:horizontalSpacing, de 5px : trois colonnes occuperaient donc 310 pixels (trois colonnes
de 100 pixels et deux sparations de 5 pixels). Si android:stretchMode vaut columnWidth, les trois colonnes slargiront de 3-4 pixels pour utiliser les 10 pixels restants ;
si android:stretchMode vaut spacingWidth, les deux espacements slargiront
chacun de 5 pixels pour absorber ces 10 pixels.

Pour le reste, GridView fonctionne exactement comme nimporte quel autre widget de
slection on utilise setAdapter() pour fournir les donnes et les vues filles, on appelle
setOnItemSelectedListener() pour enregistrer un couteur de choix, etc.
Voici, par exemple, le fichier de disposition XML du projet Selection/Grid :
<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<GridView
android:id="@+id/grid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:verticalSpacing="35px"
android:horizontalSpacing="5px"
android:numColumns="auto_fit"
android:columnWidth="100px"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 75 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

75

android:stretchMode="columnWidth"
android:gravity="center"
/>
</LinearLayout>

Cette grille occupe tout lcran, sauf la partie rserve au label qui affiche la slection
courante. Le nombre de colonnes est calcul par Android (android:numColumns =
"auto_fit") partir dun espacement horizontal de 5 pixels (android:horizontalSpacing
= "5px") et dune largeur de colonne de 100 pixels (android:columnWidth = "100px"). Les
colonnes absorberont lespace restant disponible (android:stretchMode = "columnWidth").
Le code Java permettant de configurer cette grille est le suivant :
public class GridDemo extends Activity
implements AdapterView.OnItemSelectedListener {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
GridView g=(GridView)findViewById(R.id.grid);
g.setAdapter(new FunnyLookingAdapter(this,
android.R.layout.simple_list_item_1,
items));
g.setOnItemSelectedListener(this);
}
public void onItemSelected(AdapterView<?> parent, View v,
int position, long id) {
selection.setText(items[position]);
}
public void onNothingSelected(AdapterView<?> parent) {
selection.setText("");
}
private class FunnyLookingAdapter extends ArrayAdapter {
Context ctxt;
FunnyLookingAdapter(Context ctxt, int resource,
String[] items) {
super(ctxt, resource, items);
this.ctxt=ctxt;
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 76 Dimanche, 8. novembre 2009 12:23 12

76

Lart du dveloppement Android

public View getView(int position, View convertView,


ViewGroup parent) {
TextView label=(TextView)convertView;
if (convertView==null) {
convertView=new TextView(ctxt);
label=(TextView)convertView;
}
label.setText(items[position]);
return(convertView);
}
}
}

Au lieu dutiliser des widgets TextView automatiques pour les cellules de la grille, comme
dans les sections prcdentes, on cre nos propres vues qui hritent dArrayAdapter et
redfinissent getView(). Ici, on enveloppe les chanes dans nos propres widgets TextView, juste pour changer un peu. Notre getView() se contente de rutiliser le texte du
TextView qui lui est pass en paramtre ; si ce dernier vaut null, il en cre un et le remplit.
Avec lespacement vertical de 35 pixels indiqu dans le fichier de description
(android:verticalSpacing = "35"), la grille ne tiendra pas entirement dans lcran de
lmulateur (voir Figures 8.6 et 8.7).
Figure 8.6
Lapplication GridDemo
lors de son dmarrage.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 77 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

77

Figure 8.7
La mme application,
aprs dfilement
vers le bas.

Champs : conomisez 35 % de la frappe !


AutoCompleteTextView est une sorte dhybride dEditText (champ de saisie) et de
Spinner.
Avec lauto-compltion, le texte saisi par lutilisateur est trait comme un prfixe de
filtrage : il est compar une liste de prfixes candidats et les diffrentes correspondances
saffichent dans une liste de choix qui ressemble un Spinner. Lutilisateur peut alors
continuer sa saisie (si le mot nest pas dans la liste) ou choisir une entre de celle-ci pour
quelle devienne la valeur du champ.
AutoCompleteTextView tant une sous-classe dEditText, vous pouvez utiliser toutes
les proprits de cette dernire pour contrler son aspect la police et la couleur du texte,
notamment.
En outre, la proprit android:completionThreshold dAutoCompleteTextView permet
dindiquer le nombre minimal de caractres entrer avant que la liste de propositions
napparaisse.
Vous pouvez fournir AutoCompleteTextView un adaptateur contenant la liste des
valeurs candidates laide de setAdapter() mais, comme lutilisateur peut trs bien saisir
un texte qui nest pas dans cette liste, AutoCompleteTextView ne permet pas dutiliser les
couteurs de slection. Il est donc prfrable denregistrer un TextWatcher, exactement

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 78 Dimanche, 8. novembre 2009 12:23 12

78

Lart du dveloppement Android

comme nimporte quel EditText, pour tre prvenu lorsque le texte a t modifi. Ce type
dvnement est dclench par une saisie manuelle ou par une slection dans la liste des
propositions.
Voici, par exemple, le fichier de description du projet Selection/AutoComplete :
<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<AutoCompleteTextView android:id="@+id/edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:completionThreshold="3"/>
</LinearLayout>

Le code Java correspondant est le suivant :


public class AutoCompleteDemo extends Activity
implements TextWatcher {
TextView selection;
AutoCompleteTextView edit;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
edit=(AutoCompleteTextView)findViewById(R.id.edit);
edit.addTextChangedListener(this);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 79 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

79

edit.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
items));
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
selection.setText(edit.getText());
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
// impose par linterface, mais inutilise
}
public void afterTextChanged(Editable s) {
// impose par linterface, mais inutilise
}
}

Cette fois-ci, notre activit implmente linterface TextWatcher, ce qui signifie que nos
mthodes de rappel doivent se nommer onTextChanged() et beforeTextChanged(). Ici,
seule la premire nous intresse : elle modifie le label de slection pour quil reflte le
choix courant du champ AutoCompleteTextView.
Les Figures 8.8, 8.9 et 8.10 montrent ce quaffiche cette application.
Figure 8.8
Lapplication AutoCompleteDemo aprs
son dmarrage.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 80 Dimanche, 8. novembre 2009 12:23 12

80

Lart du dveloppement Android

Figure 8.9
La mme application
aprs avoir saisi quelques
lettres. La liste des propositions apparat dans une
liste droulante.

Figure 8.10
La mme application,
aprs avoir choisi le texte
suggr.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 81 Dimanche, 8. novembre 2009 12:23 12

Chapitre 8

Widgets de slection

81

Galeries
Le widget Gallery nexiste gnralement pas dans les autres kits de dveloppement
graphiques. En ralit, il sagit dune liste horizontale, o chaque choix dfile selon laxe
horizontal et o llment slectionn est mis en surbrillance. Sur un terminal Android,
lutilisateur peut parcourir les diffrents choix laide des boutons gauche et droit du pad.
Gallery prend moins de place lcran que ListView, tout en montrant plusieurs choix
la fois (pour autant quils soient suffisamment courts). Par rapport Spinner, Gallery
montre galement plusieurs choix simultanment.
Lexemple canonique dutilisation de Gallery consiste parcourir une galerie de photos
lutilisateur peut ainsi prvisualiser les imagettes correspondant une collection de photos
ou dicnes afin den choisir une.
Du point de vue du code, un objet Gallery fonctionne quasiment comme un Spinner ou
un GridView. Il dispose de plusieurs proprits :

android:spacing indique le nombre de pixels sparant les diffrents lments de la


liste.

android:spinnerSelector prcise ce qui indiquera une slection il peut sagir


dune rfrence un objet Drawable (voir le chapitre sur les ressources) ou une valeur
RGB de la forme #AARRGGBB ou quivalente.

android:drawSelectorOnTop indique si la barre de slection (ou le Drawable) doit


tre dessine avant (false) ou aprs (true) le dessin du fils slectionn. Si cette
proprit vaut true, assurez-vous que le slecteur soit suffisamment transparent pour
que lon puisse apercevoir le fils derrire lui ; sinon les utilisateurs ne pourront pas voir
ce quils ont choisi.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 82 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 83 Dimanche, 8. novembre 2009 12:23 12

9
Samuser avec les listes
Lhumble ListView est lun des widgets les plus importants et les plus utiliss dAndroid.
Que lon choisisse un contact tlphonique, un courrier faire suivre ou un ebook lire,
cest de ce widget dont on se servira le plus souvent. Mais il serait videmment plus agrable
dnumrer autre chose que du texte simple.
La bonne nouvelle est que les listes peuvent tre aussi amusantes quon le souhaite... dans
les limites de lcran dun mobile, videmment. Cependant, cette dcoration implique un
peu de travail et met en uvre certaines fonctionnalits dAndroid que nous prsenterons
dans ce chapitre.

Premires tapes
Gnralement, un widget ListView dAndroid est une simple liste de texte robuste mais
austre. Cela est d au fait que nous nous contentons de lui fournir un tableau de mots et
que nous demandons Android dutiliser une disposition simple pour afficher ces mots
sous forme de liste.
Cependant, vous pouvez galement crer une liste dicnes, dicnes et de texte, de cases
cocher et de texte, etc. Tout cela dpend des donnes que vous fournissez ladaptateur et
de laide que vous lui apportez pour crer un ensemble plus riche dobjets View pour
chaque ligne.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 84 Dimanche, 8. novembre 2009 12:23 12

84

Lart du dveloppement Android

Supposons, par exemple, que vous vouliez produire une liste dont chaque ligne est constitue dune icne suivie dun texte. Vous pourriez utiliser une disposition de ligne comme
celle du projet FancyLists/Static :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="22px"
android:paddingLeft="2px"
android:paddingRight="2px"
android:paddingTop="2px"
android:layout_height="wrap_content"
android:src="@drawable/ok"
/>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="44sp"
/>
</LinearLayout>

On utilise ici un conteneur LinearLayout pour crer une ligne contenant une icne
gauche et un texte (utilisant une grande police agrable lire) droite.
Cependant, par dfaut, Android ne sait pas que vous souhaitez utiliser cette disposition
avec votre ListView. Pour tablir cette connexion, vous devez donc indiquer ladaptateur
lidentifiant de ressource de cette disposition personnalise :
public class StaticDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(this,
R.layout.row, R.id.label,
items));

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 85 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

85

selection=(TextView)findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(items[position]);
}
}

On peut remarquer que cette structure gnrale est identique celle du projet Selection/
List du Chapitre 8.
Le point essentiel de cet exemple est que lon a indiqu ArrayAdapter que lon voulait
utiliser notre propre disposition de ligne (R.layout.row) et que le TextView contenant le
mot est dsign par R.id.label dans cette disposition. Noubliez pas que, pour dsigner
une disposition (row.xml), il faut prfixer le nom de base du fichier de description par
R.layout (R.layout.row).
On obtient ainsi une liste avec des icnes droite. Ici, comme le montre la Figure 9.1,
toutes les icnes sont les mmes.
Figure 9.1
Lapplication
StaticDemo.

Prsentation dynamique
Cette technique fournir une disposition personnalise pour les lignes permet de traiter
trs lgamment les cas simples, mais elle ne suffit plus pour les scnarios plus compliqus
comme ceux qui suivent :

Chaque ligne utilise une disposition diffrente (certaines ont une seule ligne de texte,
dautres deux, par exemple).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 86 Dimanche, 8. novembre 2009 12:23 12

86

Lart du dveloppement Android

Vous devez configurer chaque ligne diffremment (par exemple pour mettre des icnes
diffrentes en fonction des cas).

Dans ces situations, la meilleure solution consiste crer une sous-classe de lAdapter
voulu, redfinir getView() et construire soi-mme les lignes. La mthode
getView() doit renvoyer un objet View reprsentant la ligne situe la position fournie
par ladaptateur.
Reprenons par exemple le code prcdent pour obtenir, grce getView(), des icnes
diffrentes en fonction des lignes une icne pour les mots courts, une autre pour les mots
longs. Ce projet se trouve dans le rpertoire FancyLists/Dynamic des exemples :
public class DynamicDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new IconicAdapter(this));
selection=(TextView)findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(items[position]);
}
class IconicAdapter extends ArrayAdapter {
Activity context;
IconicAdapter(Activity context) {
super(context, R.layout.row, items);
this.context=context;
}
public View getView(int position, View convertView,
ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View row=inflater.inflate(R.layout.row, null);
TextView label=(TextView)row.findViewById(R.id.label);
label.setText(items[position]);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 87 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

87

if (items[position].length()>4) {
ImageView icon=(ImageView)row.findViewById(R.id.icon);
icon.setImageResource(R.drawable.delete);
}
return(row);
}
}
}

Le principe consiste redfinir getView() pour quelle renvoie une ligne dpendant de
lobjet afficher, qui est indiqu par lindice position dans lAdapter. Si vous examinez
le code de cette implmentation, vous remarquerez que lon utilise un objet LayoutInflater,
ce qui mrite une petite explication.

Quelques mots sur linflation


Dans notre cas, "inflation" dsigne le fait de convertir une description XML dans larborescence dobjets View quelle reprsente. Il sagit indubitablement dune partie de code
assez ennuyeuse : on prend un lment, on cre une instance de la classe View approprie ;
on examine tous les attributs pour les convertir en proprits, on parcourt tous les lments
fils et on recommence.
Heureusement, lquipe qui a cr Android a encapsul ce lourd traitement dans la classe
LayoutInflater. Pour nos listes personnalises, par exemple, nous voulons obtenir des
Views pour chaque ligne de la liste et nous pouvons donc utiliser la notation XML pour
dcrire laspect des lignes.
Dans lexemple prcdent, nous transformons la description R.layout.row que nous
avions cre dans la section prcdente. Cela nous donne un objet View qui, en ralit,
nest autre que notre LinearLayout contenant un ImageView et un TextView, exactement
comme cela est spcifi par R.layout.row. Cependant, au lieu de crer nous-mmes tous
ces objets et de les lier ensemble, le code XML et la classe LayoutInflater grent pour
nous les "dtails scabreux".

Revenons nos moutons


Nous avons donc utilis LayoutInflater pour obtenir un objet View reprsentant la ligne.
Cette ligne est "vide" car le fichier de description statique ne sait pas quelles sont les
donnes quelle recevra. Il vous appartient donc de la personnaliser et de la remplir
comme vous le souhaitez avant de la renvoyer. Cest la raison pour laquelle :

On place le texte du label dans notre widget label en utilisant le mot situ la position
passe en paramtre la mthode.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 88 Dimanche, 8. novembre 2009 12:23 12

88

Lart du dveloppement Android

On regarde si ce mot fait plus de quatre caractres, auquel cas on recherche le widget
ImageView de licne et on remplace sa ressource de base par une autre.

On dispose dsormais dune liste ListView contenant des icnes diffrentes, variant selon
les entres correspondantes de la liste (voir Figure 9.2).
Figure 9.2
Lapplication
DynamicDemo.

Il sagit bien sr dun exemple assez artificiel, mais cette technique peut servir personnaliser les lignes en fonction de nimporte quel critre le contenu des colonnes dun
Cursor, par exemple.

Mieux, plus robuste et plus rapide


Limplmentation de getView() que nous venons de prsenter fonctionne, mais elle est
peu efficace. En effet, chaque fois que lutilisateur fait dfiler lcran, on doit crer tout
un lot de nouveaux objets View pour les nouvelles lignes qui saffichent. Le framework
dAndroid ne mettant pas automatiquement en cache les objets View existants, il faut en
recrer de nouveaux, mme pour des lignes que lon avait cres trs peu de temps auparavant. Ce nest donc pas trs efficace, ni du point de vue de lutilisateur, qui risque de
constater que la liste est lente, ni du point de vue de la batterie chaque action du CPU
consomme de lnergie. Ce traitement supplmentaire est, par ailleurs, aggrav par la
charge que lon impose au ramasse-miettes (garbage collector) puisque celui-ci doit
dtruire tous les objets que lon cre. Par consquent, moins le code est efficace, plus la
batterie du tlphone se dcharge vite et moins lutilisateur est content. On doit donc
passer par quelques astuces pour viter ces dfauts.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 89 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

89

Utilisation de convertView
La mthode getView() reoit en paramtre un objet View nomm, par convention,
convertView. Parfois, cet objet est null, auquel cas vous devez crer une nouvelle View
pour la ligne (par inflation), comme nous lavons expliqu plus haut.
Si convertView nest pas null, en revanche, il sagit en fait de lune des View que vous avez
dj cres. Ce sera notamment le cas lorsque lutilisateur fait dfiler la ListView : mesure
que de nouvelles lignes apparaissent, Android tentera de rutiliser les vues des lignes qui
ont disparu lautre extrmit, vous vitant ainsi de devoir les reconstruire totalement.
En supposant que chaque ligne ait la mme structure de base, vous pouvez utiliser findViewById() pour accder aux diffrents widgets qui composent la ligne, modifier leur
contenu, puis renvoyer convertView partir de getView() au lieu de crer une ligne totalement nouvelle.
Voici, par exemple, une criture optimise de limplmentation prcdente de getView(),
extraite du projet FancyLists/Recycling :
public class RecyclingDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new IconicAdapter(this));
selection=(TextView)findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(items[position]);
}
class IconicAdapter extends ArrayAdapter {
Activity context;
IconicAdapter(Activity context) {
super(context, R.layout.row, items);
this.context=context;
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 90 Dimanche, 8. novembre 2009 12:23 12

90

Lart du dveloppement Android

public View getView(int position, View convertView,


ViewGroup parent) {
View row=convertView;
if (row==null) {
LayoutInflater inflater=context. getLayoutInflater();
row=inflater.inflate(R.layout.row, null);
}
TextView label=(TextView)row.findViewById(R.id.label);
label.setText(items[position]);
ImageView icon=(ImageView)row.findViewById(R.id.icon);
if (items[position].length()>4) {
icon.setImageResource(R.drawable.delete);
}
else {
icon.setImageResource(R.drawable.ok);
}
return(row);
}
}
}

Si convertView est null, nous crons une ligne par inflation ; dans le cas contraire, nous
nous contentons de la rutiliser. Le code pour remplir les contenus (image de licne, texte
du label) est identique dans les deux cas. On vite ainsi une tape dinflation potentiellement
coteuse lorsque convertView nest pas null.
Cependant, cette approche ne fonctionne pas toujours. Si, par exemple, une ListView
comprend des lignes ne contenant quune seule ligne de texte et dautres en contenant
plusieurs, la rutilisation des lignes existantes devient problmatique car les layouts
risquent dtre trs diffrents. Si lon doit crer une View pour une ligne qui compte deux
lignes de texte, par exemple, on ne peut pas se contenter de rutiliser une View avec une
seule ligne : il faut soit modifier les dtails internes de cette View, soit lignorer et en crer
une nouvelle.
Il existe bien entendu des moyens de grer ce type de problme, comme rendre la seconde
ligne de texte visible ou non en fonction des besoins, mais noubliez pas que chaque milliseconde dutilisation du CPU dun tlphone est prcieuse pour la fluidit de linterface,
mais surtout pour la batterie.
Ceci tant dit, surtout si vous dbutez avec Android, intressez-vous dabord obtenir la
fonctionnalit que vous dsirez et essayez ensuite doptimiser les performances lors dun

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 91 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

91

second examen de votre code. Ne tentez pas de tout rgler dun coup, sous peine de vous
noyer dans un ocan de View.

Utilisation du patron de conception "support"


Lappel de findViewById() est galement coteux : cette mthode plonge dans les lignes
de la liste pour en extraire les widgets en fonction de leurs identifiants, afin que lon puisse
en personnaliser le contenu (pour modifier le texte dun TextView, changer licne dun
ImageView, par exemple).
findViewById() pouvant trouver nimporte quel widget dans larbre des fils de la View
racine de la ligne, cet appel peut demander un certain nombre dinstructions pour
sexcuter, notamment si lon doit retrouver nouveau des widgets que lon a dj trouvs
auparavant.
Certains kits de dveloppement graphiques vitent ce problme en dclarant les View
composites, comme nos lignes, dans le code du programme (en Java, ici). Laccs aux
diffrents widgets ne consiste plus, alors, qu appeler une mthode daccs ou lire un
champ. Nous pourrions bien sr faire de mme avec Android, mais cela alourdirait le code.
Nous prfrons trouver un moyen de continuer utiliser le fichier de description XML tout
en mettant en cache les widgets fils essentiels de notre ligne, afin de ne devoir les rechercher
quune seule fois.
Cest l quentre en jeu le patron de conception "support", qui est implment par une
classe que nous appellerons ViewWrapper.
Tous les objets View disposent des mthodes getTag() et setTag(), qui permettent
dassocier un objet quelconque au widget. Le patron "support" utilise ce "marqueur" pour
dtenir un objet qui, son tour, dtient chaque widget fils intressant. En attachant le
support lobjet View de la ligne, on a accs immdiatement aux widgets fils qui nous
intressent chaque fois que lon utilise cette ligne, sans devoir appeler nouveau findViewById().
Examinons lune de ces classes support (extrait du projet FancyLists/ViewWrapper) :
class ViewWrapper {
View base;
TextView label=null;
ImageView icon=null;
ViewWrapper(View base) {
this.base=base;
}
TextView getLabel() {
if (label==null) {
label=(TextView)base.findViewById(R.id.label);
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 92 Dimanche, 8. novembre 2009 12:23 12

92

Lart du dveloppement Android

return(label);
}
ImageView getIcon() {
if (icon==null) {
icon=(ImageView)base.findViewById(R.id.icon);
}
return(icon);
}
}

ViewWrapper ne dtient pas seulement les widgets fils : elle les recherche uniquement si
elle ne les dtient pas dj. Si vous crez un wrapper et que vous nayez jamais besoin
dun fils prcis, il ny aura donc jamais aucun appel de findViewById() pour le retrouver
et vous naurez jamais payer le prix de ces cycles CPU inutiles.
Le patron "support" permet galement deffectuer les traitements suivants :

Il regroupe au mme endroit le transtypage de tous nos widgets, au lieu de le dissminer


dans chaque appel findViewById().

Il permet de mmoriser dautres informations sur les lignes, comme leur tat, que nous
ne voulons pas insrer dans le modle sous-jacent.

Lutilisation de ViewWrapper consiste simplement crer une instance de cette classe


chaque fois que lon cre une ligne par inflation et attacher cette instance la vue de la
ligne via setTag(), comme dans cette version de getView() :
public class ViewWrapperDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new IconicAdapter(this));
selection=(TextView)findViewById(R.id.selection);
}
private String getModel(int position) {
return(((IconicAdapter)getListAdapter()).getItem(position));
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 93 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

93

public void onListItemClick(ListView parent, View v,


int position, long id) {
selection.setText(getModel(position));
}
class IconicAdapter extends ArrayAdapter<String> {
Activity context;
IconicAdapter(Activity context) {
super(context, R.layout.row, items);
this.context=context;
}
public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
ViewWrapper wrapper=null;
if (row==null) {
LayoutInflater inflater=context. getLayoutInflater();
row=inflater.inflate(R.layout.row, null);
wrapper=new ViewWrapper(row);
row.setTag(wrapper);
}
else {
wrapper=(ViewWrapper)row.getTag();
}
wrapper.getLabel().setText(getModel(position));
if (getModel(position).length()>4) {
wrapper.getIcon().setImageResource(R.drawable.delete);
}
else {
wrapper.getIcon().setImageResource(R.drawable.ok);
}
return(row);
}
}
}

On teste si convertView est null pour crer au besoin les View de la ligne et lon rcupre
(ou lon cre) galement le ViewWrapper de celle-ci. Accder ensuite aux widgets fils
consiste simplement appeler les mthodes appropries du wrapper.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 94 Dimanche, 8. novembre 2009 12:23 12

94

Lart du dveloppement Android

Crer une liste...


Les listes avec de belles icnes sont jolies, mais ne pourrions-nous pas crer des widgets
ListView dont les lignes contiendraient des widgets fils interactifs, et non plus passifs
comme TextView et ImageView ? Pourrions-nous, par exemple, combiner une RatingBar
avec du texte afin de permettre lutilisateur de faire dfiler une liste de chansons et de les
valuer directement dans cette liste ?
Il y a une bonne et une mauvaise nouvelle.
La bonne est que lon peut mettre des widgets interactifs dans les lignes ; la mauvaise est
que cest un peu compliqu, notamment lorsquil faut intervenir parce que ltat du widget
interactif a chang (une valeur a t tape dans un champ, par exemple). Il faut en effet
stocker cet tat quelque part puisque notre widget RatingBar sera recycl lors du dfilement de la ListView. On doit pouvoir configurer ltat de la RatingBar en fonction du
mot qui est visible lorsque la RatingBar est recycle et sauvegarder cet tat, afin de le
restaurer plus tard lorsque cette ligne prcise redeviendra visible.
Par dfaut, la RatingBar na absolument aucune ide de la manire dont les donnes de
lArrayAdapter doivent tre affiches. Aprs tout, une RatingBar nest quun widget
utilis dans une ligne dune ListView. Nous devons donc apprendre aux lignes quels sont
les modles quelles affichent, afin quelles sachent quel tat de modle modifier lorsque
leurs barres dvaluation sont coches.
tudions lactivit du projet FancyLists/RateList. On utilisera ici les mmes classes de
base que celles de lexemple prcdent on affiche une liste de mots quelconques que lon
pourra valuer. Les mots ayant la note maximale apparatront tout en majuscules.
public class RateListDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
ArrayList<RowModel> list=new ArrayList<RowModel>();
for (String s : items) {
list.add(new RowModel(s));
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 95 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

95

setListAdapter(new CheckAdapter(this, list));


selection=(TextView)findViewById(R.id.selection);
}
private RowModel getModel(int position) {
return(((CheckAdapter)getListAdapter()).getItem(position));
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(getModel(position).toString());
}
class CheckAdapter extends ArrayAdapter<RowModel> {
Activity context;
CheckAdapter(Activity context, ArrayList<RowModel> list) {
super(context, R.layout.row, list);
this.context=context;
}
public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
ViewWrapper wrapper;
RatingBar rate;
if (row==null) {
LayoutInflater inflater=context. getLayoutInflater();
row=inflater.inflate(R.layout.row, null);
wrapper=new ViewWrapper(row);
row.setTag(wrapper);
rate=wrapper.getRatingBar();
RatingBar.OnRatingBarChangeListener l=
new RatingBar.OnRatingBarChangeListener() {
public void onRatingChanged(RatingBar ratingBar,
float rating,
boolean fromTouch) {
Integer myPosition=(Integer)ratingBar. getTag();
RowModel model=getModel(myPosition);
model.rating=rating;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 96 Dimanche, 8. novembre 2009 12:23 12

96

Lart du dveloppement Android

LinearLayout parent=(LinearLayout)ratingBar. getParent();


TextView label=(TextView)parent. findViewById(R.id.label);
label.setText(model.toString());
}
};
rate.setOnRatingBarChangeListener(l);
}
else {
wrapper=(ViewWrapper)row.getTag();
rate=wrapper.getRatingBar();
}
RowModel model=getModel(position);
wrapper.getLabel().setText(model.toString());
rate.setTag(new Integer(position));
rate.setRating(model.rating);
return(row);
}
}
class RowModel {
String label;
float rating=2.0f;
RowModel(String label) {
this.label=label;
}
public String toString() {
if (rating>=3.0) {
return(label.toUpperCase());
}
return(label);
}
}
}

Les diffrences entre cette activit et la prcdente sont les suivantes :

Bien que nous utilisions toujours un tableau de String pour stocker la liste des mots,
on le transforme en liste dobjets RowModel au lieu de le fournir un ArrayAdapter.
Ici, le RowModel est un ersatz de modle mutable car il se contente de combiner un mot

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 97 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

97

et son tat de slection. Dans un vrai systme, il pourrait sagir dobjets remplis partir
dun Cursor et les proprits auraient des significations mtier plus importantes.

Les mthodes utilitaires comme onListItemClick() doivent tre modifies pour


reflter les diffrences entre un modle String pur et lutilisation dun RowModel.

Dans getView(), la sous-classe dArrayAdapter (CheckAdapter) teste si convertView est null, auquel cas elle cre une nouvelle ligne par inflation dun layout simple
(voir le code qui suit) et lui attache un ViewWrapper. Pour la RatingBar de la ligne, on
ajoute un couteur onRatingChanged() anonyme qui examine le marqueur de la ligne
(getTag()) et le convertit en entier reprsentant la position dans lArrayAdapter de ce
quaffiche cette ligne. La barre dvaluation peut alors obtenir le bon RowModel pour la
ligne et mettre jour le modle en fonction de son nouvel tat. Elle modifie galement
le texte situ ct de la barre pour quil corresponde ltat de celle-ci.

On sassure toujours que la RatingBar a le bon contenu et dispose dun marqueur (via
setTag()) pointant vers la position de la ligne dans ladaptateur.

La disposition de la ligne est trs simple : elle contient une RatingBar et un label
TextView placs dans un conteneur LinearLayout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<RatingBar
android:id="@+id/rate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="3"
android:stepSize="1"
android:rating="2" />
<TextView
android:id="@+id/label"
android:paddingLeft="2px"
android:paddingRight="2px"
android:paddingTop="2px"
android:textSize="40sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

Le ViewWrapper est tout aussi simple, car il se contente dextraire ces deux widgets de la
View de la ligne :
class ViewWrapper {
View base;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 98 Dimanche, 8. novembre 2009 12:23 12

98

Lart du dveloppement Android

RatingBar rate=null;
TextView label=null;
ViewWrapper(View base) {
this.base=base;
}
RatingBar getRatingBar() {
if (rate==null) {
rate=(RatingBar)base.findViewById(R.id.rate);
}
return(rate);
}
TextView getLabel() {
if (label==null) {
label=(TextView)base.findViewById(R.id.label);
}
return(label);
}
}

Les Figures 9.3 et 9.4 montrent ce quaffiche cette application.


Figure 9.3
Lapplication
RateListDemo lors
de son dmarrage.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 99 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

99

Figure 9.4
La mme application,
montrant un mot avec
la note maximale.

Et la vrifier deux fois


La liste dvaluation de la section prcdente fonctionne, mais son implmentation est trs
lourde. Pire, lessentiel de ce code ennuyeux ne sera pas rutilisable, sauf dans des
circonstances trs limites.
Nous pouvons mieux faire.
En fait, nous voudrions pouvoir crer un layout comme celui-ci :
<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<com.commonsware.android.fancylists.seven.RateListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
/>
</LinearLayout>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 100 Dimanche, 8. novembre 2009 12:23 12

100

Lart du dveloppement Android

o toute la logique du code qui utilisait une ListView auparavant "fonctionnerait" avec la
RateListView du layout :
public class RateListViewDemo extends ListActivity {
TextView selection;
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"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items));
selection=(TextView)findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(items[position]);
}
}

Les choses se compliquent un tout petit peu lorsquon ralise que, jusqu maintenant, les
codes de ce chapitre nont jamais rellement modifi la ListView elle-mme. Nous
navons fait que travailler sur les adaptateurs, en redfinissant getView(), en crant nos
propres lignes par inflation, etc.
Si lon souhaite que RateListView prenne nimporte quel ListAdapter et fonctionne
"comme il faut", en plaant les barres dvaluation sur les lignes comme il se doit, nous
devons faire un peu de gymnastique. Plus prcisment, nous devons envelopper le ListAdapter "brut" dans un autre, qui sait comment placer les barres dans les lignes et mmoriser ltat de ces barres.
Nous devons dabord tablir le motif de conception dans lequel un ListAdapter en
augmente un autre. Voici le code dAdapterWrapper, qui prend en charge un ListAdapter et
dlgue toutes les mthodes de linterface delegate. Vous retrouverez ce code dans le
projet FancyLists/RateListView :
public class AdapterWrapper implements ListAdapter {
ListAdapter delegate=null;
public AdapterWrapper(ListAdapter delegate) {
this.delegate=delegate;
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 101 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

101

public int getCount() {


return(delegate.getCount());
}
public Object getItem(int position) {
return(delegate.getItem(position));
}
public long getItemId(int position) {
return(delegate.getItemId(position));
}
public View getView(int position, View convertView,
ViewGroup parent) {
return(delegate.getView(position, convertView, parent));
}
public void registerDataSetObserver(DataSetObserver observer) {
delegate.registerDataSetObserver(observer);
}
public boolean hasStableIds() {
return(delegate.hasStableIds());
}
public boolean isEmpty() {
return(delegate.isEmpty());
}
public int getViewTypeCount() {
return(delegate.getViewTypeCount());
}
public int getItemViewType(int position) {
return(delegate.getItemViewType(position));
}
public void unregisterDataSetObserver(DataSetObserver observer) {
delegate.unregisterDataSetObserver(observer);
}
public boolean areAllItemsEnabled() {
return(delegate.areAllItemsEnabled());
}
public boolean isEnabled(int position) {
return(delegate.isEnabled(position));
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 102 Dimanche, 8. novembre 2009 12:23 12

102

Lart du dveloppement Android

Nous pouvons alors hriter dAdapterWrapper pour crer RateableWrapper, qui redfinit
la mthode getView() tout en laissant le ListAdapter dlgu faire "le vrai travail" :
public class RateableWrapper extends AdapterWrapper {
Context ctxt=null;
float[] rates=null;
public RateableWrapper(Context ctxt, ListAdapter delegate) {
super(delegate);
this.ctxt=ctxt;
this.rates=new float[delegate.getCount()];
for (int i=0;i<delegate.getCount();i++) {
this.rates[i]=2.0f;
}
}
public View getView(int position, View convertView,
ViewGroup parent) {
ViewWrapper wrap=null;
View row=convertView;
if (convertView==null) {
LinearLayout layout=new LinearLayout(ctxt);
RatingBar rate=new RatingBar(ctxt);
rate.setNumStars(3);
rate.setStepSize(1.0f);
View guts=delegate.getView(position, null, parent);
layout.setOrientation(LinearLayout.HORIZONTAL);
rate.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.FILL_PARENT));
guts.setLayoutParams(new LinearLayout. LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT));
RatingBar.OnRatingBarChangeListener l=
new RatingBar.OnRatingBarChangeListener() {
public void onRatingChanged(RatingBar ratingBar,
float rating,
boolean fromTouch) {
rates[(Integer)ratingBar.getTag()]=rating;
}
};

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 103 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

103

rate.setOnRatingBarChangeListener(l);
layout.addView(rate);
layout.addView(guts);
wrap=new ViewWrapper(layout);
wrap.setGuts(guts);
layout.setTag(wrap);
rate.setTag(new Integer(position));
rate.setRating(rates[position]);
row=layout;
}
else {
wrap=(ViewWrapper)convertView.getTag();
wrap.setGuts(delegate.getView(position, wrap.getGuts(),
parent));
wrap.getRatingBar().setTag(new Integer(position));
wrap.getRatingBar().setRating(rates[position]);
}
return(row);
}
}

Lessentiel du traitement de notre liste dvaluation rside dans RateableWrapper. Cette


classe place les barres dvaluation sur les lignes et mmorise leurs tats lorsquelles sont
modifies par lutilisateur. Pour stocker ces tats, on utilise un tableau de float dont la
taille correspond au nombre de lignes que delegate rapporte pour cette liste.
Limplmentation de la mthode getView() de RateableWrapper rappelle celle de RateListDemo, sauf quau lieu dutiliser LayoutInflater on doit construire manuellement un
conteneur LinearLayout pour y placer notre RatingBar et les "tripes" (cest--dire toutes
les vues cres par delegate et que lon dcore avec une barre dvaluation). LayoutInflater est conue pour construire une View partir de widgets bruts or, ici, nous ne
savons pas par avance quoi ressembleront les lignes, hormis le fait quil faudra leur
ajouter une barre dvaluation. Le reste du code est semblable celui de RateListDemo :
class ViewWrapper {
ViewGroup base;
View guts=null;
RatingBar rate=null;
ViewWrapper(ViewGroup base) {
this.base=base;
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 104 Dimanche, 8. novembre 2009 12:23 12

104

Lart du dveloppement Android

RatingBar getRatingBar() {
if (rate==null) {
rate=(RatingBar)base.getChildAt(0);
}
return(rate);
}
void setRatingBar(RatingBar rate) {
this.rate=rate;
}
View getGuts() {
if (guts==null) {
guts=base.getChildAt(1);
}
return(guts);
}
void setGuts(View guts) {
this.guts=guts;
}
}

Lorsque tout ceci est en place, le code de RateListView devient assez simple :
public class RateListView extends ListView {
public RateListView(Context context) {
super(context);
}
public RateListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RateListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public void setAdapter(ListAdapter adapter) {
super.setAdapter(new RateableWrapper(getContext(), adapter));
}
}

On hrite simplement de ListView et on redfinit setAdapter() pour pouvoir envelopper


dans notre propre RateableWrapper le ListAdapter fourni en paramtre.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 105 Dimanche, 8. novembre 2009 12:23 12

Chapitre 9

Samuser avec les listes

105

Comme le montre la Figure 9.5, les rsultats sont identiques ceux de RateListDemo,
sauf que les mots ayant la note maximale napparaissent plus en majuscules.
Figure 9.5
Lapplication
RateListViewDemo.

La diffrence est la rutilisabilit. Nous pourrions crer un paquetage JAR de RateListView


et linsrer dans nimporte quel projet Android qui en a besoin. Par consquent, bien que
RateListView soit un peu plus complique crire, il ne faut plus le faire quune seule
fois et le reste du code de lapplication est merveilleusement simple.
Cette classe RateListView pourrait, bien sr, proposer des fonctionnalits supplmentaires, comme la possibilit de modifier par programmation ltat des barres (en mettant
jour le tableau de float et la RatingBar elle-mme), autoriser lappel dun autre code
lorsque ltat dune barre est modifi (via une fonction de rappel), etc. Nous les laissons en
exercice au lecteur.

Adapter dautres adaptateurs


Toutes les classes adaptateurs peuvent suivre le modle de conception consistant redfinir
getView() pour crer les lignes de la liste.
Cependant, CursorAdapter et ses sous-classes fournissent une implmentation par dfaut
de getView(), qui inspecte la View qui lui est passe en paramtre pour la recycler et
appelle newView() si elle est null, ou bindView() dans le cas contraire. Si vous tendez
CursorAdapter, qui sert afficher le rsultat dune requte adresse une base de
donnes ou un fournisseur de contenu, il est donc prfrable de redfinir newView() et
bindView() plutt que getView().

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 106 Dimanche, 8. novembre 2009 12:23 12

106

Lart du dveloppement Android

Pour cela, il suffit de supprimer le test if de getView() et de placer chaque branche de ce


test dans deux mthodes spares, comme celles-ci :
public View newView(Context context, Cursor cursor,
ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View row=inflater.inflate(R.layout.row, null);
ViewWrapper wrapper=new ViewWrapper(row);
row.setTag(wrapper);
bindView(row, context, cursor);
return(row);
}
public void bindView(View row, Context context, Cursor cursor) {
ViewWrapper wrapper=(ViewWrapper)row. getTag();
// Code pour remplir la ligne partir du Cursor
}

Lutilisation de Cursor sera dcrite au Chapitre 20.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 107 Dimanche, 8. novembre 2009 12:23 12

10
Utiliser de jolis widgets
et de beaux conteneurs
Les widgets et les conteneurs que nous avons prsents jusqu maintenant ne se trouvent
pas seulement dans la plupart des kits de dveloppement graphiques (sous une forme ou
sous une autre), mais sont galement trs utiliss dans le dveloppement des applications
graphiques, quil sagisse dapplications web, pour les PC ou pour les tlphones. Nous
allons maintenant nous intresser des widgets et des conteneurs un peu moins
frquents, mais nanmoins trs utiles.

Choisir
Avec des terminaux ayant des capacits de saisie limites comme les tlphones, il est
trs utile de disposer de widgets et de botes de dialogue capables danticiper ce que
lutilisateur veut taper. Cela minimise le nombre de frappes au clavier et de touches
lcran et rduit les risques derreur (la saisie dune lettre la place dun chiffre, par
exemple).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 108 Dimanche, 8. novembre 2009 12:23 12

108

Lart du dveloppement Android

Comme on la mentionn prcdemment, EditText peut forcer la saisie de nombres, de


numros de tlphone, etc. 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 dpart, sous la forme
dune anne, dun mois et dun jour. Les mois vont de 0 (janvier) 11 (dcembre). Vous
pouvez galement prciser un couteur (OnDateChangedListener ou OnDateSetListener)
qui sera inform lorsquune nouvelle date a t choisie. Il vous appartient de stocker cette date
quelque part, notamment si vous utilisez la bote de dialogue, car vous naurez pas dautre
moyen dobtenir ensuite la date choisie.
De mme, TimePicker et TimePickerDialog permettent dagir comme suit :

Fixer lheure initiale que lutilisateur peut ensuite ajuster, sous la forme dune heure
(de 0 23) et de minutes (de 0 59).

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).

Fournir un couteur (OnTimeChangedListener ou OnTimeSetListener) pour tre


prvenu du choix dune nouvelle heure, qui vous sera fournie sous la forme dune
heure et de minutes.

Le projet Fancy/Chrono utilise une disposition trs simple, forme dun label et de deux
boutons ceux-ci font surgir les botes 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"
/>
<Button android:id="@+id/timeBtn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Choisir une heure"
/>
</LinearLayout>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 109 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

109

La partie intressante se trouve dans le code Java :


public class ChronoDemo extends Activity {
DateFormat fmtDateAndTime=DateFormat. getDateTimeInstance();
TextView dateAndTimeLabel;
Calendar dateAndTime=Calendar.getInstance();
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();
}
};
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();
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.dateBtn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new DatePickerDialog(ChronoDemo.this,
d,
dateAndTime.get(Calendar.YEAR),
dateAndTime.get(Calendar.MONTH),
dateAndTime.get(Calendar.DAY_OF_MONTH)).show();
}
});
btn=(Button)findViewById(R.id.timeBtn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new TimePickerDialog(ChronoDemo.this,
t,
dateAndTime.get(Calendar.HOUR_OF_DAY),
dateAndTime.get(Calendar.MINUTE),
true).show();

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 110 Dimanche, 8. novembre 2009 12:23 12

110

Lart du dveloppement Android

}
});
dateAndTimeLabel=(TextView)findViewById(R.id.dateAndTime);
updateLabel();
}
private void updateLabel() {
dateAndTimeLabel.setText(fmtDateAndTime
.format(dateAndTime. getTime()));
}
}

Le "modle" de cette activit est simplement une instance de Calendar initialise avec la
date et lheure courantes et place dans la vue via un formateur DateFormat. La mthode
updateLabel() prend le Calendar courant, le formate et le place dans le TextView
correspondant au label.
Chaque bouton est associ un couteur OnClickListener qui se charge dafficher une
bote de dialogue DatePickerDialog ou TimePickerDialog selon le bouton sur lequel
on clique. On passe un couteur OnDateSetListener DatePickerDialog pour mettre
jour le Calendar avec la nouvelle date (anne, mois, jour). On lui passe galement la
dernire date choisie, en rcuprant les valeurs qui se trouvent dans le Calendar. TimePickerDialog, quant lui, reoit un couteur OnTimeSetListener pour mettre jour la
portion horaire du Calendar ; on lui passe galement la dernire heure choisie et true
pour indiquer que lon veut un format sur 24 heures.
Le rsultat de cette activit est prsent aux Figures 10.1, 10.2 et 10.3.
Figure 10.1
Lapplication
ChronoDemo lors
de son lancement.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 111 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

111

Figure 10.2
La mme application,
montrant le dialogue
de choix de la date.

Figure 10.3
La mme application,
montrant le dialogue
de choix de lheure.

Le temps scoule comme un fleuve


Les widgets DigitalClock ou AnalogClock permettent dafficher lheure sans autoriser
les utilisateurs la modifier. 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"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 112 Dimanche, 8. novembre 2009 12:23 12

112

Lart du dveloppement Android

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 par
android create project, on obtient lapplication prsente la Figure 10.4.
Figure 10.4
Lapplication
ClocksDemo.

Mesurer la progression
Si une opration doit durer un certain temps, vos utilisateurs doivent pouvoir :

utiliser un thread en arrire-plan (voir Chapitre 15) ;

tre tenus au courant de la progression de lopration, sous peine de penser que lactivit a un problme.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 113 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

113

Lapproche classique pour tenir les utilisateurs informs dune progression consiste utiliser une barre de progression ou un "disque tournant" (pensez lanimation qui apparat en
haut droite de la plupart des navigateurs web). Android dispose pour cela du widget
ProgressBar.
Une ProgressBar mmorise la progression, dfinie par un entier allant de 0 (aucune
progression) une valeur maximale dfinie par setMax() qui indique que lopration sest
termine. Par dfaut, une barre de progression part de la valeur 0, mais vous pouvez choisir
une autre valeur de dpart via un appel sa mthode setProgress().
Si vous prfrez que la barre soit indtermine, passez la valeur true sa mthode setIndeterminate().
Dans le code Java, vous pouvez fixer le montant de progression effectue (via setProgress()) ou incrmenter la progression courante dune valeur dtermine (via incrementProgressBy()). La mthode getProgress(), quant elle, permet de connatre la
progression dj effectue.
Le widget ProgressBar tant intimement li lutilisation des threads un thread en
arrire-plan effectue le traitement et informe de sa progression le thread qui gre linterface , nous prfrons reporter la dmonstration de ce widget jusquau Chapitre 15.

Utilisation donglets
La philosophie gnrale dAndroid consiste faire en sorte que les activits soient courtes
et agrables. Lorsquil y a plus dinformations que ne peut raisonnablement en contenir
lcran (bien que lon puisse utiliser des barres de dfilement), il peut tre prfrable de
produire ces informations supplmentaires par une autre activit, lance via une Intent,
comme on lexplique au Chapitre 24. Cependant, ceci peut tre assez compliqu mettre
en place. En outre, il arrive parfois que lon doive recueillir et traiter beaucoup dinformations en une seule opration.
Dans une interface graphique classique, nous pourrions utiliser des onglets cette fin,
comme un JTabbedPane en Java/Swing. Avec Android, on dispose dsormais dun conteneur TabHost qui fonctionne exactement de la mme faon une portion de ce quaffiche
lactivit est lie des onglets qui, lorsquon clique dessus, permettent de passer dune
partie de la vue une autre. Une activit utilisera par exemple un onglet pour la saisie dun
emplacement et un autre pour afficher cet emplacement sur une carte.
Certains kits de dveloppement considrent simplement les onglets comme des objets
cliquables, permettant de passer dune vue lautre. Dautres les considrent comme une
combinaison de llment cliquable et du contenu qui apparat lorsquon clique dessus.
Avec Android, les boutons et les contenus des onglets tant des entits distinctes, nous
emploierons les termes "bouton de longlet" et "contenu de longlet" dans cette section.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 114 Dimanche, 8. novembre 2009 12:23 12

114

Lart du dveloppement Android

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

TabHost est le conteneur gnral pour les boutons et les contenus des onglets.

TabWidget implmente la ligne des boutons des onglets, qui contient les labels et,
ventuellement, des icnes.

FrameLayout est le conteneur des contenus des onglets : chaque contenu donglet est
un fils du FrameLayout.

Cette approche ressemble celle de XUL, utilise par Mozilla : les lments tabbox, tabs et
tabpanels de XUL correspondent respectivement TabHost, TabWidget et FrameLayout.

Idiosyncrasies
La version actuelle dAndroid exige de respecter les rgles suivantes pour que ces trois
composants puissent fonctionner de concert :

Lidentifiant android:id du TabWidget doit tre @android:id/tabs.

Vous devez rserver un espace de remplissage dans le FrameLayout pour les boutons
des onglets.

Si vous souhaitez utiliser TabActivity, lidentifiant android:id du TabHost doit tre


@android:id/tabhost.

TabActivity, comme ListActivity, enveloppe un motif dinterface graphique classique


(une activit compose entirement donglets) dans une sous-classe dactivit. Vous nen
avez pas ncessairement besoin une activit classique peut trs bien utiliser galement
des onglets.
Pour une raison que jignore, TabWidget ne semble pas allouer son espace dans le conteneur TabHost ; en dautres termes, quelle que soit la valeur de la proprit
android:layout_height de TabWidget, FrameLayout lignore et le place au-dessus du
TabHost, provoquant ainsi le masquage des boutons des onglets par leurs contenus. Par
consquent, vous devez rserver assez despace dans FrameLayout (avec
android:paddingTop) pour "pousser" le contenu des onglets en dessous des boutons.
En outre, TabWidget semble toujours se dessiner en laissant de la place pour des icnes, mme
si lon nen utilise pas. Avec cette version du kit de dveloppement, vous devez donc rserver au moins 62 pixels ou ventuellement plus en fonction des icnes que vous utilisez.
Voici le fichier de description dune activit utilisant des onglets, tir du projet Fancy/Tab :
<?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"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 115 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

115

android:layout_height="fill_parent">
<TabHost android:id="@+id/tabhost"
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"
android:paddingTop="62px">
<AnalogClock android:id="@+id/tab1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerHorizontal="true"
/>
<Button android:id="@+id/tab2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Bouton semi-aleatoire"
/>
</FrameLayout>
</TabHost>
</LinearLayout>

Vous remarquerez que les lments TabWidget et FrameLayout sont des fils directs de
TabHost et que llment FrameLayout a lui-mme un fils reprsentant les diffrents
onglets. Ici, il y en a deux : une horloge et un bouton. Dans un scnario plus compliqu,
les onglets seraient regroups dans un conteneur (un LinearLayout, par exemple) avec
leurs propres contenus.

Code Java
Le code Java doit indiquer au TabHost quelles sont les vues qui reprsentent les contenus
des onglets et quoi doivent ressembler les boutons de ces onglets. Tout ceci est encapsul
dans des objets TabSpec. On rcupre une instance de TabSpec via la mthode newTabSpec() du TabHost, on la remplit puis on lajoute au TabHost dans le bon ordre.
Les deux mthodes essentielles de TabSpec sont les suivantes :

setContent(), qui permet dindiquer le contenu de cet onglet. Gnralement, il sagit


de lidentifiant android:id de la vue que lon veut montrer lorsque longlet est choisi.

setIndicator(), qui permet de fournir le titre du bouton de longlet. Cette mthode


est surcharge pour permettre de fournir galement un objet Drawable reprsentant
licne de longlet.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 116 Dimanche, 8. novembre 2009 12:23 12

116

Lart du dveloppement Android

Notez que les "indicateurs" des onglets peuvent, en fait, tre eux-mmes des vues, ce qui
permet de faire mieux quun simple label et une icne facultative.
Notez galement que vous devez appeler la mthode setup() de lobjet TabHost avant de
configurer les objets TabSpec. Cet appel nest pas ncessaire si votre activit drive de la
classe de base TabActivity.
Voici, par exemple, le code Java permettant de faire fonctionner les onglets de la section
prcdente :
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");
spec.setContent(R.id.tab1);
spec.setIndicator("Horloge");
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 mthode findViewById(), puis lon appelle
sa mthode setup().
Ensuite, on cre une instance de TabSpec via un appel newTabSpec() auquel on passe
un marqueur dont le but est encore inconnu. On appelle les mthodes setContent() et
setIndicator() de cette instance, puis la mthode addTab() de lobjet TabHost pour lui
ajouter le TabSpec. Enfin, on choisit longlet qui saffichera initialement, laide de la
mthode setCurrentTab() (la valeur 0 dsigne le premier onglet).
Les Figures 10.5 et 10.6 montrent ce quaffiche cette application.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 117 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

117

Figure 10.5
Lapplication TabDemo
affichant son premier
onglet.

Figure 10.6
La mme application
affichant son second
onglet.

Ajouts dynamiques
TabWidget est configur pour simplifier la dfinition des onglets au moment de la compilation. Cependant, vous voudrez parfois crer des onglets au cours de lexcution de votre
activit. Imaginons, par exemple, un client de courrier o les diffrents e-mails souvrent
dans leurs propres onglets, afin de faciliter le passage dun message lautre. Dans cette
situation, vous ne pouvez pas savoir lavance le nombre donglets ni leur contenu : vous
devez attendre que lutilisateur ouvre un message de courrier.
Heureusement, Android permet galement dajouter dynamiquement des onglets en cours
dexcution. Cet ajout fonctionne exactement comme on vient de le voir, sauf quil faut

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 118 Dimanche, 8. novembre 2009 12:23 12

118

Lart du dveloppement Android

utiliser une autre variante de setContent() qui prend en paramtre une instance de
TabHost.TabContentFactory qui est simplement une mthode de rappel qui sera appele
automatiquement : il suffit de fournir une implmentation de createTabContent() et de
lutiliser pour construire et renvoyer la vue qui deviendra le contenu de longlet.
Cette approche est prsente dans le projet Fancy/DynamicTab.
La description de linterface de lactivit met en place les onglets et nen dfinit quun
seul, contenant un simple bouton :
<?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">
<TabHost android:id="@+id/tabhost"
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"
android:paddingTop="62px">
<Button android:id="@+id/buttontab"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Bouton semi-aleatoire"
/>
</FrameLayout>
</TabHost>
</LinearLayout>

Nous voulons maintenant ajouter de nouveaux onglets mesure quon clique sur ce bouton,
ce qui se ralise en quelques lignes de code :
public class DynamicTabDemo extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
final TabHost 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);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 119 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

119

tabs.setCurrentTab(0);
Button btn=(Button)tabs.getCurrentView().findViewById(R.id.buttontab);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
TabHost.TabSpec spec=tabs.newTabSpec("tag1");
spec.setContent(new TabHost.TabContentFactory() {
public View createTabContent(String tag) {
return(new AnalogClock(DynamicTabDemo.this));
}
});
spec.setIndicator("Horloge");
tabs.addTab(spec);
}
});
}
}

On cre un objet TabHost.TabSpec dans la mthode de rappel setOnClickListener()


de notre bouton en lui passant en paramtre une fabrique TabHost.TabContentFactory
anonyme. Cette fabrique, son tour, renvoie la vue qui sera utilise pour longlet ici une
horloge analogique. Le code de construction de cette vue pourrait tre bien plus labor et
utiliser, par exemple, un LayoutInflater pour crer une vue partir dun fichier de
description XML.
La Figure 10.7 montre que lactivit naffiche quun seul onglet lorsquelle est lance. La
Figure 10.8 montre plusieurs onglets, crs en cours dexcution.
Figure 10.7
Lapplication DynamicTabDemo avec son unique
onglet initial.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 120 Dimanche, 8. novembre 2009 12:23 12

120

Lart du dveloppement Android

Figure 10.8
La mme application,
aprs la cration
de trois onglets en cours
dexcution.

Intent et View
Dans les exemples prcdents, le contenu de chaque onglet tait une View : un Button, par
exemple. Ce type de configuration est simple mettre en place, mais ce nest pas le seul :
vous pouvez galement intgrer une autre activit partir de votre application via une
Intent.
Les Intent permettent de prciser ce que vous voulez raliser, puis de demander
Android de trouver un moyen de laccomplir, ce qui force souvent lactivit se multiplier. chaque fois que vous lancez une application partir du lanceur dapplications
dAndroid, par exemple, ce lanceur cre une Intent et laisse Android ouvrir lactivit
associe. Ce concept, ainsi que le placement des activits dans des onglets, sera dcrit au
Chapitre 25.

Tout faire basculer


Parfois, on souhaite bnficier de lavantage des onglets (ne voir que certaines vues la
fois) sans pour autant utiliser leur prsentation graphique (parce que, par exemple, les
onglets prennent trop de place lcran). On peut ainsi prfrer passer dune vue lautre
par un mouvement du doigt sur lcran ou en secouant le terminal.
La bonne nouvelle est que le mcanisme interne des onglets pour basculer entre les vues
est disponible dans le conteneur ViewFlipper, qui peut tre utilis diffremment dun
onglet traditionnel.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 121 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

121

ViewFlipper hrite de FrameLayout, que nous avons utilis plus haut pour dcrire le
fonctionnement interne dun TabWidget. Cependant, il ne montre que la premire vue fille
au dpart : cest vous quil appartient de mettre en place le basculement entre les vues,
soit manuellement par une action de lutilisateur, 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"
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="Basculemoi !"
/>
<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 dfinit trois vues filles de ViewFlipper, chacune tant un TextView contenant
un simple message. Vous pourriez videmment choisir des vues plus complexes.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 122 Dimanche, 8. novembre 2009 12:23 12

122

Lart du dveloppement Android

Pour basculer manuellement entre les vues, nous devons ajouter un couteur au bouton
pour que le basculement ait lieu lorsquon clique dessus :
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);
Button btn=(Button)findViewById(R.id.flip_me);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
flipper.showNext();
}
});
}
}

Il sagit simplement dappeler la mthode showNext() de ViewFlipper, comme pour


nimporte quelle classe ViewAnimator.
Le rsultat est une activit trs simple : un clic sur le bouton fait apparatre le TextView suivant,
en rebouclant sur le premier lorsquils se sont tous affichs (voir Figures 10.9 et 10.10).
Figure 10.9
Lapplication Flipper1
montant le premier
panneau.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 123 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

123

Figure 10.10
La mme application,
aprs basculement vers
le second panneau.

Nous pourrions bien sr grer tout cela plus simplement en utilisant un seul TextView et
en modifiant son texte et sa couleur chaque clic. Cependant, vous pouvez imaginer que le
contenu du ViewFlipper pourrait tre bien plus compliqu inclure, par exemple, tout ce
que lon peut mettre dans un TabView.
Comme pour un TabWidget, le contenu dun ViewFlipper peut ne pas tre connu lors de
la compilation et, comme pour un TabWidget, il est relativement simple dajouter du contenu
la vole.
Voici, par exemple, le layout dune 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>

Vous remarquerez que llment ViewFlipper na aucun contenu au moment de la compilation. Notez galement quil ny a pas de bouton pour basculer entre les contenus nous
reviendrons sur ce point dans un instant.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 124 Dimanche, 8. novembre 2009 12:23 12

124

Lart du dveloppement Android

Pour le contenu du ViewFlipper, nous crerons de gros boutons contenant, chacun, un


ensemble de mots quelconques. Nous configurerons galement le ViewFlipper pour quil
boucle automatiquement sur ces widgets, en utilisant une animation pour la transition :
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);
flipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_left_in));
flipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_left_out));
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();
}
}

Aprs avoir obtenu le widget ViewFlipper partir du fichier de disposition, nous


commenons par configurer les animations dentre et de sortie. En termes Android, une
animation est une description de la sortie dun widget ("out") et de son entre ("in") dans
la zone visible de lcran. Les animations sont, toutefois, un sujet complexe, mritent leur
propre chapitre et ne seront donc pas dcrites ici. Pour le moment, il suffit de savoir quil
sagit de ressources stockes dans le rpertoire res/anim/ du projet. Dans cet exemple,
nous utilisons deux exemples fournis par le SDK et publis sous les termes de la licence

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 125 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

125

Apache 2.0. Comme leur nom lindique, les widgets sont "pousss" gauche pour entrer
ou sortir de la zone visible.
Aprs avoir parcouru tous les mots en les transformant en autant de boutons fils de lobjet
ViewFlipper, nous configurons ce dernier pour quil bascule automatiquement entre ses fils
(flipper.setFlipInterval(2000);) et nous lanons le basculement (flipper.startFlipping();).
Le rsultat est une suite sans fin de boutons apparaissant puis disparaissant vers la gauche
au bout de 2 secondes, en tant remplacs chaque fois par le bouton suivant de la
squence. Lensemble revient au premier bouton aprs la disparition du dernier (voir
Figure 10.11).
Figure 10.11
Lapplication Flipper2,
avec une transition
anime.

Ce basculement automatique est utile pour les panneaux dinformation ou les autres situations dans lesquelles vous voulez afficher beaucoup dinformations dans un espace rduit.
Ces diffrentes vues basculant automatiquement de lune lautre, il serait risqu de
demander aux utilisateurs dinteragir avec elles une vue pourrait disparatre au milieu
dune interaction.

Fouiller dans les tiroirs


Depuis longtemps, les dveloppeurs Android rclamaient un conteneur de type tiroir, fonctionnant comme celui de lcran daccueil, qui contient les icnes pour lancer les applications. Limplmentation officielle existait dans le code open-source mais ntait pas

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 126 Dimanche, 8. novembre 2009 12:23 12

126

Lart du dveloppement Android

intgre dans le SDK... jusqu Android 1.5, qui fournit dsormais le widget SlidingDrawer.
la diffrence de la plupart des autres conteneurs, SlidingDrawer change daspect
puisquil passe dune position ferme une position ouverte. Cette caractristique implique quelques restrictions sur le conteneur dans lequel peut se trouver le SlidingDrawer
puisquil doit permettre plusieurs widgets de cohabiter les uns au-dessus des autres.
RelativeLayout et FrameLayout satisfont cette exigence FrameLayout est un conteneur conu spcialement pour empiler les widgets les uns sur les autres. LinearLayout,
en revanche, ne permet pas dempiler des widgets (ils sont placs les uns aprs les autres,
en ligne ou en colonne) , cest la raison pour laquelle un SlidingDrawer ne doit pas tre
un fils direct dun lment 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="Im in here!"
/>
</SlidingDrawer>
</FrameLayout>

Le SlidingDrawer doit contenir :

une poigne le plus souvent un ImageView, comme ici ;

le contenu du tiroir lui-mme gnralement un conteneur.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 127 Dimanche, 8. novembre 2009 12:23 12

Chapitre 10

Utiliser de jolis widgets et de beaux conteneurs

127

En outre, SlidingDrawer doit connatre les valeurs android:id de la poigne et du


contenu en les stockant, respectivement, dans ses attributs android:handle et
android:content. Cela permet au tiroir de savoir comment sanimer lorsquil souvre ou
se ferme.
La Figure 10.12 montre laspect du tiroir ferm, avec la poigne quon lui a fournie. La
Figure 10.13 montre le tiroir ouvert, avec son contenu.
Figure 10.12
Lapplication
DrawerDemo avec
son tiroir ferm.

Figure 10.13
La mme application
avec le tiroir ouvert.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 128 Dimanche, 8. novembre 2009 12:23 12

128

Lart du dveloppement Android

Comme on pourrait sy attendre, on peut ouvrir et refermer le tiroir en traitant les vnements "touchs" partir du code Java. Il existe deux groupes de mthodes : les premires
agissent instantanment (open(), close() et toggle()), les autres utilisent une animation
(animateOpen(), animateClose() et animateToggle()). Le tiroir se verrouille avec
lock() et se dverrouille avec unlock() ; lorsquil est verrouill, le tiroir ne rpond pas
aux touchs sur lcran.
Vous pouvez galement, si vous le souhaitez, enregistrer trois types de mthodes de rappel :

un couteur qui sera appel lors de louverture du tiroir ;

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

un couteur qui sera appel lorsque le tiroir "dfile" (cest--dire lorsque lutilisateur
tire ou repousse la poigne).

Le SlidingDrawer du lanceur, par exemple, change licne de sa poigne pour quelle


signifie "ouvrir", "fermer" ou "supprimer" (lorsque lon touche pendant un certain temps
une icne du bureau). Pour ce faire, il utilise notamment des mthodes 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 lcran : en dautres termes, si vous faites pivoter le terminal
ou lmulateur pendant quil excute DrawerDemo, le tiroir souvrira toujours en partant du
bas il ne "colle" pas toujours au bord par rapport celui auquel il sest ouvert. Pour que
le tiroir souvre toujours du mme ct, comme le lanceur, vous aurez besoin de layouts
diffrents pour le mode portrait et le mode paysage un sujet que nous aborderons au
Chapitre 19.

Autres conteneurs intressants


Android fournit galement le conteneur AbsoluteLayout, dont le contenu est dispos en
fonction de coordonnes spcifiques on lui indique o placer un fils en prcisant ses
coordonnes X, Y, et Android le positionne cet endroit sans poser de question. Ceci a
lavantage de fournir un positionnement prcis ; en revanche, cela signifie galement que
les vues nauront un aspect correct que sur des crans dune certaine dimension, moins
dcrire beaucoup de code pour ajuster les coordonnes en fonction de la taille de lcran.
Les crans Android pouvant avoir nimporte quelle taille et ces tailles voluant continuellement, lutilisation dAbsoluteLayout risque de devenir assez problmatique.
Android dispose galement dune nouvelle variante de liste, ExpandableListView, qui
fournit une reprsentation arborescente simplifie autorisant deux niveaux de profondeur :
les groupes et les fils. Les groupes contiennent les fils, qui sont les "feuilles" de larbre.
Cette liste ncessite un nouvel ensemble dadaptateurs car la famille ListAdapter ne
fournit aucune information de groupage pour les lments dune liste.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 129 Dimanche, 8. novembre 2009 12:23 12

11
Utilisation des menus
Les activits Android peuvent comprendre des menus, comme les applications de bureau
et de certains systmes mobiles Palm OS et Windows Mobile, notamment. Certains
terminaux Android disposent mme dune touche ddie leur ouverture ; les autres
offrent dautres moyens pour les faire apparatre.
En outre, vous pouvez crer des menus contextuels, comme dans la plupart des kits de
dveloppement graphiques. Dans une interface utilisateur classique, ce type de menu
souvre en cliquant sur le bouton droit de la souris ; sur les terminaux mobiles, ils apparaissent gnralement lorsque lutilisateur touche un widget pendant un certain temps. Si
un TextView dispose dun menu contextuel et que le terminal a un cran tactile, par exemple, vous pouvez toucher le label pendant une seconde ou deux : un menu surgira alors
pour vous permettre de choisir lune des entres du menu.
La construction des menus Android est diffrente de celle de la plupart des autres kits de
dveloppement. Bien que vous puissiez ajouter des lments un menu, vous navez pas
un contrle total sur son contenu ni sur le moment o il est construit. Une partie des menus
est, en effet, dfinie par le systme et cette portion est gre par Android lui-mme.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 130 Dimanche, 8. novembre 2009 12:23 12

130

Lart du dveloppement Android

Variantes de menus
Android distingue les "menus doptions" des "menus contextuels". Les premiers se
dclenchent en appuyant sur le bouton "Menu" du terminal, tandis que les seconds
souvrent lorsquon maintient une pression du doigt sur le widget qui dispose de ce menu.
En outre, les menus doption fonctionnent selon deux modes : icne et tendu. Lorsque
lutilisateur appuie sur le bouton Menu, le menu est en mode icne et naffiche que les six
premiers choix sous la forme de gros boutons faciles slectionner, disposs en ligne en
bas de lcran. Si ce menu compte plus de six choix, le sixime bouton affiche "Plus"
cliquer sur cette option fait passer le menu en mode tendu, qui affiche tous les choix
restants. Lutilisateur peut bien sr faire dfiler le menu afin deffectuer nimporte quel
choix.

Les menus doptions


Au lieu de construire le menu doptions de votre activit dans onCreate(), comme le
reste de votre interface graphique, vous devez implmenter la mthode onCreateOptionsMenu() en lui passant une instance de la classe Menu.
La premire opration raliser consiste tablir un chanage vers la mthode de la superclasse (via super.onCreateOptionsMenu(menu)), afin quAndroid puisse lui ajouter les
choix ncessaires. Puis vous pouvez ajouter les vtres, comme on lexpliquera bientt.
Si vous devez modifier le menu au cours de lactivit (pour, par exemple, dsactiver un
choix devenu inadquat), il suffit de manipuler linstance de Menu que vous avez transmise
onCreateOptionsMenu() ou dimplmenter la mthode onPrepareOptionsMenu(), qui
est appele avant chaque affichage du menu.
Pour ajouter des choix au menu, utilisez la mthode add(). Celle-ci est surcharge pour
pouvoir recevoir des combinaisons des paramtres suivants :

Un identifiant de groupe (un entier), qui doit tre NONE lorsque vous ne regroupez pas
un ensemble doptions de menu, utilisable avec setGroupCheckable().

Un identifiant de choix (galement un entier) servant identifier la slection dans la


mthode de rappel onOptionsItemSelected() lorsquune option du menu a t
choisie.

Un identifiant dordre (encore un entier), indiquant lemplacement du choix dans le


menu lorsque ce dernier contient des options ajoutes par Android pour linstant,
contentez-vous dutiliser NONE.

Le texte du choix, sous la forme dune String ou dun identifiant de ressource.

Toutes les mthodes add() renvoient une instance de MenuItem qui vous permet ensuite
de modifier tous les rglages du choix concern (son texte, par exemple). Vous pouvez

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 131 Dimanche, 8. novembre 2009 12:23 12

Chapitre 11

Utilisation des menus

131

galement mettre en place un raccourci pour un choix un mnmonique dun seul caractre, permettant de slectionner ce choix lorsque le menu est visible. Android permet
dutiliser des raccourcis alphabtiques et numriques, mis en place respectivement par
setAlphabeticShortcut() et setNumericShortcut(). Le menu est plac en mode
raccourci alphabtique en passant le paramtre true sa mthode setQwertyMode().
Les identifiants de choix et de groupe sont des cls servant dverrouiller certaines fonctionnalits supplmentaires des menus :

Un appel MenuItem#setCheckable() avec un identifiant de choix ajoute une case


cocher ct du titre de ce choix. La valeur de cette case bascule lorsque lutilisateur
slectionne ce choix.

Un appel Menu#setGroupCheckable() avec un identifiant de groupe permet de


rendre un ensemble de choix mutuellement exclusifs en leur associant des boutons
radio, afin de ne pouvoir cocher quune seule option du groupe un instant donn.

Vous pouvez galement appeler addIntentOptions() pour remplir le menu avec des
choix correspondant aux activits dun Intent (voir Chapitre 25).
Enfin, il est possible de crer des sous-menus la vole, en appelant addSubMenu() avec
les mmes paramtres que ceux daddMenu(). Android appellera alors onCreatePanelMenu() en lui passant lidentifiant du choix du sous-menu, ainsi quune autre instance de
Menu reprsentant le sous-menu lui-mme. Comme avec onCreateOptionsMenu(), vous
devez tablir un chanage avec la mthode de la superclasse, puis ajouter les choix au
sous-menu. La seule restriction est que vous ne pouvez pas imbriquer sans fin les sousmenus : un menu peut avoir un sous-menu, mais ce dernier ne peut pas contenir lui-mme
de sous-sous-menu.
Votre activit sera prvenue dun choix de lutilisateur par la mthode de rappel onOptionsItemSelected(). Vous recevrez alors lobjet MenuItem correspondant ce choix.
Un motif de conception classique consiste utiliser une instruction switch() avec lidentifiant du menu (item.getItemId()), afin dexcuter laction approprie. Notez
quonOptionsItemSelected() est utilise indpendamment du fait que loption choisie
soit un choix du menu de base ou dun sous-menu.

Menus contextuels
Le fonctionnement des menus contextuels est quasiment identique celui des menus
doptions. Les deux diffrences principales concernent leur remplissage et la faon dont
vous serez inform des choix effectus par lutilisateur.
Vous devez dabord indiquer le ou les widgets de votre activit qui disposeront de menus
contextuels. Pour cela, il suffit dappeler la mthode registerForContextMenu() partir
de lactivit, en lui passant en paramtre un objet View le widget qui a besoin dun menu
contextuel.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 132 Dimanche, 8. novembre 2009 12:23 12

132

Lart du dveloppement Android

Puis vous devez implmenter la mthode onCreateContextMenu(), qui, entre autres


choses, reoit lobjet View que vous aviez fourni registerForContextMenu(). Si votre
activit doit construire plusieurs menus contextuels, ceci permet didentifier le menu
concern.
La mthode onCreateContextMenu() reoit en paramtre le ContextMenu lui-mme, la
View laquelle est associ le menu contextuel et un objet ContextMenu.ContextMenuInfo, qui indique llment de la liste qui a t touch et maintenu par lutilisateur (au
cas o vous souhaiteriez personnaliser ce menu contextuel en fonction de cette information).
Il est galement important de remarquer quonCreateContextMenu() est appele
chaque fois que le menu contextuel est sollicit. la diffrence dun menu doptions (qui
nest construit quune seule fois par activit), les menus contextuels sont supprims aprs
utilisation. Par consquent, vous ne pouvez pas compter sur lobjet ContextMenu fourni ;
il faut reconstruire le menu pour quil corresponde aux besoins de votre activit.
Pour tre prvenu de la slection dun choix de menu contextuel, implmentez la mthode
onContextItemSelected() de lactivit. Dans cette mthode de rappel, vous nobtiendrez que linstance du MenuItem qui a t choisi : si lactivit a plusieurs menus contextuels, vous devez donc vous assurer que les identifiants des lments de menus sont
uniques afin de pouvoir les traiter de faon approprie. Vous pouvez galement appeler la
mthode getMenuInfo() du MenuItem afin dobtenir lobjet ContextMenu.ContextMenuInfo que vous aviez reu dans onCreateContextMenu(). Pour le reste, cette mthode
de rappel se comporte comme onOptionsItemSelected(), que nous avons dcrite dans
la section prcdente.

Illustration rapide
Le projet Menus/Menus contient une version modifie de Selection/List (voir Chapitre 9) avec un menu associ. Les menus tant dfinis dans le code Java, le fichier de
description XML nest pas modifi et ne sera donc pas reproduit ici.
Le code Java comprend en revanche quelques nouveauts :
public class MenuDemo extends ListActivity {
TextView selection;
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"};
public static final int EIGHT_ID = Menu.FIRST+1;
public static final int SIXTEEN_ID = Menu.FIRST+2;
public static final int TWENTY_FOUR_ID = Menu.FIRST+3;
public static final int TWO_ID = Menu.FIRST+4;
public static final int THIRTY_TWO_ID = Menu.FIRST+5;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 133 Dimanche, 8. novembre 2009 12:23 12

Chapitre 11

Utilisation des menus

133

public static final int FORTY_ID = Menu.FIRST+6;


public static final int ONE_ID = Menu.FIRST+7;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items));
selection=(TextView)findViewById(R.id.selection);
registerForContextMenu(getListView());
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(items[position]);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
populateMenu(menu);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
populateMenu(menu);
return(super.onCreateOptionsMenu(menu));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return(applyMenuChoice(item) ||
super.onOptionsItemSelected(item));
}
@Override
public boolean onContextItemSelected(MenuItem item) {
return(applyMenuChoice(item) ||
super.onContextItemSelected(item));
}
private void populateMenu(Menu menu) {
menu.add(Menu.NONE, ONE_ID, Menu.NONE, "1 Pixel");
menu.add(Menu.NONE, TWO_ID, Menu.NONE, "2 Pixels");
menu.add(Menu.NONE, EIGHT_ID, Menu.NONE, "8 Pixels");
menu.add(Menu.NONE, SIXTEEN_ID, Menu.NONE, "16 Pixels");
menu.add(Menu.NONE, TWENTY_FOUR_ID, Menu.NONE, "24 Pixels");
menu.add(Menu.NONE, THIRTY_TWO_ID, Menu.NONE, "32 Pixels");
menu.add(Menu.NONE, FORTY_ID, Menu.NONE, "40 Pixels");
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 134 Dimanche, 8. novembre 2009 12:23 12

134

Lart du dveloppement Android

private boolean applyMenuChoice(MenuItem item) {


switch (item.getItemId()) {
case ONE_ID:
getListView().setDividerHeight(1);
return(true);
case EIGHT_ID:
getListView().setDividerHeight(8);
return(true);
case SIXTEEN_ID:
getListView().setDividerHeight(16);
return(true);
case TWENTY_FOUR_ID:
getListView().setDividerHeight(24);
return(true);
case TWO_ID:
getListView().setDividerHeight(2);
return(true);
case THIRTY_TWO_ID:
getListView().setDividerHeight(32);
return(true);
case FORTY_ID:
getListView().setDividerHeight(40);
return(true);
}
return(false);
}
}

Dans onCreate(), nous prcisons que le widget liste dispose dun menu contextuel que
nous remplissons laide de notre mthode prive populateMenu() par lintermdiaire
donCreateContextMenu(). Nous implmentons galement la mthode de rappel onCreateOptionsMenu() pour signaler que notre activit dispose galement dun menu doptions
rempli, lui aussi, grce populateMenu().
Nos implmentations donOptionsItemSelected() (pour les choix dans le menu doptions)
et donContextItemSelected() (pour les choix dans le menu contextuel) dlguent
toutes les deux leur traitement une mthode applyMenuChoice() et appellent leur superclasse respective si lutilisateur na slectionn aucun choix du menu.
populateMenu() ajoute sept choix de menus, chacun ayant un identifiant unique. Comme
nous sommes paresseux, nous nutiliserons pas dicne.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 135 Dimanche, 8. novembre 2009 12:23 12

Chapitre 11

Utilisation des menus

135

Dans applyMenuChoice(), nous testons si lun des choix du menu a t slectionn,


auquel cas nous fixons lpaisseur du sparateur de la liste la valeur correspondante.
Comme le montre la Figure 11.1, lactivit initiale a le mme aspect que ListDemo.
Figure 11.1
Lapplication MenuDemo
lors de son lancement.

Si lon appuie sur le bouton Menu du terminal, on obtient le menu doptions prsent la
Figure 11.2.
Figure 11.2
La mme application,
avec son menu doptions.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 136 Dimanche, 8. novembre 2009 12:23 12

136

Lart du dveloppement Android

La Figure 11.3 montre que les deux choix restants du menu apparaissent lorsque lon
clique sur le bouton "Plus".
Figure 11.3
La mme application,
montrant les derniers
choix du menu.

Si lon choisit une valeur (16 pixels, par exemple), lpaisseur des traits de sparation de la
liste est modifie, comme le montre la Figure 11.4.
Figure 11.4
La mme application
dans une version peu
esthtique.

La Figure 11.5 montre que lon peut faire apparatre le menu contextuel en "touchant et
maintenant" un lment de la liste.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 137 Dimanche, 8. novembre 2009 12:23 12

Chapitre 11

Utilisation des menus

137

Figure 11.5
La mme application,
avec son menu contextuel.

L encore, le choix dune option modifiera lpaisseur du trait de sparation.

Encore de linflation
Nous avons vu au Chapitre 9 que lon pouvait dcrire les vues via des fichiers XML et les
transformer par inflation en objets View au moment de lexcution. Android permet de
faire de mme avec les menus, qui peuvent tre dcrits dans des fichiers XML et transforms en objets lorsquils sont appels. Cette approche permet de sparer la structure
des menus de leur implmentation et facilite le dveloppement des outils de conception de
menus.

Structure XML dun menu


Les fichiers XML de description des menus sont placs dans le rpertoire res/menu/,
ct des autres types de ressources du projet. Comme pour les layouts, vous pouvez
utiliser plusieurs fichiers de menus XML, chacun ayant son propre nom et lextension
.xml.
Voici, par exemple, le contenu du fichier sample.xml, extrait du projet Menus/Inflation :
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/close"
android:title="Fermer"
android:orderInCategory="3"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 138 Dimanche, 8. novembre 2009 12:23 12

138

Lart du dveloppement Android

android:icon="@drawable/eject" />
<item android:id="@+id/no_icon"
android:orderInCategory="2"
android:title="Sans Icne" />
<item android:id="@+id/disabled"
android:orderInCategory="4"
android:enabled="false"
android:title="Inactif" />
<group android:id="@+id/other_stuff"
android:menuCategory="secondary"
android:visible="false">
<item android:id="@+id/later"
android:orderInCategory="0"
android:title="Avant-dernier" />
<item android:id="@+id/last"
android:orderInCategory="1"
android:title="Dernier" />
</group>
<item android:id="@+id/submenu"
android:orderInCategory="3"
android:title="Sous-menu">
<menu>
<item android:id="@+id/non_ghost"
android:title="Visible"
android:visible="true"
android:alphabeticShortcut="v" />
<item android:id="@+id/ghost"
android:title="Fantome"
android:visible="false"
android:alphabeticShortcut="f" />
</menu>
</item>
</menu>

Voici quelques remarques propos des dfinitions des menus en XML :

Llment racine doit sappeler menu.

Les lments fils de menu sont des objets item et group, ces derniers reprsentant une
collection dobjets item pouvant tre manipuls comme un groupe.

Les sous-menus sont dclars en ajoutant un lment menu comme fils dun lment
item et en utilisant ce nouvel lment pour dcrire le contenu du sous-menu.

Pour dtecter quun choix a t slectionn ou faire rfrence un choix ou un


groupe partir du code Java, noubliez pas dutiliser lattribut android:id, exactement
comme pour les fichiers XML des vues.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 139 Dimanche, 8. novembre 2009 12:23 12

Chapitre 11

Utilisation des menus

139

Options des menus et XML


Les lments item et group peuvent contenir plusieurs attributs correspondant aux mthodes
de Menu et MenuItem.

Titre
Le titre dun choix de menu est fourni par lattribut android:title dun lment item.
Ce titre peut tre une chane littrale ou une rfrence vers une ressource chane
(@string/truc, par exemple).

Icne
Les choix de menus peuvent possder des icnes qui sont fournies sous la forme dune
rfrence vers une ressource Drawable (@drawable/eject, par exemple) via lattribut
android:icon dun lment item.

Ordre
Par dfaut, lordre des choix du menu est dtermin par celui de leur apparition dans le
fichier XML. Vous pouvez modifier cet ordre laide de lattribut android:orderInCategory de llment item. Sa valeur est un indice commenant 0, qui prcise son ordre
dapparition dans la catgorie courante. Les lments group peuvent utiliser lattribut
android:menuCategory pour indiquer une catgorie diffrente de celle par dfaut pour
leurs lments item.
Cependant, il est gnralement plus simple de placer les lments dans le bon ordre dans
le fichier XML.

Activation
Les item et les group peuvent tre rendus actifs ou inactifs dans le fichier XML, laide
de leur attribut android:enabled. Par dfaut, ils sont considrs comme actifs. Les objets
item et group inactifs apparaissent dans le menu mais ne peuvent pas tre slectionns.
Vous pouvez modifier ltat dun item en cours dexcution grce la mthode setEnabled()
de MenuItem et celui dun group via la mthode setGroupEnabled() de Menu.

Visibilit
De mme, les objets item et group peuvent tre visibles ou non, selon la valeur de leur attribut
android:visible. Par dfaut, ils sont visibles ; les objets item et group invisibles napparaissent pas dans le menu. Vous pouvez modifier cet tat en cours dexcution en appelant la
mthode setVisible() dun MenuItem ou la mthode setGroupVisible() dun Menu.
Dans le fichier XML prsent plus haut, le groupe other_stuff est invisible. Si nous le
rendons visible dans le code Java, les deux choix du groupe apparatront comme par
magie.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 140 Dimanche, 8. novembre 2009 12:23 12

140

Lart du dveloppement Android

Raccourci
Les item dun menu peuvent avoir des raccourcis en appuyant sur une seule lettre
(android:alphabeticShortcut) ou un chiffre (android:numericShortcut), vous
pouvez slectionner cet item sans utiliser lcran tactile, le pad ou le curseur pour naviguer
dans le menu.

Crer un menu par inflation


Lutilisation dun menu dfini dans un fichier XML est relativement simple puisquil suffit
de crer un MenuInflater et de lui demander de crer lobjet Menu :
@Override
public boolean onCreateOptionsMenu(Menu menu) {
theMenu=menu;
new MenuInflater(getApplication())
.inflate(R.menu.sample, menu);
return(super.onCreateOptionsMenu(menu));
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 141 Dimanche, 8. novembre 2009 12:23 12

12
Polices de caractres
Invitablement, un dveloppeur dapplications doit rpondre la question : "Comment
changer cette police de caractres ?" La rponse dpend des polices fournies avec la plateforme, de la possibilit den ajouter dautres et du moyen de les appliquer au widget ou
la partie de lapplication concerne. Android ny fait pas exception car il est fourni avec
quelques polices et permet den ajouter dautres. Toutefois, comme avec tout nouvel environnement, il faut savoir grer ses spcificits.

Sachez apprcier ce que vous avez


Nativement, Android connat trois polices sous les noms raccourcis "sans", "serif" et
"monospace". Elles forment la srie Droid, cre par Ascender1 pour lOpen Handset
Alliance.
Pour utiliser ces polices, il suffit de les dsigner dans le fichier de description XML.
Le layout suivant, par exemple, est extrait du projet Fonts/FontSampler :
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"

1. http://www.ascendercorp.com/oha.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 142 Dimanche, 8. novembre 2009 12:23 12

142

Lart du dveloppement Android

android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:text="sans :"
android:layout_marginRight="4px"
android:textSize="20sp"
/>
<TextView
android:id="@+id/sans"
android:text="Bonjour !"
android:typeface="sans"
android:textSize="20sp"
/>
</TableRow>
<TableRow>
<TextView
android:text="serif :"
android:layout_marginRight="4px"
android:textSize="20sp"
/>
<TextView
android:id="@+id/serif"
android:text="Bonjour !"
android:typeface="serif"
android:textSize="20sp"
/>
</TableRow>
<TableRow>
<TextView
android:text="monospace :"
android:layout_marginRight="4px"
android:textSize="20sp"
/>
<TextView
android:id="@+id/monospace"
android:text="Bonjour !"
android:typeface="monospace"
android:textSize="20sp"
/>
</TableRow>
<TableRow>
<TextView
android:text="Custom :"
android:layout_marginRight="4px"
android:textSize="20sp"
/>
<TextView
android:id="@+id/custom"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 143 Dimanche, 8. novembre 2009 12:23 12

Chapitre 12

Polices de caractres

143

android:text="Bonjour !"
android:textSize="20sp"
/>
</TableRow>
</TableLayout>

Cette description construit un tableau montrant les noms courts de quatre polices. Vous
remarquerez que les trois premiers utilisent lattribut android:typeface, dont la valeur
est lune des trois polices prdfinies ("sans", par exemple).
Ces trois polices sont parfaites. Cependant, un designer, un chef de projet ou un client peut
trs bien vouloir en utiliser une autre. Vous pouvez galement choisir dutiliser une police
spciale, comme "dingbats", la place dimages PNG.
Le moyen le plus simple dy parvenir consiste fournir la ou les polices concernes avec
votre application. Pour ce faire, il suffit de crer un rpertoire assets/ dans larborescence du projet et dy placer les polices TrueType (TTF). Vous pourriez, par exemple,
crer un rpertoire assets/polices/ et y placer les fichiers TTF.
Il faut ensuite demander aux widgets dutiliser cette police mais, malheureusement, vous
ne pouvez pas le faire dans le fichier de description XML car il ne connat pas celles que
vous avez pu placer dans les assets de lapplication. Cette modification doit donc avoir
lieu dans le code Java :
public class FontSampler extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.custom);
Typeface face=Typeface.createFromAsset(getAssets(),
"polices/HandmadeTypewriter.ttf");
tv.setTypeface(face);
}
}

Ici, on rcupre le TextView pour notre exemple "Custom" et lon cre un objet Typeface
en appelant la mthode statique createFromAsset(), qui prend en paramtre lAssetManager de lapplication (obtenu via getAssets()) et un chemin relatif de la police
prsent dans le rpertoire assets/.
Ensuite, il suffit dindiquer au TextView dutiliser cette police en appelant sa mthode
setTypeface() avec le Typeface que lon vient de crer. Ici, on utilise la police Handmade Typewriter1 (voir Figure 12.1).
1. http://moorstation.org/typoasis/designers/klein07/text01/handmade.htm.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 144 Dimanche, 8. novembre 2009 12:23 12

144

Lart du dveloppement Android

Figure 12.1
Lapplication
FontSampler.

Info

Android semble ne pas apprcier toutes les fontes TrueType. Lorsquil naime pas
une fonte extrieure, Android lui substitue sans mot dire la police Droid Sans
("sans"). Par consquent, si vous essayez dutiliser une police et quelle semble ne
pas fonctionner, cest srement parce quelle est incompatible avec Android, pour
une raison ou pour une autre.
En outre, vous avez intrt mettre en minuscules les noms des fichiers de polices,
afin de respecter les conventions utilises par vos autres ressources.

Le problme des glyphes


Les polices TrueType peuvent tre assez lourdes, notamment lorsquelles fournissent un
sous-ensemble important des caractres Unicode. La police Handmade Typewriter que
nous avons utilise ci-dessus, par exemple, pse plus de 70 Ko et les polices libres DejaVu
peuvent atteindre 500 Ko chacune. Mme compresses, elles augmentent donc significativement la taille de votre application : ne vous laissez pas envahir par les polices supplmentaires sous peine de produire une application qui prendra trop de place sur les
tlphones des utilisateurs.
Inversement, il faut savoir que les polices ne contiennent pas ncessairement tous les
glyphes dont vous avez besoin. Par exemple, la classe TextView dAndroid sait crer une
ellipse dun texte, en le tronquant et en ajoutant un caractre dellipse si le texte ne peut
pas tenir dans lespace disponible. Ce comportement peut tre obtenu via lattribut

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 145 Dimanche, 8. novembre 2009 12:23 12

Chapitre 12

Polices de caractres

145

android:ellipsize, par exemple. Ceci fonctionne trs bien, au moins pour les textes
dune seule ligne.
Lellipse utilise par Android est non pas une simple suite de trois points, mais un vritable
caractre "ellipse", o les trois points sont contenus dans un unique glyphe. Par consquent, les polices que vous utilisez auront besoin de ce glyphe pour utiliser la fonctionnalit fournie par android:ellipsize.
En outre, Android complte les chanes qui saffichent lcran pour que leur longueur (en
nombre de caractres) soit identique celles quelles avaient avant leur mise en ellipse.
Pour que cela puisse fonctionner, Android remplace un seul caractre par lellipse et tous
ceux qui seront supprims par le caractre Unicode ZERO WIDTH NO-BREAK
SPACE (U+FEFF). Ceci signifie que les caractres "supplmentaires" aprs lellipse font
partie de la chane, bien quils napparaissent pas lcran.
Les polices que vous utilisez avec TextView et lattribut android:ellipsize doivent
donc galement disposer de ce caractre Unicode, ce qui nest pas toujours le cas. En son
absence, laffichage de la chane courte lcran prsentera des caractres curieux
(des X la fin de la ligne, par exemple).
Bien entendu, le dploiement international dAndroid signifie galement quune police
doit pouvoir grer nimporte quelle langue choisie par lutilisateur, avec ses caractres
particuliers (le du franais, par exemple).
Par consquent, bien quil soit tout fait possible dutiliser des polices personnalises
avec Android, cela induit de nombreux problmes potentiels et vous devez donc soigneusement peser le pour et le contre.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 146 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 147 Dimanche, 8. novembre 2009 12:23 12

13
Intgrer le navigateur
de WebKit
Les autres kits de dveloppement graphiques permettent dutiliser le HTML pour prsenter les informations, que ce soit via des minimoteurs HTML (wxWidgets en Java/Swing,
par exemple) ou par lintgration dInternet Explorer dans les applications .NET. Android
fait de mme en vous permettant dintgrer un navigateur web dans vos activits afin
dafficher du HTML ou de naviguer sur le Web. Ce navigateur repose sur WebKit, le
moteur de Safari (le navigateur dApple).
Ce navigateur est suffisamment complexe pour disposer de son propre paquetage Java
(android.webkit). La simplicit dutilisation du widget WebView, quant elle, dpend de
vos exigences.

Un navigateur, et en vitesse !
Pour les oprations simples, WebView nest pas trs diffrent des autres widgets dAndroid
on le fait surgir dans un layout, on lui indique dans le code Java lURL vers laquelle il
doit naviguer et cest fini.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 148 Dimanche, 8. novembre 2009 12:23 12

148

Lart du dveloppement Android

Le layout du projet WebKit/Browser, par exemple, ne contient quun WebView :


<?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"
>
<WebView android:id="@+id/webkit"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>

Comme avec tous les autres widgets, vous devez lui indiquer comment remplir lespace
(ici, il occupe tout lespace restant).
Le code Java est tout aussi simple :
package com.commonsware.android.webkit;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
public class BrowserDemo1 extends Activity {
WebView browser;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
browser=(WebView)findViewById(R.id.webkit);
browser.loadUrl("http://commonsware.com");
}
}

La seule partie nouvelle de cette version donCreate() est lappel de la mthode


loadUrl() du widget WebView afin de charger une page web.
Nous devons galement modifier le fichier AndroidManifest.xml, afin de demander la
permission daccder Internet ; sinon le navigateur refusera de charger les pages :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.webkit">
<uses-permission android:name="android.permission.INTERNET" />
<application>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 149 Dimanche, 8. novembre 2009 12:23 12

Chapitre 13

Intgrer le navigateur de WebKit

149

<activity android:name=".BrowserDemo1" android:label="BrowserDemo1">


<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Comme le montre la Figure 13.1, lactivit obtenue ressemble un navigateur web sans
barre de dfilement.
Figure 13.1
Lapplication Browser1.

Comme avec le navigateur classique dAndroid, vous pouvez faire dfiler la page en la
tirant avec le doigt, et le pad directionnel permet de vous positionner successivement sur
tous ses lments actifs.
Il manque toutefois certaines parties pour en faire un vritable navigateur : une barre de
navigation, notamment.
Vous pourriez tre tent de remplacer lURL de ce code source par une autre, comme celle
de la page daccueil de Google ou toute autre page utilisant JavaScript. Cependant, par
dfaut, un widget WebView dsactive JavaScript : pour lactiver, vous devez appeler sa
mthode getSettings().setJavaScriptEnabled(true);. Nous reviendrons sur ce point
un peu plus loin dans ce chapitre.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 150 Dimanche, 8. novembre 2009 12:23 12

150

Lart du dveloppement Android

Chargement immdiat
Il existe deux moyens de faire entrer du contenu dans un widget WebView. Le premier,
comme nous lavons vu, consiste indiquer une URL au navigateur et lui faire afficher
cette page via loadUrl(). Le navigateur accdera Internet par les moyens mis disposition du terminal cet instant prcis (Wifi, rseau cellulaire, partage de donnes par Bluetooth,
pigeons voyageurs bien entrans, etc.). Lautre solution consiste utiliser loadData() en
lui fournissant le code HTML que lon veut afficher dans le navigateur. Cette dernire
mthode est notamment utile pour :

afficher un manuel install sous forme de fichier avec lapplication ;

afficher des extraits HTML rcuprs par un autre traitement la description dune
entre dun flux Atom, par exemple ;

produire une interface utilisateur entirement en HTML au lieu dutiliser les widgets
Android.

Il existe deux variantes de loadData(). La plus simple permet de fournir sous forme de
chanes un contenu, son type MIME et lencodage utilis. Pour un document HTML classique,
le type MIME sera gnralement text/html et lencodage, UTF-8.
Si vous remplacez lappel de loadUrl() par le code suivant dans lexemple prcdent,
vous obtiendrez le rsultat prsent la Figure 13.2.
browser.loadData("<html><body>Bonjour !</body></html>",
"text/html", "UTF-8");

Figure 13.2
Lapplication Browser2.

Le code complet de cette application est disponible dans le projet WebKit/Browser2.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 151 Dimanche, 8. novembre 2009 12:23 12

Chapitre 13

Intgrer le navigateur de WebKit

151

Navigation au long cours


Comme on la dj mentionn, le widget WebView ne comprend pas de barre de navigation, ce qui permet de lutiliser des endroits o cette barre serait inutile et consommerait
inutilement de lespace lcran. Ceci tant dit, il est tout fait possible de lui ajouter des
fonctionnalits de navigation, condition de fournir linterface graphique ncessaire.
WebView dispose notamment des mthodes suivantes :

reload() permet de recharger la page courante.

goBack() permet de revenir un pas en arrire dans lhistorique du navigateur, tandis


que canGoBack() teste sil existe un historique pass.

goForward() permet davancer dun pas dans lhistorique du navigateur, tandis que
canGoForward() teste sil existe un historique futur.

goBackOrForward() permet de reculer ou davancer dans lhistorique en fonction de


son paramtre. Une valeur ngative reprsente le nombre de pas vers larrire, une
valeur positive, le nombre de pas vers lavant.

canGoBackOrForward() teste si le navigateur peut reculer ou avancer du nombre de


pas indiqu dans lhistorique (en suivant la mme convention positif/ngatif que pour
goBackOrForward()).

clearCache() vide le cache du navigateur et clearHistory() nettoie son historique.

Amuser le client
Si vous utilisez un widget WebView pour crer une interface utilisateur locale (et non pour
naviguer sur le Web), vous aurez besoin de le contrler certains moments, notamment
lorsque les utilisateurs cliqueront sur des liens. Vous devrez vous assurer que ces liens sont
correctement grs, soit en rechargeant votre propre contenu dans le WebView (en soumettant un Intent Android pour quil ouvre lURL dans un vrai navigateur), soit par
dautres moyens (voir Chapitre 25).
La mthode setWebViewClient(), qui prend en paramtre une instance dune implmentation de WebViewClient, permet de placer un hook dans lactivit WebView. Lobjet
fourni sera alors prvenu dun grand nombre dactivits, allant de la rcupration dune
partie dune page (onPageStarted(), etc.) la ncessit pour lapplication de prendre en
compte un vnement utilisateur ou circonstanciel (onTooManyRedirects(), onReceivedHttpAuthRequest(), etc.).
Un hook classique est shouldOverrideUrlLoading(), qui prend en paramtre une URL
et lobjet WebView lui-mme et qui doit renvoyer true si vous traitez la requte ou false
si vous prfrez utiliser le traitement par dfaut (qui consiste rcuprer la page web dsigne par lURL). Pour une application de lecture de flux RSS, par exemple, le lecteur ne

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 152 Dimanche, 8. novembre 2009 12:23 12

152

Lart du dveloppement Android

comprendra srement pas un navigateur web complet, avec toutes les options de navigation possibles ; si lutilisateur clique sur une URL, vous utiliserez donc probablement un
Intent pour demander Android de charger cette page dans un vrai navigateur. Cependant, si vous avez insr une URL "bidon" dans le code HTML, reprsentant un lien vers
un contenu fourni par une activit, vous pouvez modifier vous-mme le WebView.
Modifions notre premier exemple pour quil devienne lquivalent de la premire application de ce livre : un programme qui affiche la date et lheure courantes lorsque lon clique
sur un bouton. Le code Java de ce projet WebKit/Browser3 est le suivant :
public class BrowserDemo3 extends Activity {
WebView browser;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
browser=(WebView)findViewById(R.id.webkit);
browser.setWebViewClient(new Callback());
loadTime();
}
void loadTime() {
String page="<html><body><a href=\"clock\">"
+new Date().toString()
+"</a></body></html>";
browser.loadDataWithBaseURL("x-data://base", page,
"text/html", "UTF-8",
null);
}
private class Callback extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
loadTime();
return(true);
}
}
}

On charge une simple page web dans le navigateur (via la mthode loadTime()). Cette
page contient la date et lheure courantes sous la forme dun lien vers lURL /clock. On
attache galement une instance dune sous-classe de WebViewClient en fournissant notre
implmentation de shouldOverrideUrlLoading(). Ici, peu importe lURL : nous voulons
simplement recharger le WebView avec loadTime().
Le rsultat de lexcution de cette activit est prsent la Figure 13.3.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 153 Dimanche, 8. novembre 2009 12:23 12

Chapitre 13

Intgrer le navigateur de WebKit

153

Figure 13.3
Lapplication Browser3.

En slectionnant le lien et en cliquant sur le bouton central du pad, ce lien sera considr
comme "cliqu", ce qui aura pour effet de reconstruire la page avec une nouvelle date/
heure.

Rglages, prfrences et options


Votre navigateur web favori contient un menu "Rglages", "Prfrences" ou "Options".
Ajout aux contrles de la barre doutils, ce menu vous permet dadapter de nombreux
aspects du comportement du navigateur, allant des polices quil utilise la gestion de
JavaScript.
Vous pouvez modifier les rglages dun widget WebView en fonction de vos besoins, via
linstance de WebSettings qui est renvoye par la mthode getSettings() du widget.
Beaucoup doptions de WebSettings sont ainsi votre disposition. Bien que la plupart
semblent assez sotriques (setFantasyFontFamily(), par exemple), quelques-unes
peuvent vous tre plus utiles :

Les mthodes setDefaultFontSize() (pour une taille en points) ou setTextSize()


(qui utilise des constantes comme LARGER et SMALLEST pour exprimer une taille relative) permettent de modifier la taille de la police.

setJavaScriptEnabled() et setJavaScriptCanOpenWindowsAutomatically()
permettent, respectivement, de dsactiver totalement JavaScript et de lempcher
douvrir des fentres popup.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 154 Dimanche, 8. novembre 2009 12:23 12

154

Lart du dveloppement Android

setUserAgent() permet de contrler le rendu dun site web. Si son paramtre vaut 0,
le WebView passera au site une chane user-agent indiquant quil sagit dun navigateur mobile alors que, sil vaut 1, le widget enverra une chane indiquant quil sagit
dun navigateur qui sexcute sur une machine de bureau.

Les modifications que vous apportez ne sont pas persistantes : vous devez donc les stocker
quelque part (via le moteur de prfrences dAndroid, par exemple) si vous autorisez vos
utilisateurs modifier ces rglages au lieu de les coder en dur dans votre application.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 155 Dimanche, 8. novembre 2009 12:23 12

14
Affichage de messages
surgissant
Une activit (ou toute autre partie dune application Android) a parfois besoin de sexprimer.
Toutes les interactions avec les utilisateurs ne seront pas soignes, bien propres et contenues dans des activits composes de vues. Les erreurs surgissent ; les tches en arrireplan peuvent mettre plus de temps que prvu pour se terminer ; parfois, les vnements,
comme les messages entrants, sont asynchrones. Dans ce type de situation, vous pouvez
avoir besoin de communiquer avec lutilisateur en vous affranchissant des limites de
linterface classique.
Ce nest videmment pas nouveau : les messages derreur prsents dans des botes de
dialogue existent depuis trs longtemps. Cependant, il existe galement des indicateurs
plus subtils, allant des icnes apparaissant dans une barre des tches (Windows) des
icnes animes dans un "dock" (Mac OS X), en passant par un tlphone qui vibre.
Android dispose de plusieurs moyens dalerter les utilisateurs par dautres systmes que
ceux des activits classiques. Les notifications, par exemple, sont intimement lies aux
Intents et aux services et seront donc prsentes au Chapitre 32. Nous tudierons ici deux
mthodes permettant de faire surgir des messages : les toasts et les alertes.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 156 Dimanche, 8. novembre 2009 12:23 12

156

Lart du dveloppement Android

Les toasts
Un toast est un message transitoire, ce qui signifie quil saffiche et disparat de lui-mme,
sans intervention de lutilisateur. En outre, il ne modifie pas le focus de lactivit
courante : si lutilisateur est en train dcrire le prochain trait fondamental sur lart de la
programmation, ses frappes au clavier ne seront donc pas captures par le message.
Un toast tant transitoire, vous navez aucun moyen de savoir si lutilisateur la remarqu.
Vous ne recevrez aucun accus de rception de sa part et le message ne restera pas affich
suffisamment longtemps pour ennuyer lutilisateur. Un Toast est donc essentiellement
conu pour diffuser des messages davertissement pour annoncer quune longue tche en
arrire-plan sest termine, que la batterie est presque vide, etc.
La cration dun toast est assez simple. La classe Toast fournit une mthode statique
makeText() qui prend un objet String (ou un identifiant dune ressource textuelle) en
paramtre et qui renvoie une instance de Toast. Les autres paramtres de cette mthode
sont lactivit (ou tout autre contexte) et une dure valant LENGTH_SHORT ou LENGTH_LONG
pour exprimer la dure relative pendant laquelle le message restera visible.
Si vous prfrez crer votre Toast partir dune View au lieu dune simple chane
ennuyeuse, il suffit de crer linstance via le constructeur (qui attend un Context) puis
dappeler setView() pour lui indiquer la vue utiliser et setDuration() pour fixer sa
dure.
Lorsque le toast est configur, il suffit dappeler sa mthode show() pour quil saffiche.

Les alertes
Si vous prfrez utiliser le style plus classique des botes de dialogue, choisissez plutt
AlertDialog. Comme toute bote de dialogue modale, un AlertDialog souvre, prend le
focus et reste affich tant que lutilisateur ne le ferme pas. Ce type daffichage convient
donc bien aux erreurs critiques, aux messages de validation qui ne peuvent pas tre affichs correctement dans linterface de base de lactivit ou toute autre information dont
vous voulez vous assurer la lecture immdiate par lutilisateur.
Pour crer un AlertDialog, le moyen le plus simple consiste utiliser la classe Builder,
qui offre un ensemble de mthodes permettant de configurer un AlertDialog. Chacune de
ces mthodes renvoie le Builder afin de faciliter le chanage des appels. la fin, il suffit
dappeler la mthode show() de lobjet Builder pour afficher la bote de dialogue.
Voici les mthodes de configuration de Builder les plus utilises :

setMessage() permet de dfinir le "corps" de la bote de dialogue partir dun simple


message de texte. Son paramtre est un objet String ou un identifiant dune ressource
textuelle.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 157 Dimanche, 8. novembre 2009 12:23 12

Chapitre 14

Affichage de messages surgissant

157

setTitle() et setIcon() permettent de configurer le texte et/ou licne qui apparatra


dans la barre de titre de la bote de dialogue.

setPositiveButton(), setNeutralButton() et setNegativeButton() permettent


dindiquer les boutons qui apparatront en bas de la bote de dialogue, leur emplacement latral (respectivement, gauche, au centre ou droite), leur texte et le code qui
sera appel lorsquon clique sur un bouton (en plus de refermer la bote de dialogue).

Si vous devez faire dautres configurations que celles proposes par Builder, appelez la
mthode create() la place de show() : vous obtiendrez ainsi une instance dAlertDialog
partiellement construite que vous pourrez configurer avant dappeler lune des mthodes
show() de lobjet AlertDialog lui-mme.
Aprs lappel de show(), la bote de dialogue saffiche et attend une saisie de lutilisateur.

Mise en uvre
Voici le fichier de description XML du projet Messages/Message :
<?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" >
<Button
android:id="@+id/alert"
android:text="Declencher une alerte"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/toast"
android:text="Lever un toast"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

Et voici son code Java :


public class MessageDemo extends Activity implements View.OnClickListener {
Button alert;
Button toast;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 158 Dimanche, 8. novembre 2009 12:23 12

158

Lart du dveloppement Android

setContentView(R.layout.main);
alert=(Button)findViewById(R.id.alert);
alert.setOnClickListener(this);
toast=(Button)findViewById(R.id.toast);
toast.setOnClickListener(this);
}
public void onClick(View view) {
if (view==alert) {
new AlertDialog.Builder(this)
.setTitle("MessageDemo")
.setMessage("Oups !")
.setNeutralButton("Fermeture",
new DialogInterface. OnClickListener() {
public void onClick(DialogInterface dlg, int sumthin) {
// On ne fait rien la bote se fermera ellemme
}
})
.show();
}
else {
Toast
.makeText(this, "<Clac, Clac>", Toast.LENGTH_SHORT)
.show();
}
}
}

Ce layout est tout ce quil y a de plus classique il comprend simplement deux boutons
pour dclencher lalerte et le toast.
Lorsque nous cliquons sur le bouton "Dclencher une alerte", nous crons un Builder
(avec new Builder(this)) pour configurer le titre (setTitle("MessageDemo")), le
message (setMessage("Oups!!") et le bouton central (setNeutralButton("Fermeture", new OnClickListener() ) avant dafficher la bote de dialogue. Quand on
clique sur ce bouton, la mthode de rappel OnClickListener() ne fait rien : le seul fait
quon ait appuy sur le bouton provoque la fermeture de la bote de dialogue. Toutefois,
vous pourriez mettre jour des informations de votre activit en fonction de laction de
lutilisateur, notamment sil peut choisir entre plusieurs boutons. Le rsultat est une bote
de dialogue classique, comme celle de la Figure 14.1.
Lorsque lon clique sur le bouton "Lever un toast", la classe Toast cre un toast textuel
(avec makeText(this, "<Clac, Clac>", LENGTH_SHORT)), puis laffiche avec show().
Le rsultat est un message de courte dure qui ninterrompt pas lactivit (voir Figure 14.2).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 159 Dimanche, 8. novembre 2009 12:23 12

Chapitre 14

Affichage de messages surgissant

159

Figure 14.1
Lapplication MessageDemo aprs avoir cliqu
sur le bouton "Dclencher
une alerte".

Figure 14.2
La mme application,
aprs avoir cliqu sur le
bouton "Lever un toast".

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 160 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 161 Dimanche, 8. novembre 2009 12:23 12

15
Utilisation des threads
Tout le monde souhaite que ses activits soient ractives. Rpondre rapidement un
utilisateur (en moins de 200 millisecondes) est un bon objectif. Au minimum, il faut
fournir une rponse en moins de 5 secondes ; sinon lActivityManager pourrait dcider de jouer le rle de la faucheuse et tuer votre activit car il considre quelle ne
rpond plus.
Mais, videmment, votre programme peut devoir accomplir une tche qui seffectue en un
temps non ngligeable. Il y a deux moyens de traiter ce problme :

raliser les oprations coteuses dans un service en arrire-plan en se servant de notifications pour demander aux utilisateurs de revenir votre activit ;

effectuer le travail coteux dans un thread en arrire-plan.

Android dispose dune vritable panoplie de moyens pour mettre en place des threads en
arrire-plan tout en leur permettant dinteragir proprement avec linterface graphique, qui,
elle, sexcute dans un thread qui lui est ddi. Parmi eux, citons les objets Handler et le
postage dobjets Runnable destination de la vue.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 162 Dimanche, 8. novembre 2009 12:23 12

162

Lart du dveloppement Android

Les handlers
Le moyen le plus souple de crer un thread en arrire-plan avec Android consiste crer
une instance dune sous-classe de Handler. Vous navez besoin que dun seul objet
Handler par activit et il nest pas ncessaire de lenregistrer manuellement ou quoi que
ce soit dautre la cration de linstance suffit lenregistrer auprs du sous-systme de
gestion des threads.
Le thread en arrire-plan peut communiquer avec le Handler, qui effectuera tout son
travail dans le thread de linterface utilisateur de votre activit. Cest important car les
changements de cette interface la modification de ses widgets, par exemple ne doivent
intervenir que dans le thread de linterface de lactivit.
Vous avez deux possibilits pour communiquer avec le Handler : les messages et les
objets Runnable.

Les messages
Pour envoyer un Message un Handler, appelez dabord la mthode obtainMessage()
afin dextraire lobjet Message du pool. Cette mthode est surcharge pour vous permettre
de crer un Message vide ou des messages contenant des identifiants de messages et des
paramtres. Plus le traitement que doit effectuer le Handler est compliqu, plus il y a de
chances que vous deviez placer des donnes dans le Message pour aider le Handler
distinguer les diffrents vnements.
Puis envoyez le Message au Handler en passant par sa file dattente des messages, laide
de lune des mthodes de la famille sendMessage...() :

sendMessage() place immdiatement le message dans la file.

sendMessageAtFrontOfQueue() place immdiatement le message en tte de file (au


lieu de le mettre la fin, ce qui est le comportement par dfaut). Votre message aura
donc priorit par rapport tous les autres.

sendMessageAtTime() place le message dans la file linstant indiqu, qui sexprime


en millisecondes par rapport luptime du systme (SystemClock.uptimeMillis()).

sendMessageDelayed() place le message dans la file aprs un certain dlai, exprim


en millisecondes.

Pour traiter ces messages, votre Handler doit implmenter la mthode handleMessage(), qui sera appele pour chaque message qui apparat dans la file dattente. Cest l
que le handler peut modifier linterface utilisateur sil le souhaite. Cependant, il doit le
faire rapidement car les autres oprations de linterface sont suspendues tant quil na pas
termin.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 163 Dimanche, 8. novembre 2009 12:23 12

Chapitre 15

Utilisation des threads

163

Le projet Threads/Handler, par exemple, cre une ProgressBar et la modifie via un


Handler. Voici son fichier de description XML :
<?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"
>
<ProgressBar android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>

La ProgressBar, qui a une largeur et une hauteur normales, utilise galement la proprit
style, qui ne sera pas dcrite dans ce livre. Pour linstant, il suffit de considrer quelle
indique que la barre de progression devra tre trace horizontalement, en montrant la
proportion de travail effectu.
Voici le code Java :
package com.commonsware.android.threads;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
public class HandlerDemo extends Activity {
ProgressBar bar;
Handler handler=new Handler() {
@Override
public void handleMessage(Message msg) {
bar.incrementProgressBy(5);
}
};
boolean isRunning=false;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
bar=(ProgressBar)findViewById(R.id.progress);
}
public void onStart() {
super.onStart();
bar.setProgress(0);
Thread background=new Thread(new Runnable() {

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 164 Dimanche, 8. novembre 2009 12:23 12

164

Lart du dveloppement Android

public void run() {


try {
for (int i=0;i<20 && isRunning;i++) {
Thread.sleep(1000);
handler.sendMessage(handler.obtainMessage());
}
}
catch (Throwable t) {
// Termine le thread en arrireplan
}
}
});
isRunning=true;
background.start();
}
public void onStop() {
super.onStop();
isRunning=false;
}
}

Une partie de la construction de lactivit passe par la cration dune instance de Handler
contenant notre implmentation de handleMessage(). Pour chaque message reu, nous
nous contentons de faire avancer la barre de progression de 5 points.
Dans la mthode onStart(), nous configurons un thread en arrire-plan. Dans une vraie
application, celui-ci effectuerait une tche significative mais, ici, nous nous mettons
simplement en pause pendant 1 seconde, nous postons un Message au Handler et nous
rptons ces deux oprations vingt fois. Combin avec la progression de 5 points de la
position de la ProgressBar, ce traitement fera donc avancer la barre travers tout lcran
puisque la valeur maximale par dfaut de la barre est 100. Vous pouvez ajuster ce maximum avec la mthode setMax() pour, par exemple, quil soit gal au nombre de lignes de
la base de donnes que vous tes en train de traiter et avancer de un la position pour
chaque ligne.
Notez que lon doit ensuite quitter onStart(). Cest un point crucial : la mthode
onStart() est appele dans le thread qui gre linterface utilisateur de lactivit afin
quelle puisse modifier les widgets et tout ce qui affecte cette interface la barre de titre,
par exemple. Ceci signifie donc que lon doit sortir donStart(), la fois pour laisser le
Handler faire son travail et pour quAndroid ne pense pas que notre activit est bloque.
La Figure 15.1 montre que lactivit se contente dafficher une barre de progression horizontale.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 165 Dimanche, 8. novembre 2009 12:23 12

Chapitre 15

Utilisation des threads

165

Figure 15.1
Lapplication
HandlerDemo.

Les runnables
Si vous prfrez ne pas vous ennuyer avec les objets Message, vous pouvez galement
passer des objets Runnable au Handler, qui les excutera dans le thread de linterface
utilisateur. Handler fournit un ensemble de mthodes post...() pour faire passer les
objets Runnable afin quils soient traits.

Excution sur place


Les mthodes post() et postDelayed() de Handler, qui permettent dajouter des objets
Runnable dans la file dattente des vnements, peuvent galement tre utilises avec les
vues. Ceci permet de simplifier lgrement le code car vous pouvez alors vous passer dun
objet Handler. Toutefois, vous perdrez galement un peu de souplesse. En outre, la classe
Handler existe depuis longtemps dans Android et a probablement t mieux teste que
cette technique.

O est pass le thread de mon interface utilisateur ?


Parfois, vous pouvez ne pas savoir si vous tes en train dexcuter le thread de linterface
utilisateur de votre application. Si vous fournissez une partie de votre code sous la forme
dune archive JAR, par exemple, vous pouvez ne pas savoir si ce code est excut dans le
thread de linterface ou dans un thread en arrire-plan.
Pour rsoudre ce problme, la classe Activity fournit la mthode runOnUiThread(). Elle
fonctionne comme les mthodes post() de Handler et View car elle met dans une file un

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 166 Dimanche, 8. novembre 2009 12:23 12

166

Lart du dveloppement Android

Runnable pour quil sexcute dans le thread de linterface si vous ny tes pas dj. Dans
le cas contraire, elle appelle immdiatement le Runnable. Vous disposez ainsi du meilleur
des deux mondes : aucun dlai si vous tes dans le thread de linterface et aucun problme
si vous ny tes pas.

Dsynchronisation
Avec AsyncTask, Android 1.5 a introduit une nouvelle faon de concevoir les oprations
en arrire-plan. Grce cette seule classe bien conue, Android soccupera du dcoupage
du travail entre le thread de linterface et un thread en arrire-plan. En outre, il allouera et
dsallouera lui-mme le thread en arrire-plan et grera une petite file de tches, ce qui
accentue encore le ct "prt lemploi" dAsyncTask.

La thorie
Il existe un dicton bien connu des vendeurs : "Lorsquun client achte un foret de 12 mm
dans un magasin, il ne veut pas un foret de 13 mm : il veut percer des trous de 13 mm."
Les magasins ne peuvent pas vendre les trous, ils vendent donc les outils (des perceuses et
des forets) qui facilitent la cration des trous.
De mme, les dveloppeurs Android qui se battent avec la gestion des threads en arrireplan ne veulent pas vraiment des threads en arrire-plan : ils souhaitent quun certain
travail seffectue en dehors du thread de linterface, afin que les utilisateurs ne soient pas
bloqus et que les activits ne reoivent pas la redoutable erreur "application not responding" (ANR). Bien quAndroid ne puisse pas, par magie, faire en sorte quun traitement ne
consomme pas le temps allou au thread de linterface, il peut offrir des techniques
permettant de faciliter et de rendre plus transparentes ces oprations en arrire-plan.
AsyncTask en est un exemple.
Pour utiliser AsyncTask, il faut :

crer une sous-classe dAsyncTask, gnralement sous la forme dune classe interne
celle qui utilise la tche (une activit, par exemple) ;

redfinir une ou plusieurs mthodes dAsyncTask pour raliser le travail en arrireplan ainsi que toute opration associe la tche et qui doit seffectuer dans le thread
de linterface (la mise jour de la progression, par exemple) ;

le moment venu, crer une instance de la sous-classe dAsyncTask et appeler


execute() pour quelle commence son travail.

Vous navez pas besoin :

de crer votre propre thread en arrire-plan ;

de terminer ce thread au moment voulu ;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 167 Dimanche, 8. novembre 2009 12:23 12

Chapitre 15

Utilisation des threads

167

dappeler des mthodes pour que des traitements seffectuent dans le thread de linterface.

AsyncTask, gnricit et paramtres variables


La cration dune sous-classe dAsyncTask nest pas aussi simple que, par exemple,
limplmentation de linterface Runnable car AsyncTask est une classe gnrique ; vous
devez donc lui indiquer trois types de donnes :

Le type de linformation qui est ncessaire pour le traitement de la tche (les URL
tlcharger, par exemple).

Le type de linformation qui est passe la tche pour indiquer sa progression.

Le type de linformation qui est passe au code aprs la tche lorsque celle-ci sest
termine.

En outre, les deux premiers types sont, en ralit, utiliss avec des paramtres variables, ce
qui signifie que votre sous-classe dAsyncTask les utilise via des tableaux.
Tout cela devrait devenir plus clair ltude dun exemple.

Les tapes dAsyncTask


Pour parvenir vos fins, vous pouvez redfinir quatre mthodes dAsyncTask.
La seule que vous devez redfinir pour que votre classe soit utilisable sappelle doInBackground(). Elle sera appele par AsyncTask dans un thread en arrire-plan et peut
sexcuter aussi longtemps quil le faut pour accomplir lopration ncessaire cette tche
spcifique. Cependant, noubliez pas que les tches doivent avoir une fin il est dconseill
dutiliser AsyncTask pour raliser une boucle infinie.
La mthode doInBackground() recevra en paramtre un tableau variable contenant des
lments du premier des trois types mentionns ci-dessus les type des donnes ncessaires au traitement de la tche. Si la mission de cette tche consiste, par exemple, tlcharger un ensemble dURL, doInBackground() recevra donc un tableau contenant toutes
ces URL.
Cette mthode doit renvoyer une valeur du troisime type de donnes mentionn le
rsultat de lopration en arrire-plan.
Vous pouvez galement redfinir onPreExecute(), qui est appele partir du thread de
linterface utilisateur avant que le thread en arrire-plan nexcute doInBackground().
Dans cette mthode, vous pourriez par exemple initialiser une ProgressBar ou tout autre
indicateur du dbut du traitement.
De mme, vous pouvez redfinir onPostExecute(), qui est appele partir du thread de
linterface graphique lorsque doInBackground() sest termine. Cette mthode reoit en
paramtre la valeur renvoye par doInBackground() (un indicateur de succs ou dchec,

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 168 Dimanche, 8. novembre 2009 12:23 12

168

Lart du dveloppement Android

par exemple). Vous pouvez donc utiliser cette mthode pour supprimer la ProgressBar et
utiliser le travail effectu en arrire-plan pour mettre jour le contenu dune liste.
onProgressUpdate() est la quatrime mthode que vous pouvez redfinir. Si doInBackground() appelle la mthode publishProgress() de la tche, lobjet ou les objets
passs cette mthode seront transmis onProgressUpdate(), mais dans le thread de
linterface. onProgressUpdate() peut donc alerter lutilisateur que la tche en arrireplan a progress en mettant jour une ProgressBar ou en continuant une animation,
par exemple. Cette mthode reoit un tableau variable dlments du second type
mentionn plus haut, contenant les donnes publies par doInBackground() via publishProgress().

Exemple de tche
Comme on la indiqu, limplmentation dune classe AsyncTask nest pas aussi simple
que celle dune classe Runnable. Cependant, une fois passe la difficult de la gnricit
et des paramtres variables, ce nest pas si compliqu que cela.
Le projet Threads/Asyncer implmente une ListActivity qui utilise une AsyncTask :
package com.commonsware.android.async;
import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import java.util.ArrayList;
public class AsyncDemo extends ListActivity {
private 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"};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 169 Dimanche, 8. novembre 2009 12:23 12

Chapitre 15

Utilisation des threads

169

setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
new ArrayList()));
new AddStringTask().execute();
}
class AddStringTask extends AsyncTask<Void, String, Void> {
@Override
protected Void doInBackground(Void... inutilise) {
for (String item : items) {
publishProgress(item);
SystemClock.sleep(200);
}
return(null);
}
@Override
protected void onProgressUpdate(String... item) {
((ArrayAdapter)getListAdapter()).add(item[0]);
}
@Override
protected void onPostExecute(Void inutilise) {
Toast
.makeText(AsyncDemo.this, "Fini !", Toast.LENGTH_SHORT)
.show();
}
}
}

Il sagit dune autre variante de la liste de mots lorem ipsum qui a dj t souvent utilise
dans ce livre. Cette fois-ci, au lieu de simplement fournir la liste un ArrayAdapter, on
simule la cration de ces mots dans un thread en arrire-plan laide de la classe
AddStringTask, notre implmentation dAsyncTask.
Les sections qui suivent passent en revue les diffrentes parties de ce code.

Dclaration dAddStringTask
class AddStringTask extends AsyncTask<Void, String, Void> {

On utilise ici la gnricit pour configurer les types de donnes spcifiques dont nous
aurons besoin dans AddStringTask :

Nous navons besoin daucune information de configuration ; par consquent, le


premier type est Void.

Nous voulons passer onProgressUpdate() chaque chane "produite" par notre tche
en arrire-plan, afin de pouvoir lajouter la liste. Le second type est donc String.

Nous ne renvoyons pas de rsultat proprement parler (hormis les mises jour) ; par
consquent, le troisime type est Void.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 170 Dimanche, 8. novembre 2009 12:23 12

170

Lart du dveloppement Android

La mthode doInBackground()
@Override
protected Void doInBackground(Void... inutilise) {
for (String item : items) {
publishProgress(item);
SystemClock.sleep(200);
}
return(null);
}

La mthode doInBackground() tant appele dans un thread en arrire-plan, elle peut


durer aussi longtemps quon le souhaite. Dans une vraie application, nous pourrions, par
exemple, parcourir une liste dURL et toutes les tlcharger. Ici, nous parcourons notre
liste statique de mots latins en appelant publishProgress() pour chacun deux, puis nous
nous mettons en sommeil pendant le cinquime de seconde pour simuler un traitement.
Comme nous avons choisi de nutiliser aucune information de configuration, nous navons
normalement pas besoin de paramtre pour doInBackground(). Cependant, le contrat
dimplmentation dAsyncTask prcise quil faut prendre un tableau variable dlments
du premier type gnrique : cest la raison pour laquelle le paramtre de cette mthode est
Void... inutilise.
En outre, ayant choisi de ne rien renvoyer alors que le contrat stipule que nous devons
renvoyer un objet du troisime type gnrique, le rsultat de cette mthode est de type
Void et nous renvoyons null.

La mthode onProgressUpdate()
@Override
protected void onProgressUpdate(String... item) {
((ArrayAdapter)getListAdapter()).add(item[0]);
}

La mthode onProgressUpdate() est appele dans le thread de linterface et nous voulons


quelle signale lutilisateur que lon est en train de charger les chanes. Ici, nous ajoutons
simplement la chane lArrayAdapter, afin quelle soit ajoute la fin de la liste.
Cette mthode attend un tableau variable dlments du deuxime type gnrique
(String... ici). Comme nous ne lui passons quune seule chane par appel publishProgress(), on ne doit examiner que le premier lment du tableau item.

La mthode onPostExecute()
@Override
protected void onPostExecute(Void inutilise) {
Toast

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 171 Dimanche, 8. novembre 2009 12:23 12

Chapitre 15

Utilisation des threads

171

.makeText(AsyncDemo.this, "Fini !", Toast.LENGTH_SHORT)


.show();
}

La mthode onPostExecute() est appele dans le thread de linterface et nous voulons


quelle signale que lopration qui sexcutait en arrire-plan sest termine. Dans une
vraie application, cela pourrait consister supprimer une ProgressBar ou stopper
une animation, par exemple. Ici, nous nous contentons de "lever un toast".
Son paramtre est Void inutilise pour respecter le contrat dimplmentation.

Lactivit
new AddStringTask().execute();

Pour utiliser AddStringTask, nous crons simplement une instance de cette classe et
appelons sa mthode execute(). Ceci a pour effet de lancer la chane des vnements qui
font raliser le travail par le thread en arrire-plan.
Si AddStringTask avait demand des paramtres de configuration, nous naurions pas
utilis Void comme premier type gnrique et le constructeur aurait attendu zro ou plusieurs
paramtres de ce type. Ces valeurs auraient ensuite t passes doInBackground().

Le rsultat
Comme le montre la Figure 15.2, cette activit affiche la liste qui se remplit "en temps
rel" pendant quelques secondes, puis affiche un toast pour indiquer que le traitement est
termin.
Figure 15.2
Lapplication AsyncDemo,
au milieu du chargement
de la liste des mots.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 172 Dimanche, 8. novembre 2009 12:23 12

172

Lart du dveloppement Android

viter les piges


Les threads en arrire-plan ne sont pas de mignons bbs bien sages, mme en passant par
le systme des Handler dAndroid. Non seulement ils ajoutent de la complexit, mais ils
ont un cot rel en termes de mmoire, de CPU et de batterie.
Cest pour cette raison que vous devez tenir compte dun grand nombre de scnarios lorsque
vous les utilisez. Parmi eux :

Les utilisateurs peuvent interagir avec linterface graphique de votre activit pendant
que le thread en arrire-plan sessouffle. Si son travail est altr ou modifi par une
action de lutilisateur, vous devez lindiquer au thread. Les nombreuses classes du
paquetage java.util.concurrent vous aideront dans cette tche.

Lactivit peut avoir t supprime alors que le travail en arrire-plan se poursuit.


Aprs avoir lanc lactivit, lutilisateur peut avoir reu un appel tlphonique, suivi
dun SMS ; il a ventuellement ensuite effectu une recherche dans la liste de ses
contacts. Toutes ces oprations peuvent suffire supprimer votre activit de la
mmoire. Le prochain chapitre prsentera les diffrents vnements par lesquels passe
une activit ; vous devez grer les bons et vous assurer de mettre correctement fin au
thread en arrire-plan lorsque vous en avez loccasion.

Un utilisateur risque dtre assez mcontent si vous consommez beaucoup de


temps CPU et dautonomie de la batterie sans rien lui donner en retour. Tactiquement,
ceci signifie que vous avez tout intrt utiliser une ProgressBar ou tout autre moyen
de faire savoir lutilisateur quil se passe vraiment quelque chose. Stratgiquement,
cela implique que vous devez tre efficace les threads en arrire-plan ne peuvent pas
servir dexcuse un code inutile et lent.

Une erreur peut se produire au cours de lexcution du traitement en arrire-plan


pendant que vous rcuprez des informations partir dInternet, le terminal peut
perdre sa connectivit, par exemple. La meilleure solution consiste alors prvenir
lutilisateur du problme via une Notification, puis mettre fin au thread en arrireplan.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 173 Dimanche, 8. novembre 2009 12:23 12

16
Gestion des vnements
du cycle de vie
dune activit
Bien que cela puisse ressembler un disque ray, noubliez pas que les terminaux Android
sont, avant tout, des tlphones. Pour cette raison, certaines activits sont plus importantes
que dautres pour un utilisateur, pouvoir recevoir un appel est srement plus important
que faire un sudoku. En outre, un tlphone possde gnralement moins de mmoire
quun ordinateur de bureau ou quun ordinateur portable.
Par consquent, votre activit risque de se faire tuer parce que dautres activits ont lieu et
que le systme a besoin de la mmoire quelle occupe. Il faut considrer tout cela comme
lquivalent Android du "cercle de la vie" votre activit meurt afin que dautres puissent
vivre, etc. Vous ne pouvez donc pas supposer que votre application sexcutera jusqu son
terme, que ce soit de votre point de vue ou de celui de lutilisateur.
Cest lun des exemples peut-tre le plus important de limpact du cycle de vie dune
activit sur le droulement de vos propres applications. Dans ce chapitre, nous prsenterons les diffrents tats qui composent le cycle de vie dune activit et la faon de les grer
correctement.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 174 Dimanche, 8. novembre 2009 12:23 12

174

Lart du dveloppement Android

Lactivit de Schroedinger
En gnral, une activit se trouve toujours dans lun des quatre tats suivants :

Active. Lactivit a t lance par lutilisateur, elle sexcute au premier plan. Cest
cet tat que lon pense quand on voque le fonctionnement dune activit.

En pause. Lactivit a t lance par lutilisateur, elle sexcute et elle est visible, mais
une notification ou un autre vnement occupe une partie de lcran. Pendant ce temps,
lutilisateur voit lactivit mais peut ne pas tre capable dinteragir avec elle.
Lorsquun appel tlphonique est reu, lutilisateur a lopportunit de prendre cet
appel ou de lignorer, par exemple.

Stoppe. Lactivit a t lance par lutilisateur, elle sexcute mais est cache par
dautres activits qui ont t lances ou vers lesquelles le systme a bascul. Votre
application ne pourra rien prsenter dintressant lutilisateur directement : elle ne
peut passer que par une Notification.

Morte. Lactivit na jamais t lance (le tlphone vient dtre rinitialis, par exemple)
ou elle a t tue, probablement cause dun manque de mmoire.

Vie et mort dune activit


Android fera appel votre activit en considrant les transitions entre les quatre tats que
nous venons de prsenter. Certaines transitions peuvent provoquer plusieurs appels votre
activit, via les mthodes prsentes dans cette section ; parfois, Android tuera votre application sans lappeler. Tout ceci est assez flou et sujet modifications : cest la raison pour
laquelle vous devez consulter attentivement la documentation officielle dAndroid en plus
de cette section pour dcider des vnements qui mritent attention et de ceux que vous
pouvez ignorer.
Notez que vous devez appeler les versions de la superclasse lorsque vous implmentez les
mthodes dcrites ici ; sinon Android peut lever une exception.

onCreate() et onDestroy()
Tous les exemples que nous avons vus jusqu maintenant ont implment onCreate()
dans leurs sous-classes dActivity.
Cette mthode sera appele dans les trois cas suivants :

Lorsque lactivit est lance pour la premire fois (aprs le redmarrage du systme,
par exemple), onCreate() est appele avec le paramtre null.

Si lactivit sest excute, puis quelle a t tue, onCreate() sera appele avec, pour
paramtre, le Bundle obtenu par onSaveInstanceState() (voir plus loin).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 175 Dimanche, 8. novembre 2009 12:23 12

Chapitre 16

Gestion des vnements du cycle de vie dune activit

175

Si lactivit sest excute et que vous layez configure pour quelle utilise des ressources
diffrentes en fonction des tats du terminal (mode portrait ou mode paysage, par
exemple), elle sera recre et onCreate() sera donc appele.

Cest dans cette mthode que vous configurez linterface utilisateur et tout ce qui ne doit
tre fait quune seule fois, quelle que soit lutilisation de lactivit.
lautre extrmit du cycle de vie, onDestroy() peut tre appele lorsque lactivit prend
fin, soit parce quelle a appel finish() (qui "finit" lactivit), soit parce quAndroid a
besoin de mmoire et la ferme prmaturment. onDestroy() peut ne pas tre appele si
ce besoin de mmoire est urgent (la rception dun appel tlphonique, par exemple) et
que lactivit se terminera quoi quil en soit. Par consquent, onDestroy() est essentiellement destine librer les ressources obtenues dans onCreate().

onStart(), onRestart() et onStop()


Une activit peut tre place au premier plan, soit parce quelle vient dtre lance, soit
parce quelle y a t mise aprs avoir t cache (par une autre activit ou la rception dun
appel, par exemple).
Dans ces deux cas, cest la mthode onStart() qui est appele. onRestart() nest invoque
que lorsque lactivit a t stoppe et redmarre.
Inversement, onStop() est appele lorsque lactivit va tre stoppe.

onPause() et onResume()
La mthode onResume() est appele immdiatement avant que lactivit ne passe au
premier plan, soit parce quelle vient dtre lance, soit parce quelle est repartie aprs
avoir t stoppe, soit aprs la fermeture dune bote de dialogue (ouverte par la rception
dun appel, par exemple). Cest donc un bon endroit pour reconstruire linterface en fonction de ce qui sest pass depuis que lutilisateur la vue pour la dernire fois. Si votre activit interroge un service pour savoir sil y a de nouvelles informations (de nouvelles
entres dans un flux RSS, par exemple), onResume() est le bon moyen de rafrachir la vue
courante et, si ncessaire, de lancer un thread en arrire-plan (via un Handler, par exemple)
pour modifier cette vue.
Inversement, tout ce qui dtourne lutilisateur de votre activit essentiellement lactivation dune autre activit provoquera lappel donPause(). Vous pouvez profiter de cette
mthode pour annuler tout ce que vous aviez fait dans onResume() : arrter les threads en
arrire-plan, librer les ressources en accs exclusif que vous auriez pu prendre (lappareil
photo, par exemple), etc.
Lorsque onPause() a t appele, Android se rserve le droit de tuer tout moment le
processus de lactivit. Par consquent, vous ne devriez pas supposer que vous pourrez
recevoir dautre vnement de la part de celle-ci.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 176 Dimanche, 8. novembre 2009 12:23 12

176

Lart du dveloppement Android

Ltat de grce
Pour lessentiel, les mthodes que nous venons de mentionner interviennent au niveau
gnral de lapplication (onCreate() relie les dernires parties de linterface, onPause()
ferme les threads en arrire-plan, etc.).
Cependant, Android a pour but de fournir une apparence de simplicit de fonctionnement.
Autrement dit, bien que les activits puissent aller et venir en fonction des besoins
mmoire, les utilisateurs ne devraient pas savoir ce quil se trame. Un utilisateur qui utilisait la calculette devrait donc retrouver le ou les nombres sur lesquels il travaillait lorsquil
la rutilise aprs une absence sauf sil avait lui-mme ferm la calculette.
Pour que tout cela fonctionne, les activits doivent donc pouvoir sauvegarder, rapidement
et efficacement, ltat de linstance de lapplication quelles excutent. En outre, comme
elles peuvent tre tues tout moment, les activits peuvent devoir sauvegarder cet tat
plus frquemment quon ne pourrait le supposer. Rciproquement, une activit qui redmarre doit rcuprer son tat antrieur afin dapparatre dans la situation o elle se trouvait
prcdemment.
La sauvegarde de ltat dune instance est gre par la mthode onSaveInstanceState(),
qui fournit un objet Bundle dans lequel lactivit peut placer les donnes quelle souhaite
(le nombre affich par la calculette, par exemple). Limplmentation de cette mthode doit
tre rapide nessayez pas den faire trop : placez simplement les donnes dans le Bundle
et quittez la mthode.
Vous pouvez rcuprer ltat de linstance dans les mthodes onCreate() et onRestoreInstanceState() : cest vous qui dcidez du moment dappliquer cet tat votre activit
lune ou lautre de ces mthodes de rappel convient.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 177 Dimanche, 8. novembre 2009 12:23 12

Partie

III

Stockage de donnes,
services rseaux et API
CHAPITRE 17.

Utilisation des prfrences

CHAPITRE 18.

Accs aux fichiers

CHAPITRE 19.

Utilisation des ressources

CHAPITRE 20.

Accs et gestion des bases de donnes locales

CHAPITRE 21.

Tirer le meilleur parti des bibliothques Java

CHAPITRE 22.

Communiquer via Internet

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 178 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 179 Dimanche, 8. novembre 2009 12:23 12

17
Utilisation des prfrences
Android offre plusieurs moyens de stocker les donnes dont a besoin une activit. Le plus
simple consiste utiliser le systme des prfrences.
Les activits et les applications peuvent sauvegarder leurs prfrences sous la forme de
paires cls/valeurs qui persisteront entre deux appels. Comme leur nom lindique, le but
principal de ces prfrences est de permettre la mmorisation dune configuration choisie
par lutilisateur le dernier flux consult par le lecteur RSS, lordre de tri par dfaut des
listes, etc. Vous pouvez, bien sr, les stocker comme vous le souhaitez du moment que les
cls sont des chanes (String) et les valeurs, des types primitifs (boolean, String, etc.).
Les prfrences peuvent tre propres une activit ou partages par toutes les activits
dune application. Elles peuvent mme tre partages par les applications, bien que cela ne
soit pas encore possible avec les versions actuelles dAndroid.

Obtenir ce que vous voulez


Pour accder aux prfrences, vous pouvez :
appeler la mthode getPreferences() partir de votre activit pour accder ses
prfrences spcifiques ;
appeler la mthode getSharedPreferences() partir de votre activit (ou dun autre
Context de lapplication) pour accder aux prfrences de lapplication ;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 180 Dimanche, 8. novembre 2009 12:23 12

180

Lart du dveloppement Android

appeler la mthode getDefaultSharedPreferences() dun objet PreferencesManager pour accder aux prfrences partages qui fonctionnent de concert avec le
systme des prfrences globales dAndroid.
Les deux premiers appels prennent un mode de scurit en paramtre pour le moment,
utilisez la valeur 0. getSharedPreferences() attend galement le nom dun ensemble de
prfrences : en ralit, getPreferences() appelle getSharedPreferences() en lui passant
le nom de classe de votre activit en paramtre. getDefaultSharedPreferences() prend
en paramtre le Context des prfrences (cest--dire votre Activity).
Toutes ces mthodes renvoient une instance de SharedPreferences qui fournit un ensemble de mthodes daccs aux prfrences par leurs noms. Ces mthodes renvoient un rsultat du type adquat (getBoolean(), par exemple, renvoie une prfrence boolenne).
Elles attendent galement une valeur par dfaut, qui sera renvoye sil nexiste pas de
prfrences ayant la cl indique.

Dfinir vos prfrences


partir dun objet SharedPreferences, vous pouvez appeler edit() pour obtenir un
"diteur" pour les prfrences. Cet objet dispose dun ensemble de mthodes modificatrices
limage des mthodes daccs de lobjet parent SharedPreferences. Il fournit galement
les mthodes suivantes :
remove() pour supprimer une prfrence par son nom ;
clear() pour supprimer toutes les prfrences ;
commit() pour valider les modifications effectues via lditeur.
La dernire est importante : si vous modifiez les prfrences avec lditeur et que vous nappeliez pas commit(), les modifications disparatront lorsque lditeur sera hors de porte.
Inversement, comme les prfrences acceptent des modifications en direct, si lune des
parties de votre application (une activit, par exemple) modifie des prfrences partages,
les autres parties (tel un service) auront immdiatement accs la nouvelle valeur.

Un mot sur le framework


partir de sa version 0.9, Android dispose dun framework de gestion des prfrences qui
ne change rien ce que nous venons de dcrire. En fait, il existe surtout pour prsenter un
ensemble cohrent de prfrences aux utilisateurs, afin que les applications naient pas
rinventer la roue.
Llment central de ce framework est encore une structure de donnes XML. Vous
pouvez dcrire les prfrences de votre application dans un fichier XML stock dans le
rpertoire res/xml/ du projet. partir de ce fichier, Android peut prsenter une interface
graphique pour manipuler ces prfrences, qui seront ensuite stockes dans lobjet SharedPreferences obtenu par getDefaultSharedPreferences().

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 181 Dimanche, 8. novembre 2009 12:23 12

Chapitre 17

Utilisation des prfrences

181

Voici, par exemple, le contenu dun fichier XML des prfrences, extrait du projet Prefs/
Simple :
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="@string/checkbox"
android:title="Preference case a cocher"
android:summary="Cochez ou decochez" />
<RingtonePreference
android:key="@string/ringtone"
android:title="Preference sonnerie"
android:showDefault="true"
android:showSilent="true"
android:summary="Choisissez une sonnerie" />
</PreferenceScreen>

La racine de ce document XML est un lment PreferenceScreen (nous expliquerons


plus loin pourquoi il sappelle ainsi). Comme le montre ce fichier, PreferenceScreen
peut contenir des dfinitions de prfrences des sous-classes de Preference, comme
CheckBoxPreference ou RingtonePreference. Comme lon pourrait sy attendre, elles
permettent, respectivement, de cocher une case et de choisir une sonnerie. Dans le cas de
RingtonePreference, vous pouvez autoriser les utilisateurs choisir la sonnerie par
dfaut du systme ou "silence".

Laisser les utilisateurs choisir


Lorsque vous avez mis en place le fichier XML des prfrences, vous pouvez utiliser une
activit "quasi intgre" pour permettre aux utilisateurs de faire leur choix. Cette activit
est "quasi intgre" car il suffit den crer une sous-classe, de la faire pointer vers ce
fichier et de la lier au reste de votre application.
Voici, par exemple, lactivit EditPreferences du projet Prefs/Simple :
package com.commonsware.android.prefs;
import android.app.Activity;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class EditPreferences extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 182 Dimanche, 8. novembre 2009 12:23 12

182

Lart du dveloppement Android

Comme vous pouvez le constater, il ny a pas grand-chose lire car il suffit dappeler
addPreferencesFromResource() en lui indiquant la ressource XML contenant les prfrences.
Vous devrez galement ajouter cette activit votre fichier AndroidManifest.xml :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.prefs">
<application android:label="@string/app_name">
<activity
android:name=".SimplePrefsDemo"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".EditPreferences"
android:label="@string/app_name">
</activity>
</application>
</manifest>

Voici le code de SimplePrefsDemo, qui permet dappeler cette activit partir dun menu
doptions :
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, EDIT_ID, Menu.NONE, "Modifier Prefs")
.setIcon(R.drawable.misc)
.setAlphabeticShortcut(m);
menu.add(Menu.NONE, CLOSE_ID, Menu.NONE, "Fermeture")
.setIcon(R.drawable.eject)
.setAlphabeticShortcut(f);
return(super.onCreateOptionsMenu(menu));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case EDIT_ID:
startActivity(new Intent(this, EditPreferences.class));
return(true);
case CLOSE_ID:
finish();
return(true);
}
return(super.onOptionsItemSelected(item));
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 183 Dimanche, 8. novembre 2009 12:23 12

Chapitre 17

Utilisation des prfrences

183

La Figure 17.1 montre linterface de configuration des prfrences de cette application.


Figure 17.1
Linterface des prfrences de SimplePrefsDemo.

La case cocher peut tre coche ou dcoche directement. Pour modifier la sonnerie, il
suffit de cliquer sur son entre dans les prfrences pour voir apparatre une bote de dialogue
comme celle de la Figure 17.2.
Figure 17.2
Choix dune sonnerie
partir des prfrences.

Vous remarquerez quil nexiste pas de bouton "Save" ou "Commit" : les modifications
sont sauvegardes ds quelles sont faites.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 184 Dimanche, 8. novembre 2009 12:23 12

184

Lart du dveloppement Android

Outre ce menu, lapplication SimplePrefsDemo affiche galement les prfrences courantes


via un TableLayout :
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TableRow>
<TextView
android:text="Case a cocher:"
android:paddingRight="5px"
/>
<TextView android:id="@+id/checkbox"
/>
</TableRow>
<TableRow>
<TextView
android:text="Sonnerie:"
android:paddingRight="5px"
/>
<TextView android:id="@+id/ringtone"
/>
</TableRow>
</TableLayout>

Les champs de cette table se trouvent dans la mthode onCreate() :


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
checkbox=(TextView)findViewById(R.id.checkbox);
ringtone=(TextView)findViewById(R.id.ringtone);
}

Ils sont mis jour chaque appel donResume() :


@Override
public void onResume() {
super.onResume();
SharedPreferences prefs=PreferenceManager
.getDefaultSharedPreferences(this);
checkbox.setText(new Boolean(prefs
.getBoolean("checkbox", false))
.toString());
ringtone.setText(prefs.getString("ringtone", "<unset>"));
}

Ceci signifie que les champs seront modifis louverture de lactivit et aprs la fin de
lactivit des prfrences (via le bouton "back", par exemple).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 185 Dimanche, 8. novembre 2009 12:23 12

Chapitre 17

Utilisation des prfrences

185

La Figure 17.3 montre le contenu de cette table aprs le choix de lutilisateur.


Figure 17.3
Liste des prfrences de
SimplePrefsDemo.

Ajouter un peu de structure


Si vous devez proposer un grand nombre de prfrences configurer, les mettre toutes
dans une seule longue liste risque dtre peu pratique. Le framework dAndroid permet
donc de structurer un ensemble de prfrences en catgories ou en crans.
Les catgories sont cres laide dlments PreferenceCategory dans le fichier XML des
prfrences et permettent de regrouper des prfrences apparentes. Au lieu quelles soient
toutes des filles de la racine PreferenceScreen, vous pouvez placer vos prfrences dans
les catgories appropries en plaant des lments PreferenceCategory sous la racine.
Visuellement, ces groupes seront spars par une barre contenant le titre de la catgorie.
Si vous avez un trs grand nombre de prfrences trop pour quil soit envisageable que
lutilisateur les fasse dfiler , vous pouvez galement les placer dans des "crans"
distincts laide de llment PreferenceScreen (le mme que llment racine).
Tout fils de PreferenceScreen est plac dans son propre cran. Si vous imbriquez des
PreferenceScreen, lcran pre affichera lcran fils sous la forme dune entre : en la
touchant, vous ferez apparatre le contenu de lcran fils correspondant.
Le fichier XML des prfrences du projet Prefs/Structured contient la fois des
lments PreferenceCategory et des lments PreferenceScreen imbriqus :
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Preferences simples">

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 186 Dimanche, 8. novembre 2009 12:23 12

186

Lart du dveloppement Android

<CheckBoxPreference
android:key="@string/checkbox"
android:title="Preference case a cocher"
android:summary="Cochez ou decochez"
/>
<RingtonePreference
android:key="@string/ringtone"
android:title="Preference sonnerie"
android:showDefault="true"
android:showSilent="true"
android:summary="Choisissez une sonnerie"
/>
</PreferenceCategory>
<PreferenceCategory android:title="Ecrans">
<PreferenceScreen
android:key="detail"
android:title="Ecran"
android:summary="Preferences supplementaires dans une autre page">
<CheckBoxPreference
android:key="@string/checkbox2"
android:title="Une autre case"
android:summary="On ou Off. Peu importe."
/>
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

Utilis avec notre implmentation de PreferenceActivity, ce fichier XML produit une


liste dlments comme ceux de la Figure 17.4, regroups en catgories.
Figure 17.4
Linterface des prfrences de StructuredPrefsDemo, montrant les
catgories et un marqueur
dcran.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 187 Dimanche, 8. novembre 2009 12:23 12

Chapitre 17

Utilisation des prfrences

187

En touchant lentre du sous-cran, celui-ci saffiche et montre les prfrences quil


contient (voir Figure 17.5).
Figure 17.5
Le sous-cran de prfrences de StructuredPrefsDemo.

Botes de dialogue
Toutes les prfrences ne sont pas, bien sr, que des cases cocher et des sonneries.
Pour les autres, comme les champs de saisie et les listes, Android utilise des botes de
dialogue. Les utilisateurs nentrent plus directement leurs prfrences dans lactivit interface des prfrences mais touchent une prfrence, remplissent une valeur et cliquent sur
OK pour valider leur modification.
Comme le montre ce fichier XML, extrait du projet Prefs/Dialogs, les champs et les
listes nont pas une structure bien diffrente de celle des autres types :
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Simple Preferences">
<CheckBoxPreference
android:key="@string/checkbox"
android:title="Preference case a cocher"
android:summary="Cochez ou decochez"
/>
<RingtonePreference
android:key="@string/ringtone"
android:title="Preference sonnerie"
android:showDefault="true"
android:showSilent="true"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 188 Dimanche, 8. novembre 2009 12:23 12

188

Lart du dveloppement Android

android:summary="Choisissez une sonnerie"


/>
</PreferenceCategory>
<PreferenceCategory android:title="Ecrans">
<PreferenceScreen
android:key="detail"
android:title="Ecran"
android:summary="Preferences supplementaires dans une autre page">
<CheckBoxPreference
android:key="@string/checkbox2"
android:title="Autre case a cocher"
android:summary="On ou Off. Peu importe."
/>
</PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory android:title="Preferences simples">
<EditTextPreference
android:key="@string/text"
android:title="Dialogue de saisie dun texte"
android:summary="Cliquez pour ouvrir un champ de saisie"
android:dialogTitle="Entrez un texte interessant"
/>
<ListPreference
android:key="@string/list"
android:title="Dialogue de choix"
android:summary="Cliquez pour ouvrir une liste de choix"
android:entries="@array/villes"
android:entryValues="@array/codes_aeroports"
android:dialogTitle="Choisissez une ville" />
</PreferenceCategory>
</PreferenceScreen>

Pour le champ de texte (EditTextPreference), outre le titre et le rsum de la prfrence


elle-mme, nous donnons galement un titre la bote de dialogue.
Pour la liste (ListPreference), on fournit la fois un titre au dialogue et deux ressources
de type tableau de chanes : lun pour les noms affichs, lautre pour les valeurs correspondantes qui doivent tre dans le mme ordre lindice du nom affich dtermine la valeur
stocke dans lobjet SharedPreferences. Voici par exemple les tableaux utiliss pour
cette liste :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="villes">
<item>Philadelphia</item>
<item>Pittsburgh</item>
<item>Allentown/Bethlehem</item>
<item>Erie</item>
<item>Reading</item>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 189 Dimanche, 8. novembre 2009 12:23 12

Chapitre 17

Utilisation des prfrences

189

<item>Scranton</item>
<item>Lancaster</item>
<item>Altoona</item>
<item>Harrisburg</item>
</string-array>
<string-array name="codes_aeroports">
<item>PHL</item>
<item>PIT</item>
<item>ABE</item>
<item>ERI</item>
<item>RDG</item>
<item>AVP</item>
<item>LNS</item>
<item>AOO</item>
<item>MDT</item>
</string-array>
</resources>

Comme le montre la Figure 17.6, les prfrences comprennent dsormais une catgorie
supplmentaire contenant deux nouvelles entres.
Figure 17.6
Lcran des prfrences
de DialogsDemo.

Toucher lentre "Dialogue de saisie dun texte" provoque laffichage dun... dialogue de
saisie dun texte ici, il est prrempli avec la valeur courante de la prfrence (voir
Figure 17.7).
Toucher lentre "Dialogue de choix" affiche... une liste de choix sous forme de bote de
dialogue prsentant les noms des villes (voir Figure 17.8).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 190 Dimanche, 8. novembre 2009 12:23 12

190

Lart du dveloppement Android

Figure 17.7
Modification dune prfrence avec un champ de
saisie.

Figure 17.8
Modification dune prfrence avec une liste.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 191 Dimanche, 8. novembre 2009 12:23 12

18
Accs aux fichiers
Bien quAndroid dispose de moyens de stockage structurs via les prfrences et les bases
de donnes, un simple fichier suffit parfois. Android offre donc deux modles daccs aux
fichiers : lun pour les fichiers fournis dans le paquetage de lapplication, un autre pour
ceux qui sont crs sur le terminal par lapplication.

Allons-y !
Supposons que vous vouliez fournir des donnes statiques avec une application : une liste
de mots pour un correcteur orthographique, par exemple. Le moyen le plus simple dy
parvenir consiste placer ces donnes dans un fichier situ dans le rpertoire res/raw:
elles seront alors intgres au fichier APK de lapplication comme une ressource brute au
cours du processus dempaquetage.
Pour accder ce fichier, vous avez besoin dun objet Resources que vous pouvez obtenir
partir de lactivit en appelant getResources(). Cet objet fournit la mthode openRawResource() pour rcuprer un InputStream sur le fichier spcifi par son identifiant
(un entier). Cela fonctionne exactement comme laccs aux widgets avec findViewById() : si vous placez un fichier mots.xml dans res/raw, son identifiant dans le
code Java sera R.raw.mots.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 192 Dimanche, 8. novembre 2009 12:23 12

192

Lart du dveloppement Android

Comme vous ne pouvez obtenir quun InputStream, vous navez aucun moyen de modifier ce fichier : cette approche nest donc vraiment utile que pour lire des donnes statiques. En outre, comme elles ne changeront pas jusqu ce que lutilisateur installe une
nouvelle version de votre paquetage, ces donnes doivent tre valides pour une certaine
priode ou vous devrez fournir un moyen de les mettre jour. Le moyen le plus simple de
rgler ce problme consiste utiliser ces donnes pour construire une autre forme de stockage qui, elle, sera modifiable (une base de donnes, par exemple), mais cela impose
davoir deux copies des donnes. Une autre solution consiste les conserver telles quelles
et placer les modifications dans un autre fichier ou dans une base de donnes, puis les
fusionner lorsque vous avez besoin dune vue complte de ces informations. Si votre application fournit une liste dURL, par exemple, vous pourriez grer un deuxime fichier pour
stocker les URL ajoutes par lutilisateur ou pour rfrencer celles quil a supprimes.
Le projet Files/Static reprend lexemple ListViewDemo du Chapitre 8 en utilisant cette
fois-ci un fichier XML la place dun tableau dfini directement dans le programme.
Le fichier de description XML est identique dans les deux cas :
<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
/>
</LinearLayout>

On a galement besoin dun autre fichier XML contenant les mots de la liste :
<words>
<word
<word
<word
<word
<word
<word
<word
<word
<word
<word

value="lorem" />
value="ipsum" />
value="dolor" />
value="sit" />
value="amet" />
value="consectetuer" />
value="adipiscing" />
value="elit" />
value="morbi" />
value="vel" />

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 193 Dimanche, 8. novembre 2009 12:23 12

Chapitre 18

Accs aux fichiers

193

<word value="ligula" />


<word value="vitae" />
<word value="arcu" />
<word value="aliquet" />
<word value="mollis" />
<word value="etiam" />
<word value="vel" />
<word value="erat" />
<word value="placerat" />
<word value="ante" />
<word value="porttitor" />
<word value="sodales" />
<word value="pellentesque" />
<word value="augue" />
<word value="purus" />
</words>

Bien que cette structure XML ne soit pas exactement un modle de concision, elle suffira
pour notre dmonstration.
Le code Java doit maintenant lire ce fichier, en extraire les mots et les placer quelque part
pour que lon puisse remplir la liste :
public class StaticFileDemo extends ListActivity {
TextView selection;
ArrayList<String> items=new ArrayList<String>();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
try {
InputStream in=getResources().openRawResource(R.raw.mots);
DocumentBuilder builder=DocumentBuilderFactory
.newInstance()
.newDocumentBuilder();
Document doc=builder.parse(in, null);
NodeList words=doc.getElementsByTagName("word");
for (int i=0;i<words.getLength();i++) {
items.add(((Element)words.item(i)).getAttribute("value"));
}
in.close();
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 194 Dimanche, 8. novembre 2009 12:23 12

194

Lart du dveloppement Android

catch (Throwable t) {
Toast
.makeText(this, "Exception : " + t.toString(), 2000)
.show();
}
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items));
}
public void onListItemClick(ListView parent, View v, int position,
long id) {
selection.setText(items.get(position).toString());
}
}

Les diffrences entre cet exemple et celui du Chapitre 8 se situent essentiellement dans le
corps de la mthode onCreate(). Ici, on obtient un InputStream pour le fichier XML en
appelant getResources().openRawResource(R.raw.mots), puis on se sert des fonctionnalits danalyse XML prdfinies pour transformer le fichier en document DOM, en
extraire les lments word et placer les valeurs de leurs attributs value dans un objet
ArrayList qui sera utilis par lArrayAdapter.
Comme le montre la Figure 18.1, le rsultat de lactivit est identique celui lexemple du
Chapitre 8 car la liste de mots est la mme.
Figure 18.1
Lapplication
StaticFileDemo.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 195 Dimanche, 8. novembre 2009 12:23 12

Chapitre 18

Accs aux fichiers

195

Comme nous le verrons au chapitre suivant, il existe videmment des moyens encore plus
simples dutiliser les fichiers XML contenus dans le paquetage dune application : utiliser
une ressource XML, par exemple. Cependant, bien que cet exemple utilise XML, le fichier
aurait pu simplement contenir un mot par ligne ou utiliser un format non reconnu nativement
par le systme de ressources dAndroid.

Lire et crire
Lire et crire ses propres fichiers de donnes spcifiques est quasiment identique ce que
lon pourrait faire avec une application Java traditionnelle. La solution consiste utiliser
les mthodes openFileInput() et openFileOutput() sur lactivit ou tout autre
Context afin dobtenir, respectivement, un InputStream et un OutputStream. partir de
l, le processus nest pas beaucoup diffrent de celui des E/S Java classiques :

On enveloppe ces flux selon les besoins, par exemple avec un InputStreamReader ou
un OutputStreamWriter si lon souhaite effectuer des E/S en mode texte.

On lit ou on crit les donnes.

On libre le flux avec close() lorsquon a termin.

Deux applications qui essaient de lire en mme temps un fichier notes.txt via openFileInput() accderont chacune leur propre dition du fichier. Si vous voulez quun
mme fichier soit accessible partir de plusieurs endroits, vous devrez srement crer un
fournisseur de contenu, comme on lexplique au Chapitre 28. Notez galement quopenFileInput() et openFileOutput() ne prennent pas en paramtre des chemins daccs
(chemin/vers/fichier.txt, par exemple), mais uniquement des noms de fichiers.
Le code suivant, extrait du projet Files/ReadWrite, montre la disposition de lditeur de
texte le plus simple du monde :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<Button android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fermer" />
<EditText
android:id="@+id/editor"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:singleLine="false"
/>
</LinearLayout>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 196 Dimanche, 8. novembre 2009 12:23 12

196

Lart du dveloppement Android

Les deux seuls lments de linterface sont un gros widget ddition de texte et un bouton
"Fermer" plac en dessous. Le code Java est peine plus compliqu :
package com.commonsware.android.files;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class ReadWriteFileDemo extends Activity {
private final static String NOTES="notes.txt";
private EditText editor;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
editor=(EditText)findViewById(R.id.editor);
Button btn=(Button)findViewById(R.id.close);
btn.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
public void onResume() {
super.onResume();
try {
InputStream in=openFileInput(NOTES);
if (in!=null) {
InputStreamReader tmp=new InputStreamReader(in);
BufferedReader reader=new BufferedReader(tmp);
String str;
StringBuffer buf=new StringBuffer();
while ((str = reader.readLine())!= null) {
buf.append(str + "\n");
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 197 Dimanche, 8. novembre 2009 12:23 12

Chapitre 18

Accs aux fichiers

197

in.close();
editor.setText(buf.toString());
}
}
catch (java.io.FileNotFoundException e) {
// Ok, nous ne lavons probablement pas encore cr
}
catch (Throwable t) {
Toast
.makeText(this, "Exception : "+ t.toString(), 2000)
.show();
}
}
public void onPause() {
super.onPause();
try {
OutputStreamWriter out=
new OutputStreamWriter(openFileOutput(NOTES, 0));
out.write(editor.getText().toString());
out.close();
}
catch (Throwable t) {
Toast
.makeText(this, "Exception : "+ t.toString(), 2000)
.show();
}
}
}

Nous commenons par lier le bouton la fermeture de notre activit, en invoquant


finish() sur lactivit partir de setOnClickListener() lorsque nous cliquons sur le
bouton.
Puis nous nous servons donResume() pour prendre le contrle lorsque notre diteur
devient actif (aprs son lancement) ou le redevient (aprs avoir t fig). Nous lisons le
fichier notes.txt laide dopenFileInput() et nous y plaons son contenu dans
lditeur de texte. Si le fichier na pas t trouv, nous supposons que cest parce que cest
la premire fois que lactivit sexcute (ou que le fichier a t supprim par dautres
moyens) et nous nous contentons de laisser lditeur vide.
Enfin, nous nous servons donPause() pour prendre le contrle lorsque notre activit a t
cache par une autre ou quelle a t ferme (par notre bouton "Fermer", par exemple).
Nous ouvrons alors le fichier notes.txt laide dopenFileOutput() et nous y plaons
le contenu de lditeur de texte.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 198 Dimanche, 8. novembre 2009 12:23 12

198

Lart du dveloppement Android

Le rsultat est un bloc-notes persistant : tout ce qui est tap le restera jusqu sa suppression ; le texte survivra la fermeture de lactivit, lextinction du tlphone et aux autres
situations similaires.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 199 Dimanche, 8. novembre 2009 12:23 12

19
Utilisation des ressources
Les ressources sont des informations statiques, stockes en dehors du code Java. Dans les
exemples de ce livre, vous avez dj souvent rencontr un type de ressource les fichiers
de description (layouts) , mais il en existe de nombreux autres dont vos applications
peuvent tirer profit : les images et les chanes de caractres, par exemple.

Les diffrents types de ressources


Les ressources dun projet Android sont stockes dans des fichiers situs sous le rpertoire
res/ de larborescence. lexception des ressources brutes (res/raw/), tous les types de
ressources sont analyss automatiquement, soit par le systme de paquetages dAndroid,
soit par le systme du terminal ou de lmulateur. Si vous dcrivez, par exemple, linterface utilisateur dune activit via une ressource de type layout (dans res/layout), vous
navez pas besoin danalyser vous-mme le contenu du fichier XML Android sen
chargera pour vous.
Outre les layouts (que nous avons rencontrs pour la premire fois au Chapitre 5) et les
ressources brutes (introduites au Chapitre 18), il existe plusieurs autres types de ressources :

Les animations (res/anim/) sont destines aux animations courtes qui font partie de
linterface utilisateur : la simulation dune page qui se tourne quand on clique sur un
bouton, par exemple.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 200 Dimanche, 8. novembre 2009 12:23 12

200

Lart du dveloppement Android

Les images (res/drawable) permettent de placer des icnes statiques ou dautres


images dans une interface utilisateur.

Les chanes, les couleurs, les tableaux et les dimensions (res/values/) permettent
dassocier des noms symboliques ces types de constantes et de les sparer du reste du
code (pour linternationalisation et la localisation, notamment).

Les fichiers XML statiques (res/xml/) permettent de stocker vos propres donnes et
structures.

Thorie des chanes


Placer les labels et les autres textes lextrieur du code source de lapplication est, gnralement, une trs bonne ide. Ceci facilite, notamment, linternationalisation (I18N) et la
localisation (L10N), qui sont prsentes un peu plus loin dans ce chapitre. Mme si vous
ne comptez pas traduire vos textes dans dautres langues, cette sparation facilite les
corrections car toutes les chanes sont au mme endroit au lieu dtre dissmines dans le
code source.
Android permet dutiliser des chanes externes classiques, mais galement des "formats de
chanes", contenant des emplacements qui seront remplacs par des informations au cours
de lexcution. En outre, le formatage de texte simple, appel "texte styl", est galement
disponible, ce qui permet de mlanger des mots en gras ou en italique avec du texte
normal.

Chanes normales
En rgle gnrale, vous navez besoin pour les chanes normales que dun fichier XML
situ dans le rpertoire res/values (le plus souvent res/values/strings.xml). La
racine de ce document est llment ressources, qui a autant de fils string quil y a de
chanes encoder comme ressource. Llment string a un attribut name, contenant le
nom unique de la chane. Le contenu de cet lment est le texte de la chane :
<resources>
<string name="whisky">Portez ce vieux whisky...</string>
<string name="zephir">Le vif zphir jubile...</string>
</resources>

Le seul point pineux concerne la prsence de guillemets (") ou dapostrophes () dans le


texte de la chane car vous devrez alors les protger en les prfixant dun antislash (il
fait beau aujourd\hui, par exemple). Dans le cas de lapostrophe, vous pouvez galement
placer tout le texte entre guillemets ("il fait beau aujourdhui").
Vous pouvez ensuite faire rfrence cette chane depuis un fichier de description (sous la
forme @string/whisky, ou @string/zephir, par exemple) ou y accder depuis votre

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 201 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

201

code Java laide de getString(), en lui passant lidentifiant de ressource de la chane,


cest--dire son nom unique prfix par R.string (comme getString(R.string.whisky)).

Formats de chanes
Comme les autres implmentations du langage Java, la machine virtuelle Dalvik
dAndroid reconnat les formats de chanes. Ces formats sont des chanes contenant des
marqueurs demplacements et seront remplacs lors de lexcution par des donnes variables (Mon nom est %1$s, par exemple). Les chanes normales stockes sous forme de
ressources peuvent tre utilises comme des formats de chanes :
String strFormat=getString(R.string.mon_nom);
String strResult=String.format(strFormat, "Tim");
((TextView)findViewById(R.id.un_label))
.setText(strResult);

Texte styl
Pour enrichir du texte, vous pourriez utiliser des ressources brutes contenant du HTML,
puis les placer dans un widget WebKit. Cependant, pour un formatage lger utilisant <b>,
<i> et <u>, une ressource chane fera laffaire :
<resources>
<string name="b">Ce texte est en <b>gras</b>.</string>
<string name="i">Alors que celui-ci est en <i>italiques</i> !</string>
</resources>

Vous pouvez ensuite y accder comme nimporte quelle autre chane normale, sauf que
le rsultat de lappel getString() sera ici un objet implmentant linterface
android.text.Spanned :
((TextView)findViewById(R.id.autre_label))
.setText(getString(R.string.i));

Formats styls
Les styles deviennent compliqus grer lorsquil sagit de les utiliser avec les formats de
chanes. En effet, String.format() sapplique des objets String, pas des objets
Spanned disposant dinstructions de formatage. Si vous avez vraiment besoin de formats
de chanes styls, vous pouvez suivre ces tapes :
1. Dans la ressource chane, remplacez les chevrons par des entits HTML (Je suis
&lt;b&gt;%1$s&lt;/b&gt;, par exemple).
2. Rcuprez la ressource comme dhabitude, bien quelle ne soit pas encore style (avec
getString(R.string.format_style)).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 202 Dimanche, 8. novembre 2009 12:23 12

202

Lart du dveloppement Android

3. Produisez le rsultat du formatage, en vous assurant de protger les valeurs de chanes


que vous substituez, au cas o elles contiendraient des chevrons ou des esperluettes :
String.format(getString(R.string.format_style),
TextUtils.htmlEncode(nom));

4. Convertissez le HTML encod en objet Spanned grce Html.fromHtml().


uneTextView.setText(Html.fromHtml(resultatDeStringFormat));

Pour voir tout ceci en action, examinons le fichier de description du projet Resources/
Strings :
<?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"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button android:id="@+id/format"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_name"
/>
<EditText android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
<TextView android:id="@+id/result"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>

Comme vous pouvez le constater, linterface utilisateur nest compose que dun bouton,
dun champ et dun label. Le but est que lutilisateur entre son nom dans le champ puis clique
sur le bouton pour que le label soit remplac par un message format contenant ce nom.
Llment Button de ce fichier faisant rfrence une ressource chane (@string/
btn_name), nous avons besoin dun fichier de ressource chane (res/values/strings.xml) :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">StringsDemo</string>
<string name="btn_name">Nom :</string>
<string name="funky_format">Je suis &lt;b&gt;%1$s&lt;/b&gt;</string>
</resources>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 203 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

203

La ressource app_name est automatiquement cre par le script activityCreator. La


chane btn_name est le titre du bouton, tandis que le format de chane styl se trouve dans
funky_format.
Enfin, nous avons besoin dun peu de Java pour relier tous les morceaux :
package com.commonsware.android.resources;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class StringsDemo extends Activity {
EditText name;
TextView result;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
name=(EditText)findViewById(R.id.name);
result=(TextView)findViewById(R.id.result);
Button btn=(Button)findViewById(R.id.format);
btn.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
applyFormat();
}
});
}
private void applyFormat() {
String format=getString(R.string.funky_format);
String simpleResult=String.format(format,
TextUtils.htmlEncode(name.getText().toString()));
result.setText(Html.fromHtml(simpleResult));
}
}

La manipulation de la ressource chane a lieu dans applyFormat(), qui est appele


lorsque lon clique sur le bouton. On rcupre dabord notre format par un appel
getString() chose que nous aurions pu faire dans onCreate() pour plus defficacit.
Puis nous nous en servons pour formater la valeur du champ, ce qui nous renvoie une
String puisque la ressource chane est encode en HTML. Vous remarquerez que nous

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 204 Dimanche, 8. novembre 2009 12:23 12

204

Lart du dveloppement Android

utilisons TextUtils.htmlEncode() pour traduire en entits les caractres spciaux du


nom qui a t saisi, au cas o un utilisateur dciderait dentrer une esperluette, par exemple. Enfin, on convertit ce texte HTML en objet texte styl laide de Html.fromHtml() et
nous modifions notre label.
La Figure 19.1 montre que le label de lactivit est vide lors de son lancement.
Figure 19.1
Lapplication StringsDemo aprs son lancement.

La Figure 19.2 montre ce que lon obtient aprs avoir saisi un nom et cliqu sur le bouton.
Figure 19.2
La mme application,
aprs avoir entr un nom
clbre.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 205 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

205

Vous voulez gagner une image ?


Android reconnat les images aux formats PNG, JPEG et GIF, bien que GIF soit officiellement dconseill ; il est gnralement prfrable dutiliser PNG. Les images peuvent tre
utilises partout o lon attend un objet Drawable, comme pour limage et le fond dune
ImageView.
Lutilisation des images consiste simplement placer les fichiers images dans res/drawable/ puis y faire rfrence comme des ressources. Dans les fichiers de description XML,
vous pouvez les dsigner par @drawable/nomfic, o nomfic est le nom de base du fichier
(le nom de la ressource correspondant au fichier res/drawable/truc.png est donc
@drawable/truc). Dans le code Java, il suffit de prfixer le nom de base du fichier par
R.drawable lorsque vous avez besoin de lidentifiant de ressource (R.drawable.truc,
par exemple).
Si vous avez besoin dune URI vers une ressource image, vous pouvez utiliser deux formats
diffrents pour indiquer son chemin :

android.resource://com.example.app/id, o com.example.app est le nom du


paquetage Java utilis par lapplication dans AndroidManifest.xml et id est lidentifiant de ressource numrique de la ressource (R.drawable.truc, par exemple).

android.resource://com.example.app/raw/nom, o com.example.app est le nom


du paquetage Java utilis par lapplication dans AndroidManifest.xml et nom est le
nom de la ressource brute (tel truc pour res/drawable/truc.png).

Android est fourni avec quelques ressources images prdfinies qui sont accessibles en
Java via un prfixe android.R.drawable afin de les distinguer des ressources spcifiques
aux applications (android.R.drawable.picture_frame, par exemple).
Modifions lexemple prcdent pour quil utilise une icne la place de la ressource
chane du bouton. Ce projet, Resources/Image, ncessite dabord de modifier lgrement
le fichier de description afin dutiliser un ImageButton et de faire rfrence un Drawable
nomm @drawable/icon :
<?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"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<ImageButton android:id="@+id/format"
android:layout_width="wrap_content"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 206 Dimanche, 8. novembre 2009 12:23 12

206

Lart du dveloppement Android

android:layout_height="wrap_content"
android:src="@drawable/icon"
/>
<EditText android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
<TextView android:id="@+id/result"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>

Puis nous devons placer un fichier image dans res/drawable avec icon comme nom de
base. Ici, nous utiliserons un fichier PNG de 32 _ 32 pixels, issu de lensemble dicnes
Nuvola1. Enfin, nous modifions le code Java pour remplacer le Button par un ImageButton :
package com.commonsware.android.resources;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.EditText;
import android.widget.TextView;
public class ImagesDemo extends Activity {
EditText name;
TextView result;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
name=(EditText)findViewById(R.id.name);
result=(TextView)findViewById(R.id.result);
ImageButton btn=(ImageButton)findViewById(R.id.format);

1. http://en.wikipedia.org/wiki/Nuvola.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 207 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

207

btn.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
applyFormat();
}
});
}
private void applyFormat() {
String format=getString(R.string.funky_format);
String simpleResult=String.format(format,
TextUtils.htmlEncode(name.getText().toString()));
result.setText(Html.fromHtml(simpleResult));
}
}

Notre bouton contient dsormais licne dsire (voir Figure 19.3).


Figure 19.3
Lapplication
ImagesDemo.

Les ressources XML


Au Chapitre 18, nous avons vu que nous pouvions intgrer au paquetage de lapplication
des fichiers XML sous forme de ressources brutes : il fallait ensuite y accder depuis le
code Java pour les analyser et les utiliser. Il existe un autre moyen dempaqueter des
documents XML statiques avec une application : les ressources XML.
Il suffit de placer un fichier XML dans res/xml/ pour pouvoir y accder en appelant la
mthode getXml() sur un objet Resources et en lui passant en paramtre un identifiant

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 208 Dimanche, 8. novembre 2009 12:23 12

208

Lart du dveloppement Android

prfix par R.xml, suivi du nom de base du fichier XML. Si une activit est fournie avec
un fichier words.xml, par exemple, vous pouvez appeler getResources().getXml(R.xml.words) afin dobtenir une instance de la classe XmlPullParser,
qui se trouve dans lespace de noms org.xmlpull.v1. lheure o ce livre est crit, cette
classe nest pas documente, mais vous trouverez la documentation de cette bibliothque
sur son site web1.
Un analyseur pull XML est pilot par les vnements : il suffit dappeler sa mthode
next() pour obtenir lvnement suivant, qui peut tre START_TAG, END_TAG,
END_DOCUMENT, etc. Sur un vnement START_TAG, vous pouvez accder au nom du
marqueur et ses attributs ; un unique vnement TEXT reprsente la concatnation de tous
les nuds qui sont les fils directs de cet lment. En itrant, en testant et en invoquant du
code lment par lment, vous finissez par analyser tout le fichier.
Rcrivons le projet Files/Static pour quil utilise une ressource XML. Ce nouveau
projet Resources/XML exige que vous placiez le fichier words.xml dans res/xml et
non plus dans res/raw. Le layout restant identique, il suffit de modifier le code
source :
package com.commonsware.android.resources;
import android.app.Activity;
import android.os.Bundle;
import android.app.ListActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.InputStream;
import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class XMLResourceDemo extends ListActivity {
TextView selection;
ArrayList<String> items=new ArrayList<String>();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
try {

1. http://www.xmlpull.org/v1/doc/api/org/xmlpull/v1/package-summary.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 209 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

209

XmlPullParser xpp=getResources().getXml(R.xml.words);
while (xpp.getEventType()!=XmlPullParser.END_DOCUMENT) {
if (xpp.getEventType()==XmlPullParser.START_TAG) {
if (xpp.getName().equals("word")) {
items.add(xpp.getAttributeValue(0));
}
}
xpp.next();
}
}
catch (Throwable t) {
Toast
.makeText(this, "Echec de la requete : "+ t.toString(), 4000)
.show();
}
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items));
}
public void onListItemClick(ListView parent, View v, int position,
long id) {
selection.setText(items.get(position).toString());
}
}

Dsormais, dans notre bloc try...catch, nous obtenons un XmlPullParser et nous


bouclons jusqu la fin du document. Si lvnement courant est START_TAG et que le nom
de llment soit word (xpp.getName(). equals("word")), nous rcuprons le seul et
unique attribut et nous lajoutons la liste de mots de notre widget. Comme nous avons un
contrle total sur le fichier XML (puisque cest nous qui lavons cr), nous pouvons
supposer quil ny a exactement quun attribut si nous nen tions pas srs, nous pourrions compter le nombre dattributs avec getAttributeCount() et obtenir leur nom avec
getAttributeName() au lieu de supposer aveuglment que lattribut dindice 0 est celui
auquel on pense.
Comme le montre la Figure 19.4, mis part le nom dans la barre de titre, le rsultat est
identique lexemple du Chapitre 18.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 210 Dimanche, 8. novembre 2009 12:23 12

210

Lart du dveloppement Android

Figure 19.4
Lapplication
XMLResourceDemo.

Valeurs diverses
Dans le rpertoire res/values/, vous pouvez placer un ou plusieurs fichiers XML dcrivant des ressources simples : des dimensions, des couleurs et des tableaux. Dans les exemples prcdents, nous avons dj vu des utilisations des dimensions et des couleurs, qui
taient passes sous forme de chanes simples aux appels de mthodes ("10px", par exemple). Vous pouvez, bien sr, configurer ces valeurs comme des objets Java constants et
utiliser leurs noms symboliques, mais cela ne peut fonctionner que dans le code source
Java, pas dans les fichiers de description XML. En les plaant dans des fichiers de
ressources XML, vous pouvez rfrencer ces valeurs la fois dans du code Java et dans
les fichiers de description ; en outre, elles sont ainsi centralises au mme endroit, ce qui
facilite leur maintenance.
Les fichiers ressources XML ont pour racine llment resources ; tous les autres
lments sont des fils de cette racine.

Dimensions
Android utilise les dimensions en de nombreux endroits pour dcrire des distances, comme
la valeur de remplissage dun widget. Bien que nous utilisions souvent le pixel comme unit
de mesure (10px pour 10 pixels, par exemple), vous pouvez en choisir dautres :

in et mm indiquent, respectivement, des pouces et des millimtres, daprs la taille


physique de lcran.

pt reprsente des points, cest--dire 1/72e de pouce en termes typographiques, l aussi


daprs la taille physique de lcran.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 211 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

211

dp (device-independant pixel) et sp (scale-independant pixel) indiquent des pixels


indpendants du terminal un pixel est gal un dp pour un cran de 160 dpi, le
facteur dchelle reposant sur la densit de lcran (les pixels indpendants de lchelle
tiennent galement compte de la taille de la police choisie par lutilisateur).

Pour encoder une dimension comme une ressource, ajoutez un lment dimen ayant un
attribut name qui nomme de faon unique cette ressource. Le contenu de cet lment est
un texte reprsentant la valeur de la dimension :
<resources>
<dimen name="fin">10px</dimen>
<dimen name="epais">1in</dimen>
</resources>

Dans un fichier de description, les dimensions peuvent tre rfrences par @dimen/nom,
o nom est le nom unique de la ressource (fin ou epais, dans lexemple prcdent). Dans
le code Java, il suffit dutiliser le nom unique prfix par R.dimen (Resources.getDimen
(R.dimen.fin), par exemple).

Couleurs
Les couleurs Android sexpriment en valeurs RGB hexadcimales et peuvent prciser un
canal alpha. Vous avez le choix entre des valeurs hexadcimales dun seul caractre ou de
deux caractres, ce qui donne donc quatre formats possibles :

#RGB ;

#ARGB ;

#RRGGBB ;

#AARRGGBB.

Ces valeurs sont les mmes que dans les feuilles de style CSS.
Vous pouvez, bien sr, les placer comme des chanes de caractres dans le code Java ou les
fichiers de description. Cependant, il suffit dajouter des lments color au fichier de
ressource afin de les grer comme des ressources. Ces lments doivent possder un attribut
name pour donner un nom unique la couleur et contenir la valeur RGB :
<resources>
<color name="jaune_orange">#FFD555</color>
<color name="vert_foret">#005500</color>
<color name="ambre_fonce">#8A3324</color>
</resources>

Dans un fichier de description, ces couleurs peuvent tre dsignes par @color/nom, o
nom est le nom unique de la couleur (ambre_fonce, par exemple). En Java, prfixez ce
nom unique par R.color (Resources.getColor(R.color.vert_foret), par exemple).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 212 Dimanche, 8. novembre 2009 12:23 12

212

Lart du dveloppement Android

Tableaux
Les ressources tableaux sont conues pour contenir des listes de chanes simples, comme
une liste de civilits (M., Mme, Mlle, Dr, etc.).
Dans le fichier ressource, vous avez besoin dun lment string-array ayant un attribut
name pour donner un nom unique au tableau. Cet lment doit avoir autant de fils item
quil y a dlments dans ce tableau. Le contenu de chacun de ces fils est la valeur de
lentre correspondante :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="villes">
<item>Philadelphia</item>
<item>Pittsburgh</item>
<item>Allentown/Bethlehem</item>
<item>Erie</item>
<item>Reading</item>
<item>Scranton</item>
<item>Lancaster</item>
<item>Altoona</item>
<item>Harrisburg</item>
</string-array>
<string-array name="codes_aeroports">
<item>PHL</item>
<item>PIT</item>
<item>ABE</item>
<item>ERI</item>
<item>RDG</item>
<item>AVP</item>
<item>LNS</item>
<item>AOO</item>
<item>MDT</item>
</string-array>
</resources>

Dans le code Java, vous pouvez ensuite utiliser Resources.getStringArray(R.array.nom)


pour obtenir un String[] contenant tous les lments du tableau nom (Resources.getStringArray(R.array.codes_aeroports), par exemple).

Grer la diffrence
Un mme ensemble de ressources peut ne pas convenir toutes les situations dans lesquelles votre application est utilise. Un exemple vident est celui des ressources chanes et la
gestion de linternationalisation (I18N) et de la localisation (L10N). Nutiliser que des
chanes dune mme langue fonctionne parfaitement au moins pour le dveloppeur
mais cela produit une application qui rduit le nombre de ses utilisateurs potentiels.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 213 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

213

Les ressources peuvent galement diffrer sur dautres points :

Lorientation de lcran. Lcran est-il en mode portrait (vertical) ou en mode


paysage (horizontal) ? Il peut galement tre carr, ce qui signifie quil na donc pas
dorientation particulire.

La taille de lcran. Combien a-t-il de pixels ? Vous devez en tenir compte pour accorder en consquence la taille de vos ressources (petites ou grandes icnes, par exemple).

cran tactile. Si votre cran est tactile, est-il manipulable avec un stylet ou avec le
doigt ?

Clavier. De quel clavier dispose lutilisateur (alphanumrique, numrique, les deux) ?


Est-il toujours disponible ou est-ce une option ?

Autres dispositifs de saisie. Le terminal a-t-il dautres moyens de saisie, comme un


pad directionnel ou une molette ?

Pour grer toutes ces diffrences, Android utilise plusieurs rpertoires de ressources dont
les noms contiennent le critre concern.
Supposons, par exemple, que vous vouliez fournir des textes anglais et franais. Pour une
application non traduite, vous placeriez normalement ces chanes dans le fichier res/
values/strings.xml. Pour pouvoir reconnatre la fois langlais et le franais, vous
devrez crer deux rpertoires, res/values-en et res/values-fr, o les deux lettres qui
suivent le tiret reprsentent la langue souhaite selon le codage ISO-639-1 1. Les chanes
anglaises seraient donc places dans le fichier res/values-en/strings.xml et les franaises, dans res/values-fr/strings.xml. Android choisira alors le bon fichier en fonction
de la configuration du terminal de lutilisateur.
Cela semble simple, nest-ce pas ?
Les choses se compliquent lorsque vous avez besoin de plusieurs critres distincts pour
vos ressources. Supposons, par exemple, que vous vouliez dvelopper une application la
fois pour le G1 de T-Mobile et deux autres terminaux fictifs. Lun deux (le Fictif Un)
dispose dun cran VGA gnralement en mode paysage (640 _ 480), dun clavier alphanumrique toujours ouvert, dun pad directionnel, mais pas dcran tactile. Lautre (le
Fictif Deux) a le mme cran que le G1 (320 _ 480), un clavier numrique mais pas alphabtique, un pad directionnel mais pas dcran tactile. Pour tirer parti de ces diffrences
dcrans et doptions de saisie, vous pourriez crer des fichiers de description diffrents :

pour chaque combinaison de rsolution et dorientation ;

pour les terminaux qui ont un cran tactile et ceux qui nen ont pas ;

pour les terminaux qui ont des claviers alphanumriques et ceux qui nen ont pas.

Dans ces situations, toutes sortes de rgles entrent en jeu :


1. http://fr.wikipedia.org/wiki/Liste_des_codes_ISO_639-1.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 214 Dimanche, 8. novembre 2009 12:23 12

214

Lart du dveloppement Android

Les options de configuration (-en, par exemple) ont une certaine priorit et doivent
apparatre dans cet ordre dans le nom du rpertoire. La documentation dAndroid 1
dcrit lordre prcis dans lequel ces options peuvent apparatre. Pour les besoins de
notre exemple, lorientation de lcran doit prcder le type de lcran (tactile ou non),
qui doit lui-mme prcder sa taille.

Il ne peut exister quune seule valeur par rpertoire pour chaque catgorie doption de
configuration.

Les options sont sensibles la casse.

Pour notre scnario, nous aurions donc besoin, en thorie, des rpertoires suivants :

res/layout-port-notouch-qwerty-640x480 ;
res/layout-port-notouch-qwerty-480x320 ;
res/layout-port-notouch-12key-640x480 ;
res/layout-port-notouch-12key-480x320 ;
res/layout-port-notouch-nokeys-640x480 ;
res/layout-port-notouch-nokeys-480x320 ;
res/layout-port-stylus-qwerty-640x480 ;
res/layout-port-stylus-qwerty-480x320 ;
res/layout-port-stylus-12key-640x480 ;
res/layout-port-stylus-12key-480x320 ;
res/layout-port-stylus-nokeys-640x480 ;
res/layout-port-stylus-nokeys-480x320 ;
res/layout-port-finger-qwerty-640x480 ;
res/layout-port-finger-qwerty-480x320 ;
res/layout-port-finger-12key-640x480 ;
res/layout-port-finger-12key-480x320 ;
res/layout-port-finger-nokeys-640x480 ;
res/layout-port-finger-nokeys-480x320 ;
res/layout-land-notouch-qwerty-640x480 ;
res/layout-land-notouch-qwerty-480x320 ;
res/layout-land-notouch-12key-640x480 ;
res/layout-land-notouch-12key-480x320 ;
res/layout-land-notouch-nokeys-640x480 ;
res/layout-land-notouch-nokeys-480x320 ;

1. http://code.google.com/android/devel/resources-i18n.html#AlternateResources.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 215 Dimanche, 8. novembre 2009 12:23 12

Chapitre 19

Utilisation des ressources

215

res/layout-land-stylus-qwerty-640x480 ;
res/layout-land-stylus-qwerty-480x320 ;
res/layout-land-stylus-12key-640x480 ;
res/layout-land-stylus-12key-480x320 ;
res/layout-land-stylus-nokeys-640x480 ;
res/layout-land-stylus-nokeys-480x320 ;
res/layout-land-finger-qwerty-640x480 ;
res/layout-land-finger-qwerty-480x320 ;
res/layout-land-finger-12key-640x480 ;
res/layout-land-finger-12key-480x320 ;
res/layout-land-finger-nokeys-640x480 ;
res/layout-land-finger-nokeys-480x320.
Pas de panique ! Nous allons abrger cette liste dans un petit moment !

En ralit, beaucoup de ces fichiers de description seront identiques. Par exemple, nous
voulons simplement que les descriptions des crans tactiles soient diffrentes de celles des
crans non tactiles. Cependant, comme nous ne pouvons pas combiner les deux types
dcrans, nous devrons thoriquement avoir des rpertoires distincts avec des contenus
identiques pour les crans manipulables avec le doigt et ceux manipulables avec un stylet.
Notez galement que rien nempche davoir un rpertoire avec un nom de base simple
(res/layout). En ralit, cest mme srement prfrable au cas o une nouvelle version
dAndroid introduirait dautres options de configuration que vous navez pas prises en
compte disposer dune description par dfaut permettra alors votre application de fonctionner sur ce nouveau terminal.
Nous pouvons maintenant "tricher" un peu en dcodant les rgles quutilise Android pour
dterminer le "bon" rpertoire des ressources parmi lensemble des candidats :
1. Premirement, Android limine les candidats invalides dans le contexte. Si la taille de
lcran du terminal est de 320 240, par exemple, les rpertoires 640x480 seront
limins des candidats possibles car ils font spcifiquement appel une autre taille.
2. Deuximement, Android compte le nombre de correspondances pour chaque rpertoire et ne conserve que les rpertoires qui en ont le plus.
3. Enfin, Android suit lordre de priorit des options en dautres termes, il parcourt le
nom du rpertoire de gauche droite.
Nous pouvons donc nous ramener aux configurations suivantes :

res/layout-port-notouch-qwerty-640x480 ;

res/layout-port-notouch-qwerty ;

res/layout-port-notouch-640x480 ;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 216 Dimanche, 8. novembre 2009 12:23 12

216

Lart du dveloppement Android

res/layout-port-notouch ;

res/layout-port-qwerty-640x480 ;

res/layout-port-qwerty ;

res/layout-port-640x480 ;

res/layout-port ;

res/layout-land-notouch-qwerty-640x480 ;

res/layout-land-notouch-qwerty ;

res/layout-land-notouch-640x480 ;

res/layout-land-notouch ;

res/layout-land-qwerty-640x480 ;

res/layout-land-qwerty ;

res/layout-land-640x480 ;

res/layout-land.

Ici, nous tirons parti du fait que les correspondances spcifiques ont priorit sur les valeurs
"non spcifies". Ainsi, un terminal disposant dun clavier alphanumrique choisira une
ressource ayant qwerty dans son nom de rpertoire plutt quune ressource qui ne prcise
pas son type de clavier. Si lon combine cet tat de fait avec la rgle "le plus grand nombre
de correspondances lemporte", nous voyons que res/layout-port ne correspondra
quaux terminaux dots dcrans de 480 320 pixels, sans clavier alphanumrique et avec
un cran tactile orient en mode portrait.
Nous pourrions prciser tout cela encore un peu plus, pour ne couvrir que les terminaux
que nous visons (le G1 de HTC, Fictif Un et Fictif Deux) et en gardant res/layout
comme description par dfaut :

res/layout-port-notouch-640x480 ;

res/layout-port-notouch ;

res/layout-land-notouch-640x480 ;

res/layout-land-notouch ;

res/layout-land ;

res/layout.

Ici, 640x480 permet de diffrencier Fictif Un des deux autres, tandis que notouch distingue
Fictif Deux du G1 de HTC.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 217 Dimanche, 8. novembre 2009 12:23 12

20
Accs et gestion des bases
de donnes locales
SQLite1 est une base de donnes trs apprcie car elle fournit une interface SQL tout en
offrant une empreinte mmoire trs rduite et une rapidit de traitement satisfaisante. En
outre, elle appartient au domaine public et tout le monde peut donc lutiliser. De nombreuses
socits (Adobe, Apple, Google, Sun, Symbian) et plusieurs projets open-source (Mozilla,
PHP, Python) fournissent dsormais des produits intgrant SQLite.
SQLite tant intgr au moteur dexcution dAndroid, toute application peut crer des
bases de donnes SQLite. Ce SGBD disposant dune interface SQL, son utilisation est
assez vidente pour quiconque a une exprience avec dautres SGBDR. Cependant, son
API native nest pas JDBC, qui, dailleurs, serait trop lourd pour les terminaux limits en
mmoire comme les tlphones. Par consquent, les programmeurs Android doivent
apprendre une nouvelle API mais, comme nous allons le voir, ce nest pas trs difficile.
Ce chapitre prsente les bases de lutilisation de SQLite dans le contexte du dveloppement Android. Il ne prtend absolument pas tre une prsentation exhaustive de ce
SGBDR : pour plus de renseignements et pour savoir comment lutiliser dans dautres
1. http://www.sqlite.org.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 218 Dimanche, 8. novembre 2009 12:23 12

218

Lart du dveloppement Android

environnements quAndroid, nous vous conseillons louvrage de Mike Owens, The Definitive Guide to SQLite1 (Apress, 2006).
Les activits accdant gnralement une base de donnes via un fournisseur de contenu
(content provider) ou un service, ce chapitre ne contient pas dexemple complet : vous
trouverez un exemple de fournisseur de contenu faisant appel une base de donnes au
Chapitre 28.

Prsentation rapide de SQLite


SQLite, comme son nom lindique, utilise un dialecte de SQL pour effectuer des requtes
(SELECT), des manipulations de donnes (INSERT, etc.) et des dfinitions de donnes
(CREATE TABLE, etc.). certains moments, il scarte du standard SQL-92, comme la
plupart des autres SGBDR, dailleurs. La bonne nouvelle est que SQLite est si efficace en
terme de mmoire que le moteur dexcution dAndroid peut linclure dans son intgralit : vous ntes donc pas oblig de vous contenter dun sous-ensemble de ses fonctionnalits pour gagner de la place.
La plus grosse diffrence avec les autres SGBDR concerne principalement le typage des
donnes. Tant que vous pouvez prciser les types des colonnes dans une instruction
CREATE TABLE et tant que SQLite les utilise comme indication, tout va pour le mieux.
Vous pouvez mettre les donnes que vous voulez dans les colonnes que vous souhaitez. Vous
voulez placer une chane dans une colonne INTEGER ? Pas de problme ! Et vice versa ?
Cela marche aussi ! Cest ce que SQLite appelle "typage manifeste" ; il est dcrit de la
faon suivante dans sa documentation2 :
Avec le typage manifeste, le type dune donne est une proprit de la valeur elle-mme,
pas de la colonne dans laquelle la valeur est stocke. SQLite permet donc de stocker une
valeur de nimporte quel type dans nimporte quelle colonne, quel que soit le type dclar
de cette colonne.
Certaines fonctionnalits standard de SQL ne sont pas reconnues par SQLite, notamment
les contraintes FOREIGN KEY, les transactions imbriques, RIGHT OUTER JOIN,
FULL OUTER JOIN et certaines variantes de ALTER TABLE.
Ces remarques mises part, vous disposez dun SGBDR complet, avec des triggers, des
transactions, etc. Les instructions SQL de base, comme SELECT, fonctionnent exactement
comme vous tes en droit de lattendre. Si vous tes habitu travailler avec un gros SGBDR
comme Oracle, vous pourriez considrer que SQLite est un "jouet", mais noubliez pas
que ces deux systmes ont t conus pour rsoudre des problmes diffrents et que vous
ntes pas prs de voir une installation complte dOracle sur un tlphone.
1. http://www.amazon.com/Definitive-Guide-SQLite/dp/1590596730.
2. http://www.sqlite.org/different.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 219 Dimanche, 8. novembre 2009 12:23 12

Chapitre 20

Accs et gestion des bases de donnes locales

219

Commencer par le dbut


Android ne fournit aucune base de donnes de son propre chef. Si vous voulez utiliser
SQLite, vous devez crer votre propre base, puis la remplir avec vos tables, vos index et
vos donnes.
Pour crer et ouvrir une base de donnes, la meilleure solution consiste crer une sousclasse de SQLiteOpenHelper. Cette classe enveloppe tout ce qui est ncessaire la cration et la mise jour dune base, selon vos spcifications et les besoins de votre application.
Cette sous-classe aura besoin de trois mthodes :

Un constructeur qui appelle celui de sa classe parente et qui prend en paramtre le


Context (une Activity), le nom de la base de donnes, une ventuelle fabrique de
curseur (le plus souvent, ce paramtre vaudra null) et un entier reprsentant la version
du schma de la base.

onCreate(), laquelle vous passerez lobjet SQLiteDatabase que vous devrez


remplir avec les tables et les donnes initiales que vous souhaitez.

onUpgrade(), laquelle vous passerez un objet SQLiteDatabase ainsi que lancien et


le nouveau numro de version. Pour convertir une base dun ancien schma un
nouveau, lapproche la plus simple consiste supprimer les anciennes tables et en
crer de nouvelles. Le Chapitre 28 donnera tous les dtails ncessaires.

Le reste de ce chapitre est consacr la cration et la suppression des tables, linsertion


des donnes, etc. Il prsentera galement un exemple de sous-classe de SQLiteOpenHelper.
Pour utiliser votre sous-classe, crez une instance et demandez-lui dappeler getReadableDatabase() ou getWriteableDatabase() selon que vous vouliez ou non modifier son
contenu :
db=(new DatabaseHelper(getContext())).getWritableDatabase();
return (db == null)? false: true;

Cet appel renverra une instance de SQLiteDatabase qui vous servira ensuite interroger
ou modifier la base de donnes.
Lorsque vous avez fini de travailler sur cette base (lorsque lactivit est ferme, par exemple), il suffit dappeler la mthode close() de cette instance pour librer votre connexion.

Mettre la table
Pour crer des tables et des index, vous devez appeler la mthode execSQL() de lobjet
SQLiteDatabase en lui passant linstruction du LDD (langage de dfinition des donnes)
que vous voulez excuter. En cas derreur, cette mthode renvoie null.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 220 Dimanche, 8. novembre 2009 12:23 12

220

Lart du dveloppement Android

Vous pouvez, par exemple, utiliser le code suivant :


db.execSQL("CREATE TABLE
constantes (_id INTEGER PRIMARY KEY AUTOINCREMENT,
titre TEXT, valeur REAL);");

Cet appel cre une table constantes avec une colonne de cl primaire _id qui est un
entier incrment automatiquement (SQLite lui affectera une valeur pour vous lorsque
vous insrerez les lignes). Cette table contient galement deux colonnes de donnes :
titre (un texte) et valeur (un nombre rel). SQLite crera automatiquement un index sur
la colonne de cl primaire si vous le souhaitez, vous pouvez en ajouter dautres laide
dinstructions CREATE INDEX.
Le plus souvent, vous crerez les tables et les index ds la cration de la base de donnes
ou, ventuellement, lorsquelle devra tre mise jour suite une nouvelle version de votre
application. Si les schmas des tables ne changent pas, les tables et les index nont pas
besoin dtre supprims mais, si vous devez le faire, il suffit dutiliser execSQL() afin
dexcuter les instructions DROP INDEX et DROP TABLE.

Ajouter des donnes


Lorsque lon cre une base de donnes et une ou plusieurs tables, cest gnralement pour
y placer des donnes. Pour ce faire, il existe principalement deux approches.
Vous pouvez encore utiliser execSQL(), comme vous lavez fait pour crer les tables.
Cette mthode permet en effet dexcuter nimporte quelle instruction SQL qui ne renvoie
pas de rsultat, ce qui est le cas dINSERT, UPDATE, DELETE, etc. Vous pourriez donc
utiliser ce code :
db.execSQL("INSERT INTO widgets (name, inventory)"+
"VALUES (Sprocket, 5)");

Une autre solution consiste utiliser insert(), update() et delete() sur lobjet SQLiteDatabase. Ces mthodes utilisent des objets ContentValues qui implmentent une interface ressemblant Map mais avec des mthodes supplmentaires pour prendre en compte
les types de SQLite : outre get(), qui permet de rcuprer une valeur par sa cl, vous
disposez galement de getAsInteger(), getAsString(), etc.
La mthode insert() prend en paramtre le nom de la table, celui dune colonne pour
lastuce de la colonne nulle et un objet ContentValues contenant les valeurs que vous
voulez placer dans cette ligne. Lastuce de la colonne nulle est utilise dans le cas o
linstance de ContentValues est vide la colonne indique pour cette astuce recevra alors
explicitement la valeur NULL dans linstruction INSERT produite par insert().
ContentValues cv=new ContentValues();
cv.put(Constantes.TITRE, "Gravity, Death Star I");
cv.put(Constantes.VALEUR, SensorManager.GRAVITY_DEATH_STAR_I);
db.insert("constantes", getNullColumnHack(), cv);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 221 Dimanche, 8. novembre 2009 12:23 12

Chapitre 20

Accs et gestion des bases de donnes locales

221

La mthode update() prend en paramtre le nom de la table, un objet ContentValues


contenant les colonnes et leurs nouvelles valeurs et, ventuellement, une clause WHERE
et une liste de paramtres qui remplaceront les marqueurs prsents dans celle-ci. update()
nautorisant que des valeurs fixes pour mettre jour les colonnes, vous devrez utiliser
execSQL() si vous souhaitez affecter des rsultats calculs.
La clause WHERE et la liste de paramtres fonctionnent comme les paramtres positionnels
qui existent galement dans dautres API de SQL :
// remplacements est une instance de ContentValues
String[] params=new String[] {"snicklefritz"};
db.update("widgets", remplacements, "name=?", params);

La mthode delete() fonctionne comme update() et prend en paramtre le nom de la


table et, ventuellement, une clause WHERE et une liste des paramtres positionnels pour
cette clause.

Le retour de vos requtes


Comme pour INSERT, UPDATE et DELETE, vous pouvez utiliser plusieurs approches
pour rcuprer les donnes dune base SQLite avec SELECT :

rawQuery() permet dexcuter directement une instruction SELECT.

query() permet de construire une requte partir de ses diffrentes composantes.

Un sujet de confusion classique est la classe SQLiteQueryBuilder et le problme des


curseurs et de leurs fabriques.

Requtes brutes
La solution la plus simple, au moins du point de vue de lAPI, consiste utiliser
rawQuery() en lui passant simplement la requte SELECT. Cette dernire peut contenir
des paramtres positionnels qui seront remplacs par les lments du tableau pass en
second paramtre. Voici un exemple :
Cursor c=db.rawQuery("SELECT name FROM sqlite_master
WHERE type=table
AND name=constantes", null);

Ici, nous interrogeons une table systme de SQLite (sqlite_master) pour savoir si la
table constantes existe dj. La valeur renvoye est un Cursor qui dispose de mthodes
permettant de parcourir le rsultat (voir la section "Utilisation des curseurs").
Si vos requtes sont bien intgres votre application, cest une approche trs simple. En
revanche, elle se complique lorsquune requte comprend des parties dynamiques que les
paramtres positionnels ne peuvent plus grer. Si lensemble de colonnes que vous voulez

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 222 Dimanche, 8. novembre 2009 12:23 12

222

Lart du dveloppement Android

rcuprer nest pas connu au moment de la compilation, par exemple, concatner les noms
des colonnes pour former une liste dlimite par des virgules peut tre ennuyeux cest l
que query() entre en jeu.

Requtes normales
La mthode query() prend en paramtre les parties dune instruction SELECT afin de
construire la requte. Ces diffrentes composantes apparaissent dans lordre suivant dans
la liste des paramtres :
1. Le nom de la table interroge.
2. La liste des colonnes rcuprer.
3. La clause WHERE, qui peut contenir des paramtres positionnels.
4. La liste des valeurs substituer ces paramtres positionnels.
5. Une ventuelle clause GROUP BY.
6. Une ventuelle clause ORDER BY.
7. Une ventuelle clause HAVING.
part le nom de la table, ces paramtres peuvent valoir null lorsquils ne sont pas
ncessaires :
String[] colonnes={"ID", "inventory"};
String[] params={"snicklefritz"};
Cursor result=db.query("widgets", colonnes, "name=?",
params, null, null, null);

Utilisation des "builders"


Une autre possibilit consiste utiliser SQLiteQueryBuilder, qui offre bien plus de possibilits pour construire les requtes complexes, notamment celles qui impliquent dunir les
rsultats de plusieurs sous-requtes, par exemple. En outre, linterface SQLiteQueryBuilder
saccorde parfaitement avec linterface ContentProvider pour excuter les requtes.
Un patron de conception classique pour limplmentation de la mthode query() de votre
fournisseur de contenu consiste donc crer un objet SQLiteQueryBuilder, lui fournir
certaines valeurs par dfaut, puis lui faire construire (et, ventuellement, excuter) la
requte complte en combinant ces valeurs par dfaut avec celles qui ont t passes au
fournisseur de contenu lors de la demande de requte.
Voici, par exemple, un extrait de code dun fournisseur de contenu utilisant SQLiteQueryBuilder :
@Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 223 Dimanche, 8. novembre 2009 12:23 12

Chapitre 20

Accs et gestion des bases de donnes locales

223

SQLiteQueryBuilder qb=new SQLiteQueryBuilder();


qb.setTables(getTableName());
if (isCollectionUri(url)) {
qb.setProjectionMap(getDefaultProjection());
}
else {
qb.appendWhere(getIdColumnName()+"="+url.getPathSegments().get(1));
}
String orderBy;
if (TextUtils.isEmpty(sort)) {
orderBy=getDefaultSortOrder();
} else {
orderBy=sort;
}
Cursor c=qb.query(db, projection, selection, selectionArgs,
null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), url);
return c;
}

Les fournisseurs de contenu (content provider) seront expliqus en dtail dans la


cinquime partie de ce livre. Ici, nous pouvons nous contenter de remarquer que :
1. Nous construisons un objet SQLiteQueryBuilder.
2. Nous lui indiquons la table concerne par la requte avec setTables(getTableName()).
3. Soit nous lui indiquons lensemble de colonnes renvoyer par dfaut (avec setProjectionMap()), soit nous lui donnons une partie de clause WHERE afin didentifier
une ligne prcise de la table partir dun identifiant extrait de lURI fournie lappel
de query() (avec appendWhere()).
4. Enfin, nous lui demandons dexcuter la requte en mlangeant les valeurs de dpart
avec celles fournies query() (qb.query(db, projection, selection, selectionArgs, null, null, orderBy)).
Au lieu de faire excuter directement la requte par lobjet SQLiteQueryBuilder, nous
aurions pu appeler buildQuery() pour la produire et renvoyer linstruction SELECT dont
nous avions besoin ; nous aurions alors pu lexcuter nous-mmes.

Utilisation des curseurs


Quelle que soit la faon dont vous excutez la requte, vous obtiendrez un Cursor en retour.
Il sagit de la version Android/SQLite des curseurs de bases de donnes, un concept utilis par
de nombreux SGBDR. Avec ce curseur, vous pouvez effectuer les oprations suivantes :

connatre le nombre de lignes du rsultat grce getCount() ;

parcourir les lignes du rsultat avec moveToFirst(), moveToNext() et isAfterLast() ;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 224 Dimanche, 8. novembre 2009 12:23 12

224

Lart du dveloppement Android

connatre les noms des colonnes avec getColumnNames(), les convertir en numros de
colonnes grce getColumnIndex() et obtenir la valeur dune colonne donne de la
ligne courante via des mthodes comme getString(), getInt(), etc. ;

excuter nouveau la requte qui a cr le curseur, avec requery() ;

librer les ressources occupes par le curseur avec close().

Voici, par exemple, comment parcourir les entres de la table widgets rencontre dans les
extraits prcdents :
Cursor result=
db.rawQuery("SELECT ID, name, inventory FROM widgets");
result.moveToFirst();
while (!result.isAfterLast()) {
int id=result.getInt(0);
String name=result.getString(1);
int inventory=result.getInt(2);
// Faire quelque chose de ces valeurs...
result.moveToNext();
}
result.close();

Crer ses propres curseurs


Dans certains cas, vous pouvez vouloir utiliser votre propre sous-classe de Cursor plutt
que limplmentation de base fournie par Android. Dans ces situations, vous pouvez vous
servir des mthodes queryWithFactory() et rawQueryWithFactory(), qui prennent
toutes les deux en paramtre une instance de SQLiteDatabase.CursorFactory. Cette
fabrique, comme lon pourrait sy attendre, est responsable de la cration de nouveaux
curseurs via son implmentation de newCursor().
Limplmentation et lutilisation de cette approche sont laisses en exercice au lecteur. En
fait, vous ne devriez pas avoir besoin de crer vos propres classes de curseur au cours du
dveloppement dune application Android classique.

Des donnes, des donnes, encore des donnes


Si vous avez lhabitude de dvelopper avec dautres SGBDR, vous avez srement aussi utilis
des outils permettant dinspecter et de manipuler le contenu de la base de donnes et qui vont
au-del de lAPI. Avec lmulateur dAndroid, vous avez galement deux possibilits.
Premirement, lmulateur est cens fournir le programme sqlite3, accessible via la
commande adb shell. Lorsque vous avez lanc cette commande, tapez simplement sqlite3
suivi du chemin vers le fichier de votre base de donnes, qui est gnralement de la forme :
/data/data/votre.paquetage.app/databases/nom_base

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 225 Dimanche, 8. novembre 2009 12:23 12

Chapitre 20

Accs et gestion des bases de donnes locales

225

Ici, votre.paquetage.app est le paquetage Java de lapplication (com.commonsware.android, par exemple) et nom_base est le nom de la base de donnes, tel quil est
fourni createDatabase().
Le programme sqlite3 fonctionne bien et, si vous avez lhabitude de manipuler vos
tables partir de la console, il vous sera trs utile. Si vous prfrez disposer dune interface un peu plus conviviale, vous pouvez copier la base de donnes SQLite du terminal sur
votre machine de dveloppement, puis utiliser un client graphique pour SQLite. Cependant, noubliez pas que vous travaillez alors sur une copie de la base : si vous voulez rpercuter les modifications sur le terminal, vous devrez retransfrer cette base sur celui-ci.
Pour rcuprer la base du terminal, utilisez la commande adb pull (ou son quivalent
dans votre environnement de dveloppement) en lui fournissant le chemin de la base sur le
terminal et celui de la destination sur votre machine. Pour stocker une base de donnes
modifie sur le terminal, utilisez la commande adb push en lui indiquant le chemin de
cette base sur votre machine et le chemin de destination sur le terminal.
Lextension SQLite Manager1 pour Firefox est lun des clients SQLite les plus accessibles
(voir Figure 20.1), car elle est disponible sur toutes les plates-formes.

Figure 20.1
Lextension SQLite Manager de Firefox.

Vous trouverez galement dautres clients2 sur le site web de SQLite3.


1. https://addons.mozilla.org/en-US/firefox/addon/5817.
2. http://www.sqlite.org/cvstrac/wiki?p=SqliteTools.
3. http://www.sqlite.org.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 226 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 227 Dimanche, 8. novembre 2009 12:23 12

21
Tirer le meilleur parti
des bibliothques Java
Java a autant de bibliothques tierces que les autres langages de programmation modernes,
si ce nest plus. Quand nous parlons de "bibliothques tierces", nous faisons rfrence ici
aux innombrables JAR que vous pouvez inclure dans une application Java, quelle quelle
soit : cela concerne tout ce que le SDK Java ne fournit pas lui-mme.
Dans le cas dAndroid, le cur de la machine virtuelle Dalvik nest pas exactement Java,
et ce que fournit son SDK nest pas la mme chose quun SDK Java traditionnel. Ceci
tant dit, de nombreuses bibliothques Java fournissent les fonctionnalits dont ne dispose
pas Android et peuvent donc vous tre utiles.
Ce chapitre explique ce quil faut faire pour tirer parti de ces bibliothques et dcrit les
limites de lintgration du code tiers une application Android.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 228 Dimanche, 8. novembre 2009 12:23 12

228

Lart du dveloppement Android

Limites extrieures
Tout le code Java existant ne fonctionne videmment pas avec Android. Un certain nombre
de facteurs doivent tre pris en compte :

API pour la plate-forme. Est-ce que le code suppose que vous utilisez une JVM plus
rcente que celle sur laquelle repose Android ou suppose-t-il lexistence dune API
Java fournie avec J2SE mais qui nexiste pas dans Android, comme Swing ?

Taille. Le code Java conu pour tre utilis sur les machines de bureau ou les serveurs
ne se soucie pas beaucoup de lespace disque ni de la taille mmoire. Android, videmment, manque des deux. Lutilisation de code tiers, notamment lorsquil est empaquet
sous forme de JAR, peut faire gonfler la taille de votre application..

Performances. Est-ce que le code Java suppose un CPU beaucoup plus puissant que
ceux que vous pouvez trouver sur la plupart des terminaux Android ? Ce nest pas
parce quun ordinateur de bureau peut lexcuter sans problme quun tlphone
mobile moyen pourra faire de mme.

Interface. Est-ce que le code Java suppose une interface en mode console, ou sagit-il
dune API que vous pouvez envelopper dans votre propre interface ?

Une astuce pour rgler quelques-uns de ces problmes consiste utiliser du code Java
open-source et modifier ce code pour ladapter Android. Si, par exemple, vous nutilisez que 10 % dune bibliothque tierce, il est peut-tre plus intressant de recompiler ce
sous-ensemble ou, au moins, dter les classes inutilises du JAR. La premire approche
est plus sre dans la mesure o le compilateur vous garantit que vous ne supprimerez pas
une partie essentielle du code, mais elle peut tre assez dlicate.

Ant et JAR
Vous avez deux possibilits pour intgrer du code tiers dans votre projet : utiliser du code
source ou des JAR dj compils.
Si vous choisissez la premire mthode, il suffit de copier le code source dans larborescence de votre projet (sous le rpertoire src/) afin quil soit plac ct de votre propre
code, puis de laisser le compilateur faire son travail.
Si vous choisissez dutiliser un JAR dont vous ne possdez peut-tre pas les sources, vous
devrez expliquer votre chane de dveloppement comment lutiliser. Avec un IDE, il
suffit de lui donner la rfrence du JAR. Si, en revanche, vous utilisez le script Ant
build.xml, vous devez placer le fichier JAR dans le rpertoire libs/ cr par activityCreator, o le processus de construction dAnt ira le chercher.
Dans une dition prcdente de ce livre, par exemple, nous prsentions un projet MailBuzz
qui, comme son nom lindique, traitait du courrier lectronique. Ce projet utilisait les API

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 229 Dimanche, 8. novembre 2009 12:23 12

Chapitre 21

Tirer le meilleur parti des bibliothques Java

229

JavaMail et avait besoin de deux JAR JavaMail : mail-1.4.jar et activation-1.1.jar.


Avec ces deux fichiers dans le rpertoire libs/, le classpath demandait javac deffectuer une dition de liens avec ces JAR afin que toutes les rfrences JavaMail dans le
code de MailBuzz puissent tre correctement rsolues. Puis le contenu de ces JAR
tait numr avec les classes compiles de MailBuzz lors de la conversion en instructions Dalvik laide de loutil dex. Sans cette tape, le code se serait peut-tre
compil, mais il naurait pas trouv les classes JavaMail lexcution, ce qui aurait provoqu
une exception.
Cependant, la machine virtuelle Dalvik et le compilateur fournis avec Android 0.9 et les
SDK plus rcents ne supportent plus certaines fonctionnalits du langage Java utilises par
JavaMail et, bien que le code source de JavaMail soit disponible, sa licence open-source
(Common Development and Distribution licence CDDL) pose certains problmes.

Suivre le script
la diffrence des autres systmes pour terminaux mobiles, Android nimpose aucune
restriction sur ce qui peut sexcuter tant que cest du Java qui utilise la machine virtuelle
Dalvik. Vous pouvez donc incorporer votre propre langage de script dans votre application, ce
qui est expressment interdit sur dautres terminaux.
BeanShell1 est lun de ces langages de script Java. Il offre une syntaxe compatible Java,
avec un typage implicite, et ne ncessite pas de compilation.
Pour ajouter BeanShell, vous devez placer le fichier JAR de linterprteur dans votre rpertoire libs/. Malheureusement, le JAR 2.0b4 disponible au tlchargement sur le site de
BeanShell ne fonctionne pas tel quel avec Android 0.9 et les SDK plus rcents, probablement cause du compilateur utilis pour le compiler. Il est donc prfrable de rcuprer
son code source laide de Subversion2, dexcuter ant jarcore pour le compiler et de
copier le JAR ainsi obtenu (dans le rpertoire dist/ de BeanShell) dans le rpertoire
libs/ de votre projet. Vous pouvez galement utiliser le JAR BeanShell accompagnant les
codes sources de ce livre (il se trouve dans le projet Java/AndShell). Ensuite, lutilisation
de BeanShell avec Android est identique son utilisation dans un autre environnement
Java :
1. On cre une instance de la classe Interpreter de BeanShell.
2. On configure les variables globales pour le script laide dInterpreter#set().
3. On appelle Interpreter#eval() pour lancer le script et, ventuellement, obtenir le
rsultat de la dernire instruction.
1. http://beanshell.org.
2. http://beanshell.org/developer.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 230 Dimanche, 8. novembre 2009 12:23 12

230

Lart du dveloppement Android

Voici par exemple le fichier de description XML du plus petit IDE BeanShell du monde :
<?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"
>
<Button
android:id="@+id/eval"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Go!"
/>
<EditText
android:id="@+id/script"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:singleLine="false"
android:gravity="top"
/>
</LinearLayout>

Voici limplmentation de lactivit :


package com.commonsware.android.andshell;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import bsh.Interpreter;
public class MainActivity extends Activity {
private Interpreter i=new Interpreter();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.eval);
final EditText script=(EditText)findViewById(R.id.script);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String src=script.getText().toString();
try {
i.set("context", MainActivity.this);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 231 Dimanche, 8. novembre 2009 12:23 12

Chapitre 21

Tirer le meilleur parti des bibliothques Java

231

i.eval(src);
}
catch (bsh.EvalError e) {
AlertDialog.Builder builder=
new AlertDialog.Builder(MainActivity.this);
builder
.setTitle("Exception!")
.setMessage(e.toString())
.setPositiveButton("OK", null)
.show();
}
}
});
}
}

Compilez ce projet (en incorporant le JAR de BeanShell comme on la mentionn plus


haut), puis installez-le sur lmulateur. Lorsque vous le lancerez, vous obtiendrez un IDE
trs simple, avec une grande zone de texte vous permettant de saisir votre script et un gros
bouton Go! pour lexcuter (voir Figure 21.1).
import android.widget.Toast;
Toast.makeText(context, "Hello, world!", 5000).show();

Figure 21.1
LIDE AndShell.

Notez lutilisation de context pour dsigner lactivit lors de la cration du toast.


Cette variable a t configure globalement par lactivit pour se dsigner elle-mme.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 232 Dimanche, 8. novembre 2009 12:23 12

232

Lart du dveloppement Android

Vous pourriez lappeler autrement : ce qui importe est que lappel set() et le code du
script utilisent le mme nom.
Lorsque vous cliquez sur le bouton Go!, vous obtenez le rsultat de la Figure 21.2.
Figure 21.2
LIDE AndShell excutant
un script BeanShell.

Ceci tant dit, il y a quelques prcautions prendre.


Premirement, tous les langages de script ne fonctionneront pas. Ceux qui implmentent
leur propre forme de compilation JIT (just-in-time) en produisant le pseudo-code Java
la vole , notamment, devront srement tre modifis pour produire du pseudo-code
Dalvik la place. Les langages plus simples, qui interprtent seulement les fichiers de
script en appelant les API dintrospection de Java pour se ramener des appels de classes
compiles, fonctionneront probablement mieux. Mme en ce cas, certaines fonctionnalits
du langage peuvent ne pas tre disponibles si elles reposent sur une caractristique de lAPI
Java traditionnelle qui nexiste pas avec Dalvik ce qui peut tre le cas du BeanShell ou
de certains JAR tiers avec les versions actuelles dAndroid.
Deuximement, les langages de script sans JIT seront forcment plus lents que des applications Dalvik compiles ; cette lenteur peut dplaire aux utilisateurs et impliquer plus de
consommation de batterie pour le mme travail. Construire une application Android en
BeanShell uniquement parce que vous trouvez quelle est plus facile crire peut donc
rendre vos utilisateurs assez mcontents.
Troisimement, les langages de script qui exposent toute lAPI Java, comme BeanShell,
peuvent raliser tout ce quautorise le modle de scurit sous-jacent dAndroid. Si votre
application dispose de la permission READ_CONTACTS, par exemple, tous les scripts
BeanShell quelle excutera lauront galement.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 233 Dimanche, 8. novembre 2009 12:23 12

Chapitre 21

Tirer le meilleur parti des bibliothques Java

233

Enfin, mais ce nest pas le moins important, les JAR des interprteurs ont tendance tre...
gros. Celui du BeanShell utilis ici fait 200 Ko, par exemple. Ce nest pas ridicule si lon
considre ce quil est capable de faire, mais cela implique que les applications qui utilisent
BeanShell seront bien plus longues tlcharger, quelles prendront plus de place sur le
terminal, etc.

Tout fonctionne... enfin, presque


Tous les codes Java ne fonctionneront pas avec Android et Dalvik. Vous devez plus prcisment tenir compte des paramtres suivants :

Si le code Java suppose quil sexcute avec Java SE, Java ME ou Java EE, il ne trouvera peut-tre pas certaines API quil a lhabitude de trouver sur ces plates-formes
mais qui ne sont pas disponibles avec Android. Certaines bibliothques de trac de
diagrammes supposent, par exemple, la prsence de primitives de trac Swing ou AWT
(Abstract Window Toolkit), qui sont gnralement absentes dAndroid.

Le code Java peut dpendre dun autre code Java qui, son tour, peut avoir des problmes pour sexcuter sur Android. Vous pourriez vouloir utiliser un JAR qui repose, par
exemple, sur une version de la classe HTTPComponents dApache plus ancienne (ou
plus rcente) que celle fournie avec Android.

Le code Java peut utiliser des fonctionnalits du langage que le moteur Dalvik ne
reconnat pas.

Dans toutes ces situations, vous pouvez ne rencontrer aucun problme lors de la compilation de votre application avec un JAR compil ; ces problmes surviendront plutt lors de
lexcution. Cest pour cette raison quil est prfrable dutiliser du code open-source avec
Android chaque fois que cela est possible : vous pourrez ainsi construire vous-mme le
code tiers en mme temps que le vtre et dtecter plus tt les difficults rsoudre.

Relecture des scripts


Ce chapitre tant consacr lcriture des scripts avec Android, vous apprcierez srement
de savoir quil existe dautres possibilits que lintgration directe de Beanshell dans votre
projet.
Certains essais ont t raliss avec dautres langages reposant sur la JVM, notamment
JRuby et Jython. Pour le moment, leur support dAndroid est incomplet, mais cette intgration progresse.
En outre, ASE (Android Scripting Environment), tlchargeable partir dAndroid
Market, permet dcrire des scripts en Python et Lua, et de les faire excuter par
BeanShell. Ces scripts ne sont pas des applications part entire et, lheure o ce livre
est crit, elles ne sont pas vraiment redistribuables. De plus, ASE na pas t rellement

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 234 Dimanche, 8. novembre 2009 12:23 12

234

Lart du dveloppement Android

conu pour tendre dautres applications, mme sil peut tre utilis de cette faon. Cependant, si vous voulez programmer directement sur le terminal, il est srement la meilleure
solution actuelle.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 235 Dimanche, 8. novembre 2009 12:23 12

22
Communiquer via Internet
On sattend gnralement ce que la plupart des terminaux Android, si ce nest tous, intgrent un accs Internet. Cet accs peut passer par le Wifi, les services de donnes cellulaires (EDGE, 3G, etc.) ou, ventuellement, un mcanisme totalement diffrent. Quoi quil
en soit, la plupart des gens en tout cas, ceux qui ont un accs donnes ou Wifi peuvent
accder Internet partir de leur tlphone Android.
Il nest donc pas tonnant quAndroid offre aux dveloppeurs un large ventail de moyens
leur permettant dexploiter cet accs. Ce dernier peut tre de haut niveau, comme le navigateur WebKit intgr que nous avons tudi au Chapitre 13. Mais il peut galement intervenir au niveau le plus bas et utiliser des sockets brutes. Entre ces deux extrmits, il
existe des API disponibles sur le terminal ou via des JAR tiers donnant accs des
protocoles spcifiques comme HTTP, XMPP, SMTP, etc.
Ce livre sintresse plutt aux accs de haut niveau comme le composant WebKit et les
API Internet car, dans la mesure du possible, les dveloppeurs devraient sefforcer de
rutiliser des composants existants au lieu de crer leurs propres protocoles.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 236 Dimanche, 8. novembre 2009 12:23 12

236

Lart du dveloppement Android

REST et relaxation
Bien quAndroid ne dispose pas dAPI cliente pour SOAP ou XML-RPC, il intgre la
bibliothque HttpComponents dApache. Vous pouvez donc soit ajouter une couche
SOAP/XML-RPC au-dessus de cette bibliothque, soit lutiliser directement pour accder aux
services web de type REST. Dans ce livre, nous considrerons les "services web REST"
comme de simples requtes HTTP classiques avec des rponses aux formats XML, JSON,
etc.
Vous trouverez des didacticiels plus complets, des FAQ et des HOWTO sur le site web de
HttpComponents1 : nous ne prsenterons ici que les bases en montrant comment consulter
les informations mtorologiques.

Oprations HTTP via HttpComponents


Le composant HttpClient de HttpComponents gre pour vous toutes les requtes HTTP.
La premire tape pour lutiliser consiste videmment crer un objet. HttpClient tant
une interface, vous devrez donc instancier une implmentation de celle-ci, comme
DefaultHttpClient.
Ces requtes sont enveloppes dans des instances de HttpRequest, chaque commande
HTTP tant gre par une implmentation diffrente de cette interface (HttpGet pour les
requtes GET, par exemple). On cre donc une instance dune implmentation de HttpRequest, on construit lURL rcuprer ainsi que les autres donnes de configuration (les
valeurs des formulaires si lon effectue une commande POST via HttpPost, par exemple) puis
lon passe la mthode au client pour quil effectue la requte HTTP en appelant execute().
Ce qui se passe ensuite peut tre trs simple ou trs compliqu. On peut obtenir un objet
HttpResponse enveloppant un code de rponse (200 pour OK, par exemple), des en-ttes
HTTP, etc. Mais on peut galement utiliser une variante dexecute() qui prend en paramtre un objet ResponseHandler<String> : cet appel renverra simplement une reprsentation String de la rponse. En pratique, cette approche est dconseille car il est
prfrable de vrifier les codes de rponses HTTP pour dtecter les erreurs. Cependant,
pour les applications triviales comme les exemples de ce livre, la technique ResponseHandler<String> convient parfaitement.
Le projet Internet/Weather, par exemple, implmente une activit qui rcupre les
donnes mtorologiques de votre emplacement actuel partir du site Google Weather.
Ces donnes sont converties en HTML puis passes un widget WebKit qui se charge de
les afficher. Nous laissons en exercice au lecteur la rcriture de ce programme pour quil
utilise un ListView. En outre, cet exemple tant relativement long, nous ne prsenterons
1. http://hc.apache.org/.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 237 Dimanche, 8. novembre 2009 12:23 12

Chapitre 22

Communiquer via Internet

237

ici que les extraits de code en rapport avec ce chapitre ; les sources complets sont disponibles
dans les exemples fournis avec ce livre1.
Pour rendre tout ceci un peu plus intressant, nous utilisons les services de localisation
dAndroid pour dterminer notre emplacement actuel. Les dtails de fonctionnement de ce
service sont dcrits au Chapitre 33.
Lorsquun emplacement a t trouv soit au lancement, soit parce que nous avons boug
, nous rcuprons les donnes de Google Weather via la mthode updateForecast() :
private void updateForecast(Location loc) {
String url = String.format(format, ""
+ (int) (loc.getLatitude() * 1000000), ""
+ (int) (loc.getLongitude() * 1000000));
HttpGet getMethod = new HttpGet(url);
try {
ResponseHandler<String> responseHandler =
new BasicResponseHandler();
String responseBody = client.execute(getMethod,
responseHandler);
buildForecasts(responseBody);
String page = generatePage();
browser.loadDataWithBaseURL(null, page, "text/html",
"UTF-8", null);
} catch (Throwable t) {
Toast.makeText(this, "La requete a echouee: " +
t.toString(), 4000).show();
}
}
}

La mthode updateForecast() prend un objet Location en paramtre, obtenu via le


processus de mise jour de la localisation. Pour linstant, il suffit de savoir que Location
dispose des mthodes getLatitude() et getLongitude(), qui renvoient, respectivement,
la latitude et la longitude.
LURL Google Weather est stocke dans une ressource chane laquelle nous ajoutons en
cours dexcution la latitude et la longitude. Nous construisons un objet HttpGet avec
cette URL (lobjet HttpClient a t cr dans onCreate()) puis nous excutons cette
mthode. partir de la rponse XML, nous construisons la page HTML des prvisions que
nous transmettons au widget WebKit. Si lobjet HttpClient choue avec une exception,
nous indiquons lerreur laide dun toast.
1. Reportez-vous la page ddie cet ouvrage sur le site www.pearson.fr.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 238 Dimanche, 8. novembre 2009 12:23 12

238

Lart du dveloppement Android

Traitement des rponses


La rponse que lon obtient est dans un certain format HTML, XML, JSON, etc. et
cest nous, bien sr, de choisir linformation qui nous intresse pour en tirer quelque
chose dutile. Dans le cas de WeatherDemo, nous voulons extraire lheure de la prvision,
la temprature et licne (qui reprsente les conditions mtorologiques) afin de nous en
servir pour produire une page HTML.
Android fournit :

trois analyseurs XML, lanalyseur DOM classique du W3C (org.w3c.dom), un analyseur


SAX (org.xml.sax) et lanalyseur pull prsent au Chapitre 19 ;

un analyseur JSON (org.json).

Lorsque cela est possible, vous pouvez bien sr utiliser du code Java tiers pour prendre en
charge dautres formats un analyseur RSS/Atom, par exemple. Lutilisation du code tiers
a t dcrite au Chapitre 21.
Pour WeatherDemo, nous utilisons lanalyseur DOM du W3C dans notre mthode buildForecasts() :
void buildForecasts(String raw) throws Exception {
DocumentBuilder builder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc =
builder.parse(new InputSource(new StringReader(raw)));
NodeList forecastList =
doc.getElementsByTagName("forecast_conditions");
for (int i = 0; i < forecastList.getLength(); i++) {
Element currentFore = (Element) forecastList.item(i);
// Retrouvons le jour de la semaine
String day =currentFore.getElementsByTagName("day_of_week")
.item(0).getAttributes()
.item(0).getNodeValue();
String lowTemp = currentFore.getElementsByTagName("low")
.item(0).getAttributes()
.item(0).getNodeValue();
String highTemp = currentFore.getElementsByTagName("high")
.item(0).getAttributes()
.item(0).getNodeValue();
String icon = currentFore.getElementsByTagName("icon")
.item(0).getAttributes()
.item(0).getNodeValue();
Forecast f = new Forecast();
f.setDay(day);
f.setLowTemp(lowTemp);
f.setHighTemp(highTemp);
f.setIcon(icon);
forecasts.add(f);
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 239 Dimanche, 8. novembre 2009 12:23 12

Chapitre 22

Communiquer via Internet

239

Le code HTML est lu comme un InputStream et est fourni lanalyseur DOM. Puis on
recherche les lments forecast_conditions et lon remplit un ensemble de modles Forecast en incluant la date, la temprature et lURL de licne qui sera affiche en fonction du
temps.
La mthode generatePage() produit son tour un tableau HTML rudimentaire contenant
les prvisions :
String generatePage() {
StringBuffer bufResult = new StringBuffer("<html><body><table>");
bufResult.append("<tr><th width=\"50%\">Jour</th>"
+ "<th>Basse</th><th>Haute</th><th>Tendance</th></tr>");
for (Forecast forecast : forecasts) {
bufResult.append("<tr><td align=\"center\">");
bufResult.append(forecast.getDay());
bufResult.append("</td><td align=\"center\">");
bufResult.append(forecast.getLowTemp());
bufResult.append("</td>");
bufResult.append("</td><td align=\"center\">");
bufResult.append(forecast.getHighTemp());
bufResult.append("</td><td><img src=\"");
bufResult.append(forecast.getIcon());
bufResult.append("\"></td></tr>");
}
bufResult.append("</table></body></html>");
return (bufResult.toString());
}

La Figure 22.1 montre le rsultat obtenu.


Figure 22.1
Lapplication
WeatherDemo.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 240 Dimanche, 8. novembre 2009 12:23 12

240

Lart du dveloppement Android

Autres points importants


Si vous devez utiliser SSL, noubliez pas que la configuration de HttpClient ninclut pas
SSL par dfaut car cest vous de dcider comment grer la prsentation des certificats SSL
les acceptez-vous tous aveuglment, mme ceux qui sont autosigns ou qui ont expir ?
Prfrez-vous demander confirmation lutilisateur avant daccepter les certificats un peu
tranges ?
De mme, HttpClient est conu par dfaut pour tre utilis dans une application
monothread, bien quil puisse aisment tre configur pour travailler dans un contexte
multithread.
Pour tous ces types de problmes, la meilleure solution consiste consulter la documentation
et le support disponibles sur le site web de HttpComponents.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 241 Dimanche, 8. novembre 2009 12:23 12

Partie

IV

Intentions (Intents)
CHAPITRE 23.

Cration de filtres dintentions

CHAPITRE 24.

Lancement dactivits et de sous-activits

CHAPITRE 25.

Trouver les actions possibles grce lintrospection

CHAPITRE 26.

Gestion de la rotation

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 242 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 243 Dimanche, 8. novembre 2009 12:23 12

23
Cration de filtres
dintentions
Pour linstant, nous ne nous sommes intresss quaux activits ouvertes directement par
lutilisateur partir du lanceur du terminal, ce qui est, videmment, le moyen le plus
vident de lancer une activit et de la rendre disponible lutilisateur. Dans la plupart des
cas, cest de cette faon que lutilisateur commencera utiliser votre application.
Dans de nombreuses situations, le systme Android repose sur un grand nombre de
composants troitement lis. Ce que vous pouvez obtenir dans une interface graphique via
des botes de dialogue, des fentres filles, etc. est gnralement trait par des activits
indpendantes. Bien que lune delles puisse tre "spcifique" puisquelle apparat dans le
lanceur, les autres doivent toutes tre accessibles... dune faon ou dune autre.
Elles le sont grce aux intentions.
Une intention est essentiellement un message que lon passe Android pour lui dire "je
veux que tu fasses... quelque chose". Ce "quelque chose" dpend de la situation parfois,
on sait parfaitement de quoi il sagit (ouvrir lune de nos autres activits, par exemple)
mais, dautres fois, on ne le sait pas.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 244 Dimanche, 8. novembre 2009 12:23 12

244

Lart du dveloppement Android

Dans labsolu, Android ne se consacre quaux intentions et leurs rcepteurs. Maintenant que nous avons vu comment crer des activits, plongeons-nous dans les intentions
afin de pouvoir crer des applications plus complexes tout en tant de "bons citoyens
dAndroid".

Quelle est votre intention ?


Lorsque sir Tim Berners-Lee a conu le protocole de transfert hypertexte, HTTP, il a dfini un
ensemble de verbes et dadresses sous la forme dURL. Une adresse dsigne une ressource :
une page web, une image ou un programme qui sexcute sur un serveur, par exemple. Un
verbe prcise laction qui doit sappliquer cette adresse : GET pour la rcuprer, POST
pour lui envoyer des donnes de formulaire afin quelle les traite, etc.
Les intentions sont similaires car elles reprsentent une action et un contexte. Bien
quelles permettent de dfinir plus dactions et de composants de contexte quil ny a de
verbes et de ressources HTTP, le concept est le mme.
Tout comme un navigateur web sait comment traiter une paire verbe + URL, Android sait
comment trouver les activits ou les autres applications qui sauront grer une intention
donne.

Composantes des intentions


Les deux parties les plus importantes dune intention sont laction et ce quAndroid
appelle les "donnes". Elles sont quasiment analogues aux verbes et aux URL de HTTP
laction est le verbe et les "donnes" sont une Uri comme content://contacts/people/1,
reprsentant un contact dans la base de donnes des contacts. Les actions sont des constantes, comme ACTION_VIEW (pour afficher la ressource), ACTION_EDIT (pour lditer) ou
ACTION_PICK (pour choisir un lment disponible dans une Uri reprsentant une collection,
comme content://contacts/people).
Si vous crez une intention combinant ACTION_VIEW avec lUri content://contacts/
people/1 et que vous la passiez Android, ce dernier saura comment trouver et ouvrir une
activit capable dafficher cette ressource.
Outre laction et lUri des "donnes", vous pouvez placer dautres critres dans une intention
(qui est reprsente par un objet Intent) :

Une catgorie. Votre activit "principale" appartient la catgorie LAUNCHER, pour


indiquer quelle apparat dans le menu du lanceur. Les autres activits appartiendront
probablement aux catgories DEFAULT ou ALTERNATIVE.

Un type MIME indiquant le type de ressource sur laquelle vous voulez travailler si
vous ne connaissez pas une Uri collection.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 245 Dimanche, 8. novembre 2009 12:23 12

Chapitre 23

Cration de filtres dintentions

245

Un composant, cest--dire la classe de lactivit suppose recevoir cette intention. Cette


utilisation des composants vite davoir besoin des autres proprits de lintention,
mais elle rend cette dernire plus fragile car elle suppose des implmentations spcifiques.

Des "Extras", cest--dire un Bundle dautres informations que vous voulez passer au
rcepteur en mme temps que lintention et dont ce dernier pourra tirer parti. Les informations utilisables par un rcepteur donn dpendent du rcepteur et sont (heureusement)
bien documentes.

La documentation dAndroid consacre la classe Intent contient les listes des actions et
des catgories standard.

Routage des intentions


Comme on la mentionn prcdemment, si le composant cible a t prcis dans lintention, Android naura aucun doute sur sa destination il lancera lactivit en question. Ce
mcanisme peut convenir si lintention cible se trouve dans votre application, mais nest
vraiment pas recommand pour envoyer des intentions dautres applications car les noms
des composants sont globalement considrs comme privs lapplication et peuvent
donc tre modifis. Il est prfrable dutiliser les modles dUri et les types MIME pour
identifier les services auxquels vous souhaitez accder.
Si vous ne prcisez pas de composant cible, Android devra trouver les activits (ou les
autres rcepteurs dintentions) ligibles pour cette intention. Vous aurez remarqu que
nous avons mis "activits" au pluriel car une activit peut trs bien se rsoudre en plusieurs
activits. Cette approche du routage est prfrable au routage implicite.
Essentiellement, trois conditions doivent tre vrifies pour quune activit soit ligible
pour une intention donne :
1. Lactivit doit supporter laction indique.
2. Lactivit doit supporter le type MIME indiqu (sil a t fourni).
3. Lactivit doit supporter toutes les catgories nommes dans lintention.
La conclusion est que vous avez intrt ce que vos intentions soient suffisamment spcifiques pour trouver le ou les bons rcepteurs, mais pas plus.
Tout ceci deviendra plus clair mesure que nous tudierons quelques exemples.

Dclarer vos intentions


Tous les composants Android qui souhaitent tre prvenus par des intentions doivent
dclarer des filtres dintention afin quAndroid sache quelles intentions devraient aller vers
quel composant. Pour ce faire, vous devez ajouter des lments intent-filter au fichier
AndroidManifest.xml.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 246 Dimanche, 8. novembre 2009 12:23 12

246

Lart du dveloppement Android

Le script de cration des applications Android (activityCreator ou son quivalent IDE)


fournit des filtres dintention tous les projets. Ces dclarations sont de la forme :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.skeleton">
<application>
<activity android:name=".Now" android:label="Now">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Notez la prsence de llment intent-filter sous llment activity. Il annonceles


choses suivantes :

Cette activit est lactivit principale de cette application.

Elle appartient la catgorie LAUNCHER, ce qui signifie quelle aura une icne dans le
menu principal dAndroid.

Cette activit tant lactivit principale de lapplication, Android sait quelle est le
composant quil doit lancer lorsquun utilisateur choisit cette application partir du
menu principal.
Vous pouvez indiquer plusieurs actions ou catgories dans vos filtres dintention afin de
prciser que le composant associ (lactivit) gre plusieurs sortes dintentions diffrentes.
Il est fort probable que vous voudrez galement que vos activits secondaires (non MAIN)
prcisent le type MIME des donnes quelles manipulent. Ainsi, si une intention est destine ce type MIME directement ou indirectement via une Uri rfrenant une ressource
de ce type , Android saura que le composant sait grer ces donnes.
Vous pourriez, par exemple, dclarer une activit de la faon suivante :
<activity android:name=".TourViewActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.commonsware.tour" />
</intent-filter>
</activity>

Celle-ci sera alors lance par une intention demandant laffichage dune Uri reprsentant
un contenu vnd.android.cursor.item/vnd.commonsware.tour. Cette intention pourrait provenir dune autre activit de la mme application (lactivit principale, par exemple)
ou dune autre application qui connat une Uri que cette activit peut grer.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 247 Dimanche, 8. novembre 2009 12:23 12

Chapitre 23

Cration de filtres dintentions

247

Rcepteurs dintention
Dans les exemples que nous venons de voir, les filtres dintention taient configurs sur
des activits. Cependant, lier les intentions des activits nest parfois pas exactement ce
dont on a besoin :

Certains vnements systme peuvent nous obliger dclencher une opration dans un
service plutt quune activit.

Certains vnements peuvent devoir lancer des activits diffrentes en fonction des
circonstances, o le critre repose non pas uniquement sur lintention elle-mme, mais
sur un autre tat (si lon obtient lintention X et que la base de donnes contienne Y, on
lance lactivit M ; si la base ne contient pas Y, on lance lactivit N, par exemple).

Dans ces situations, Android offre un rcepteur dintention dfini comme une classe qui
implmente linterface BroadcastReceiver. Les rcepteurs dintention sont des objets
conus pour recevoir des intentions notamment celles qui sont diffuses et pour effectuer une action impliquant gnralement le lancement dautres intentions pour dclencher
une opration dans une activit, un service ou un autre composant.
Linterface BroadcastReceiver ne possde quune seule mthode onReceive(), que les
rcepteurs dintention doivent donc implmenter pour y effectuer les traitements quils
souhaitent en cas de rception dune intention. Pour dclarer un rcepteur dintention, il
suffit dajouter un lment receiver au fichier AndroidManifest.xml :
<receiver android:name=".MaClasseReceptriceDIntention/>

Un rcepteur dintention ne vit que le temps de traiter onReceive() lorsque cette


mthode se termine, linstance est susceptible dtre supprime par le ramasse-miettes et
ne sera pas rutilise. Ceci signifie donc que les fonctionnalits de ces rcepteurs sont un
peu limites, essentiellement pour viter lappel de fonctions de rappel. Ils ne peuvent
notamment pas tre lis un service ni ouvrir une bote de dialogue.
La seule exception est lorsque le BroadcastReceiver est implment sur un composant
qui a une dure de vie assez longue, comme une activit ou un service : dans ce cas, le
rcepteur vivra aussi longtemps que son "hte" (jusqu ce que lactivit soit stoppe, par
exemple). Cependant, dans cette situation, vous ne pouvez pas dclarer le rcepteur dans
AndroidManifest.xml : il faut appeler registerReceiver() dans la mthode onResume()
de lactivit pour annoncer son intrt pour une intention, puis appeler unregisterReceiver() dans sa mthode onPause() lorsque vous navez plus besoin de ces intentions.

Attention la pause
Il y a un petit problme lorsque lon utilise des objets Intent pour transmettre des messages : ceci ne fonctionne que lorsque le rcepteur est actif. Voici ce que prcise la documentation de BroadcastReceiver ce sujet :

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 248 Dimanche, 8. novembre 2009 12:23 12

248

Lart du dveloppement Android

Si vous enregistrez un rcepteur dans votre implmentation dActivity.onResume(), il


faut le dsinscrire dans Activity.onPause() (vous ne recevrez pas dintention pendant
la pause et cela vite une surcharge inutile du systme). Neffectuez pas cette dsinscription
dans Activity.onSaveInstanceState(), car cette mthode nest pas appele lorsque
lutilisateur revient dans son historique.
Vous pouvez donc utiliser les intentions pour transmettre des messages aux condition
suivantes :

Votre rcepteur ne se soucie pas de manquer des messages lorsquil est inactif.

Vous fournissez un moyen pour que le rcepteur rcupre les messages quil a manqus
pendant quil tait inactif.

Aux Chapitres 30 et 31, nous verrons un exemple de la premire condition, o le rcepteur


(le client du service) utilise des messages reposant sur des intentions lorsquelles sont
disponibles, mais pas quand le client nest pas actif.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 249 Dimanche, 8. novembre 2009 12:23 12

24
Lancement dactivits
et de sous-activits
La thorie sous-jacente de larchitecture de linterface utilisateur dAndroid est que les
dveloppeurs devraient dcomposer leurs applications en activits distinctes, chacune
tant implmente par une Activity accessible via des intentions, avec une activit
"principale" lance partir du menu dAndroid. Une application de calendrier, par exemple, pourrait avoir des activits permettant de consulter le calendrier, de visualiser un
simple vnement, den modifier un (et den ajouter un), etc.
Ceci implique, bien sr, que lune de vos activits ait un moyen den lancer une autre. Si,
par exemple, lutilisateur clique sur un vnement partir de lactivit qui affiche tout le
calendrier, vous voudrez montrer lactivit permettant dafficher cet vnement. Ceci
signifie que vous devez pouvoir lancer cette activit en lui faisant afficher un vnement
spcifique (celui sur lequel lutilisateur a cliqu).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 250 Dimanche, 8. novembre 2009 12:23 12

250

Lart du dveloppement Android

Cette approche peut utiliser deux scnarios :

Vous connaissez lactivit lancer, probablement parce quelle fait partie de votre
application.

Vous disposez dune Uri vers quelque chose et vous voulez que vos utilisateurs
puissent en faire quelque chose, bien que vous ne sachiez pas encore comment.

Ce chapitre prsente le premier scnario ; le suivant dtaillera le second.

Activits paires et sous-activits


Lorsque vous dcidez de lancer une activit, une question essentielle laquelle vous devez
rpondre est : "Est-ce que mon activit a besoin de savoir quand se termine lactivit
quelle a lance ?"
Supposons par exemple que vous vouliez crer une activit pour collecter des informations
dauthentification pour un service web auquel vous vous connectez vous devrez peuttre vous authentifier avec OpenID1 pour utiliser un service OAuth2. En ce cas, votre activit principale devra savoir quand se termine lauthentification pour pouvoir commencer
utiliser le service web.
Imaginons maintenant une application de courrier lectronique Android. Lorsque lutilisateur dcide de visualiser un fichier attach, ni vous ni lutilisateur ne sattend ce que
lactivit principale sache quand cette visualisation se terminera.
Dans le premier scnario, lactivit lance est clairement subordonne lactivit qui la
lance. La premire sera donc srement lance comme une sous-activit, ce qui signifie
que la seconde sera prvenue de la fin de son activit fille.
Dans le second scnario, lactivit lance est plutt un "pair" de lactivit qui la lance. Elle
sera donc plutt lance comme une activit classique. Votre activit ne sera pas informe de la
fin de sa "fille" mais, encore une fois, elle na pas vraiment besoin de le savoir.

Dmarrage
Pour dmarrer une activit, il faut une intention et choisir comment la lancer.

Cration dune intention


Comme on la expliqu au Chapitre 1, les intentions encapsulent une requte pour une
activit ou une demande adresse un autre rcepteur dintention, afin quil ralise une
certaine tche.
1. http://openid.net/.
2. http://oauth.net/.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 251 Dimanche, 8. novembre 2009 12:23 12

Chapitre 24

Lancement dactivits et de sous-activits

251

Si lactivit que vous comptez lancer vous appartient, il peut tre plus simple de crer une
intention explicite, nommant le composant lancer. partir de votre activit, vous pourriez
par exemple crer une intention de la faon suivante :
new Intent(this, HelpActivity.class);

Cette instruction indique que vous voulez lancer HelpActivity.


Vous pourriez galement crer une intention pour une Uri donne, demandant une action
particulire :
Uri uri=Uri.parse("geo:" + lat.toString() + "," + lon.toString());
Intent i=new Intent(Intent.ACTION_VIEW, uri);

Ici, partir de la latitude et de la longitude dune position (respectivement lat et lon),


nous construisons une Uri de schma geo et nous crons une intention demandant de
lafficher (ACTION_VIEW).

Faire appel
Lorsque lon dispose de lintention, il faut la passer Android et rcuprer lactivit fille
lancer. Quatre choix sont alors possibles :

Le plus simple consiste appeler startActivity() en lui passant lintention


Android recherchera lactivit qui correspond le mieux et lui passera lintention pour
quelle la traite. Votre activit ne sera pas prvenue de la fin de lactivit fille.

Vous pouvez appeler startActivityForResult() en lui passant lintention et un


identifiant (unique pour lactivit appelante). Android recherchera lactivit qui correspond le mieux et lui passera lintention. Votre activit sera prvenue par la mthode de
rappel onActivityResult() de la fin de lactivit fille (voir plus loin).

Vous pouvez appeler sendBroadcast(). Dans ce cas, Android passera lintention


tous les BroadcastReceiver enregistrs qui pourraient vouloir cette intention, pas
uniquement celui qui correspond le mieux.

Vous pouvez appeler sendOrderedBroadcast(). Android passera alors lintention


tous les BroadcastReceiver candidats, chacun leur tour si lun deux "consomme"
lintention, les autres candidats ne sont pas prvenus.

La plupart du temps, vous utiliserez startActivity() ou startActivityForResult()


les intentions diffuses sont plutt lances par le systme Android lui-mme.
Comme on la indiqu, vous pouvez implmenter la mthode de rappel onActivityResult() lorsque vous utilisez startActivityForResult(), afin dtre prvenu de la fin
de lactivit fille. Cette mthode reoit lidentifiant unique fourni startActivityForResult() pour que vous puissiez savoir quelle est lactivit qui sest termine. Vous rcuprez galement :

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 252 Dimanche, 8. novembre 2009 12:23 12

252

Lart du dveloppement Android

Le code rsultat de lactivit fille qui a appel setResult(). Gnralement, ce code


vaut RESULT_OK ou RESULT_CANCELLED, bien que vous puissiez crer vos propres
codes (choisissez un entier partir de la valeur RESULT_FIRST_USER).

Un objet String optionnel contenant des donnes du rsultat, comme une URL vers
une ressource interne ou externe une intention ACTION_PICK se sert gnralement de
cette chane pour renvoyer le contenu slectionn.

Un objet Bundle optionnel contenant des informations supplmentaires autres que le


code rsultat et la chane de donnes.

Pour mieux comprendre le lancement dune activit paire, examinons le projet Activities/Launch. Le fichier de description XML est assez simple puisquil contient deux
champs pour la latitude et la longitude, ainsi quun bouton :
<?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"
>
<TableLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="2dip"
android:paddingRight="4dip"
android:text="Situation :"
/>
<EditText android:id="@+id/lat"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="true"
android:editable="true"
android:singleLine="true"
android:layout_weight="1"
/>
<EditText android:id="@+id/lon"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="true"
android:editable="true"
android:singleLine="true"
android:layout_weight="1"
/>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 253 Dimanche, 8. novembre 2009 12:23 12

Chapitre 24

Lancement dactivits et de sous-activits

253

</TableRow>
</TableLayout>
<Button android:id="@+id/map"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Montre moi !"
/>
</LinearLayout>

LOnClickListener du bouton prend la latitude et la longitude pour les intgrer dans une
Uri de schma geo, puis lance lactivit.
package com.commonsware.android.activities;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class LaunchDemo extends Activity {
private EditText lat;
private EditText lon;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.map);
lat=(EditText)findViewById(R.id.lat);
lon=(EditText)findViewById(R.id.lon);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String _lat=lat.getText().toString();
String _lon=lon.getText().toString();
Uri uri=Uri.parse("geo:" + _lat + "," +_lon);
startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
});
}
}

Comme on le voit la Figure 24.1, cette activit ne montre pas grand-chose lorsquelle est
lance.
Laffichage devient plus intressant si lon entre un emplacement (38.8891 de latitude et
77.0492 de longitude, par exemple) et que lon clique sur le bouton (voir Figure 24.2).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 254 Dimanche, 8. novembre 2009 12:23 12

254

Lart du dveloppement Android

Figure 24.1
Lapplication
LaunchDemo, dans
laquelle on a saisi
un emplacement.

Notez quil sagit de lactivit de cartographie intgre Android : nous navons pas cr
dactivit pour afficher cette carte.
Au Chapitre 34, nous verrons comment crer des cartes dans nos activits, au cas o lon
aurait besoin de plus de contrle sur leur affichage.
Figure 24.2
La carte lance
par LaunchDemo,
montrant lemplacement
de la Tour Eiffel Paris.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 255 Dimanche, 8. novembre 2009 12:23 12

Chapitre 24

Lancement dactivits et de sous-activits

255

Navigation avec onglets


La navigation par onglet est lune des principales fonctionnalits des navigateurs web
actuels : grce elle, une mme fentre peut afficher plusieurs pages rparties dans une
srie donglets. Sur un terminal mobile, cela a moins dintrt car on gaspillerait la
prcieuse surface de lcran pour afficher les onglets eux-mmes. Toutefois, pour les
besoins de la dmonstration, nous montrerons comment crer ce genre de navigateur, en
utilisant TabActivity et les intentions.
Au Chapitre 10, nous avons vu quun onglet pouvait contenir une vue ou une activit.
Dans ce dernier cas, vous devez fournir une intention qui lancera lactivit souhaite ; le
framework de gestion des onglets placera alors linterface utilisateur de cette activit dans
longlet.
Votre premier instinct pourrait tre dutiliser une Uri http: comme nous lavions fait avec
une Uri geo: dans lexemple prcdent :
Intent i=new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("http://commonsware.com"));

Vous pourriez ainsi utiliser le navigateur intgr et disposer de toutes ses fonctionnalits.
Malheureusement, cela ne marche pas car, pour des raisons de scurit, vous ne pouvez
pas hberger les activits dautres applications dans vos onglets uniquement vos propres
activits.
Nous allons donc dpoussirer nos dmonstrations de WebView du Chapitre 13 pour crer
le projet Activities/IntentTab.
Voici le code source de lactivit principale, celle qui hberge le TabView :
public class IntentTabDemo extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TabHost host=getTabHost();
host.addTab(host.newTabSpec("un")
.setIndicator("CW")
.setContent(new Intent(this, CWBrowser.class)));
host.addTab(host.newTabSpec("deux")
.setIndicator("Android")
.setContent(new Intent(this, AndroidBrowser.class)));
}
}

Comme vous pouvez le constater, notre classe hrite de TabActivity : nous navons donc
pas besoin de crer un fichier de description XML TabActivity sen occupe pour nous.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 256 Dimanche, 8. novembre 2009 12:23 12

256

Lart du dveloppement Android

Nous nous contentons daccder au TabHost et de lui ajouter deux onglets, chacun prcisant une intention qui fait directement rfrence une autre classe. Ici, nos deux onglets
hbergeront respectivement un CWBrowser et un AndroidBrowser.
Ces activits sont de simples variantes de nos prcdents exemples de navigateurs :
public class CWBrowser extends Activity {
WebView browser;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
browser=new WebView(this);
setContentView(browser);
browser.loadUrl("http://commonsware.com");
}
}
public class AndroidBrowser extends Activity {
WebView browser;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
browser=new WebView(this);
setContentView(browser);
browser.loadUrl("http://code.google.com/android");
}
}

Tous les deux chargent simplement une URL diffrente dans le navigateur : la page
daccueil de CommonsWare dans lun (voir Figure 24.3), celle dAndroid dans lautre
(voir Figure 24.4). Le rsultat montre laspect quaurait un navigateur onglets avec
Android.
Lutilisation de sous-classes diffrentes pour chaque page cible est plutt onreuse. la
place, nous aurions pu empaqueter lURL pour quelle souvre comme un "extra" dans une
intention et utiliser cette intention pour crer une activit BrowserTab gnraliste qui
extrairait lURL de cet "extra" afin de lutiliser. Cette approche est laisse en exercice au
lecteur.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 257 Dimanche, 8. novembre 2009 12:23 12

Chapitre 24

Lancement dactivits et de sous-activits

257

Figure 24.3
Lapplication IntentTabDemo montrant le premier
onglet.

Figure 24.4
Lapplication IntentTabDemo montrant le second
onglet.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 258 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 259 Dimanche, 8. novembre 2009 12:23 12

25
Trouver les actions
possibles grce
lintrospection
Parfois, on sait exactement ce que lon veut faire afficher lune de nos autres activits,
par exemple ou lon en a une assez bonne ide comme voir le contenu de la ressource
dsigne par une Uri ou demander lutilisateur de choisir un contenu dun certain type
MIME. Mais, dautres fois, on ne sait rien... on dispose simplement dune Uri dont on ne
sait vraiment que faire.
Supposons que nous dveloppions un sous-systme de marquage pour Android, afin que
les utilisateurs puissent marquer des contenus contacts, URL, emplacements gographiques, etc. Ce sous-systme se ramne lUri du contenu concern et aux marqueurs
associs, afin que dautres sous-systmes puissent, par exemple, demander tous les contenus
utilisant un marqueur particulier.
Nous devons galement prvoir une activit de lecture permettant aux utilisateurs de
consulter tous leurs marqueurs et les contenus marqus. Le problme est quils sattendront pouvoir manipuler les contenus trouvs par ce sous-systme appeler un contact
ou afficher une carte correspondant un emplacement, par exemple.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 260 Dimanche, 8. novembre 2009 12:23 12

260

Lart du dveloppement Android

Pourtant, on na absolument aucune ide de ce quil est possible de faire avec toutes
les Uri. On peut srement afficher tous les contenus, mais peut-on les modifier ? Peut-on les
appeler au tlphone ? En outre, lutilisateur pouvant ajouter des applications avec des
nouveaux types de contenus tout moment, on ne peut pas supposer connatre toutes
les combinaisons possibles en consultant simplement les applications de base fournies
avec tous les terminaux Android.
Heureusement, les concepteurs dAndroid ont pens ce problme et ont mis notre
disposition plusieurs moyens de prsenter nos utilisateurs un ensemble dactivits
lancer pour une Uri donne mme si lon na aucune ide de ce que reprsente vraiment
cette Uri. Ce chapitre explore quelques-uns de ces outils dintrospection.

Faites votre choix


Parfois, on sait quune Uri reprsente une collection dun certain type : on sait, par exemple, que content://contacts/people reprsente la liste des contacts dans le rpertoire
initial des contacts. Dans ce cas, on laisse lutilisateur choisir un contact que notre activit
pourra ensuite utiliser (pour le marquer ou lappeler, par exemple).
Pour ce faire, on doit crer une intention ACTION_PICK sur lUri concerne, puis lancer
une sous-activit (par un appel startActivityForResult()) afin que lutilisateur
puisse choisir un contenu du type indiqu. Si notre mthode de rappel onActivityResult() pour cette requte reoit le code rsultat RESULT_OK, la chane de donnes peut
tre analyse afin de produire une Uri reprsentant le contenu choisi.
titre dexemple, examinons le projet Introspection/Pick : cette activit prsente
lutilisateur un champ pouvant contenir une Uri dsignant une collection (prremplie ici
avec content://contacts/people), plus un trs gros bouton :
<?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"
>
<EditText android:id="@+id/type"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cursorVisible="true"
android:editable="true"
android:singleLine="true"
android:text="content://contacts/people"
/>
<Button
android:id="@+id/pick"
android:layout_width="fill_parent"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 261 Dimanche, 8. novembre 2009 12:23 12

Chapitre 25

Trouver les actions possibles grce lintrospection

261

android:layout_height="fill_parent"
android:text="Dismoi tout!"
android:layout_weight="1"
/>
</LinearLayout>

Lorsquon clique dessus, le bouton cre une intention ACTION_PICK pour lUri collection
qui a t saisie par lutilisateur ; puis la sous-activit est lance. Si cette dernire se
termine par RESULT_OK, une intention ACTION_VIEW est invoque pour lUri rsultante.
public class PickDemo extends Activity {
static final int PICK_REQUEST=1337;
private EditText type;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
type=(EditText)findViewById(R.id.type);
Button btn=(Button)findViewById(R.id.pick);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(Intent.ACTION_PICK,
Uri.parse(type.getText().toString()));
startActivityForResult(i, PICK_REQUEST);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode==PICK_REQUEST) {
if (resultCode==RESULT_OK) {
startActivity(new Intent(Intent.ACTION_VIEW,
data.getData()));
}
}
}
}

Lutilisateur peut donc choisir une collection (voir Figure 25.1), slectionner un contenu
(voir Figure 25.2) et lafficher (voir Figure 25.3).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 262 Dimanche, 8. novembre 2009 12:23 12

262

Lart du dveloppement Android

Figure 25.1
Lapplication PickDemo
lors de son dmarrage.

Figure 25.2
La mme application,
aprs avoir cliqu sur le
bouton : la liste des
contacts saffiche.

Figure 25.3
Affichage dun contact
lanc par PickDemo aprs
que lutilisateur a choisi
une personne de la liste.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 263 Dimanche, 8. novembre 2009 12:23 12

Chapitre 25

Trouver les actions possibles grce lintrospection

263

Prfrez-vous le menu ?
Un autre moyen dautoriser lutilisateur effectuer des actions sur un contenu, sans savoir
lavance quelles sont les actions possibles, consiste injecter un ensemble de choix dans
le menu de lapplication, en appelant addIntentOptions(). Cette mthode prend en
paramtre une intention et remplit un ensemble de choix de linstance Menu sur laquelle
elle est appele, chacun de ces choix reprsentant une action possible. Slectionner lun de
ces choix lancera lactivit associe.
Dans lexemple prcdent, le contenu, dont nous ne savions rien, provenait dune autre
application Android. Ceci dit, nous pouvons aussi savoir parfaitement quel est ce contenu,
lorsque cest le ntre. Cependant, les applications Android tant tout fait capables
dajouter de nouvelles actions des types de contenus existants, les utilisateurs auront
peut-tre dautres possibilits que lon ne connat pas encore, mme si lon crit une application en sattendant un certain traitement du contenu.
Revenons, par exemple, au sous-systme de marquage voqu au dbut de ce chapitre.
Il serait trs ennuyeux pour les utilisateurs de devoir faire appel un outil de marquage
spar pour choisir un marqueur, puis revenir au contenu sur lequel ils travaillaient afin de
lui associer le marqueur choisi. Ils prfreraient srement disposer dune option dans le
menu "Home" de lactivit leur permettant dindiquer quils veulent effectuer un
marquage, ce qui les mnerait une activit de configuration du marqueur, qui saurait dj
le contenu qui doit tre marqu.
Pour ce faire, le sous-systme de marquage doit configurer un filtre dintention supportant
nimporte quel contenu avec sa propre action (ACTION_TAG, par exemple) et avec la catgorie CATEGORY_ALTERNATIVE, ce qui est la convention lorsquune application ajoute des
actions au contenu dune autre.
Pour crire des activits qui seront prvenues des ajouts possibles, comme le marquage,
faites appel addIntentOptions() pour ajouter les actions de ces ajouts votre menu,
comme ici :
Intent intent = new Intent(null, monUri);
intent.addCategory(Intent.ALTERNATIVE_CATEGORY);
menu.addIntentOptions(Menu.ALTERNATIVE, 0,
new ComponentName(this, MonActivite.class),
null, intent, 0, null);

Ici, monUri est une Uri dcrivant le contenu qui sera affich par lutilisateur dans cette
activit. MonActivite est le nom de la classe de lactivit et menu, le menu modifier.
Dans notre cas, lintention que lon utilise pour choisir les actions exige que les rcepteurs
dintention appropris reconnaissent la catgorie CATEGORY_ALTERNATIVE. Puis nous

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 264 Dimanche, 8. novembre 2009 12:23 12

264

Lart du dveloppement Android

ajoutons les options au menu avec la mthode addIntentOptions(), laquelle nous


passons les paramtres suivants :

La position de tri pour cet ensemble de choix. Cette valeur est gnralement 0 (pour
que lensemble apparaisse dans lordre o il est ajout au menu) ou ALTERNATIVE
(pour quil apparaisse aprs les autres choix du menu).

Un nombre unique pour cet ensemble de choix ou 0 si lon na pas besoin de ce nombre.

Une instance de ComponentName reprsentant lactivit qui remplit son menu elle sert
filtrer les propres actions de lactivit, afin quelle puisse les traiter comme elle le souhaite.

Un tableau dinstances dIntent, contenant les correspondances "spcifiques" toutes


les actions correspondant ces Intent safficheront dans le menu avant les autres
actions possibles.

Lintention pour laquelle vous voulez les actions disponibles.

Un ensemble dindicateurs. Le seul rellement pertinent est reprsent par


MATCH_DEFAULT_ONLY, qui indique que les actions qui correspondent doivent galement implmenter la catgorie DEFAULT_CATEGORY. Si lon na pas besoin de cette
information, il suffit dutiliser la valeur 0 pour ces indicateurs.

Un tableau de Menu.Items qui contiendra les lments de menu qui correspondent au


tableau des instances Intent spcifiques fourni en quatrime paramtre, ou null si
lon nutilise pas ces lments.

Demander lentourage
Les familles ActivityAdapter et addIntentOptions() utilisent toutes les deux la mthode
queryIntentActivityOptions() pour rechercher les actions possibles. queryIntentActivityOptions() est implmente dans PackageManager : pour obtenir une instance
de cette classe, servez-vous de la mthode getPackageManager().
La mthode queryIntentActivityOptions() prend certains des paramtres daddIntentOptions(), notamment le ComponentName de lappelant, le tableau des instances
dIntent "spcifiques", lIntent gnrale reprsentant les actions que vous recherchez et
lensemble des indicateurs. Elle renvoie une liste dinstances dIntent correspondant aux
critres indiqus, les Intent spcifiques en premier.
Pour offrir des actions alternatives aux utilisateurs par un autre moyen quaddIntentOptions(), vous pouvez appeler queryIntentActivityOptions(), obtenir les instances
dIntent et les utiliser pour remplir une autre interface utilisateur (une barre doutils, par
exemple).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 265 Dimanche, 8. novembre 2009 12:23 12

26
Gestion de la rotation
Certains terminaux Android, comme le G1 de T-Mobile, disposent dun clavier " tiroir"
qui, lorsquil est sorti, provoque le passage de lcran du mode portrait au mode paysage.
Dautres, comme liPhone, utilisent des acclromtres pour dterminer lorientation de
lcran.
Android fournit plusieurs moyens de grer la rotation de lcran afin que vos applications
puissent elles-mmes traiter correctement les deux orientations. Ces outils vous aident
simplement dtecter et grer le processus de rotation cest vous de vrifier que vos
interfaces utilisateurs apparatront correctement dans les deux orientations.

Philosophie de la destruction
Par dfaut, lorsquune modification dans la configuration du tlphone risque daffecter la
slection des ressources, Android supprimera et recrera toutes les activits en cours
dexcution ou en pause la prochaine fois quelles seront affiches. Bien que ce phnomne puisse avoir lieu pour un grand nombre de modifications de configuration (changement de la langue, par exemple), il interviendra surtout dans le cas des rotations, car un
pivotement de lcran force le chargement dun nouvel ensemble de ressources (les
fichiers de description, notamment).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 266 Dimanche, 8. novembre 2009 12:23 12

266

Lart du dveloppement Android

Il faut bien comprendre quil sagit du comportement par dfaut : il peut tre le plus adapt
lune ou lautre de vos activits, mais vous pouvez le contrler et adapter la rponse de
vos activits aux changements dorientation ou de configuration.

Tout est pareil, juste diffrent


Comme, par dfaut, Android dtruit et rcre votre activit lorsque lorientation change, il
suffit dutiliser la mthode onSaveInstanceState(), qui est appele chaque fois que
lactivit est supprime quelle quen soit la raison (tel un manque de mmoire). Implmentez cette mthode dans votre activit afin de stocker dans le Bundle fourni suffisamment
dinformations pour pouvoir revenir ltat courant. Puis, dans onCreate() (ou onRestore-InstanceState(), si vous prfrez), restaurez les donnes du Bundle et utilisez-les
pour remettre lactivit dans son tat antrieur.
Le projet Rotation/RotationOne, par exemple, utilise deux fichiers layout main.xml
pour les modes portrait et paysage. Ces fichiers se trouvent respectivement dans les rpertoires res/layout/ et res/layout-land/.
Voici la disposition du mode portrait :
<?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"
>
<Button android:id="@+id/pick"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="Choisir"
android:enabled="true"
/>
<Button android:id="@+id/view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="Voir"
android:enabled="false"
/>
</LinearLayout>

Voici celle du mode paysage :


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 267 Dimanche, 8. novembre 2009 12:23 12

Chapitre 26

Gestion de la rotation

267

android:layout_height="fill_parent"
>
<Button android:id="@+id/pick"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="Choisir"
android:enabled="true"
/>
<Button android:id="@+id/view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="Voir"
android:enabled="false"
/>
</LinearLayout>

Linterface utilisateur est essentiellement compose de deux boutons occupant, chacun, la


moiti de lcran. En mode portrait, les boutons sont placs lun au-dessus de lautre ; en
mode paysage, ils sont cte cte.
Lapplication semble fonctionner correctement une rotation (Ctrl+F12 dans lmulateur)
modifie la disposition de linterface. Bien que les boutons naient pas dtat, vous constateriez, si vous utilisiez dautres widgets (comme EditText), quAndroid prend automatiquement en charge une partie de leur tat (le texte saisi dans lEditText, par exemple).
En revanche, Android ne peut pas vous aider pour tout ce qui se trouve lextrieur des
widgets.
Cette application drive de lexemple PickDemo du Chapitre 25 (voir Figures 25.1, 25.2 et
25.3), o cliquer sur un contact vous permettait de consulter sa fiche. Ici, on la divise en
deux boutons, "Voir" ntant actif que lorsquun contact a t slectionn.
Voyons comment tout ceci est gr par onSaveInstanceState() :
public class RotationOneDemo extends Activity {
static final int PICK_REQUEST=1337;
Button viewButton=null;
Uri contact=null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.pick);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(Intent.ACTION_PICK,

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 268 Dimanche, 8. novembre 2009 12:23 12

268

Lart du dveloppement Android

Uri.parse("content://contacts/people"));
startActivityForResult(i, PICK_REQUEST);
}
});
viewButton=(Button)findViewById(R.id.view);
viewButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startActivity(new Intent(Intent.ACTION_VIEW, contact));
}
});
restoreMe(savedInstanceState);
viewButton.setEnabled(contact!=null);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode==PICK_REQUEST) {
if (resultCode==RESULT_OK) {
contact=data.getData();
viewButton.setEnabled(true);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (contact!=null) {
outState.putString("contact", contact.toString());
}
}
private void restoreMe(Bundle state) {
contact=null;
if (state!=null) {
String contactUri=state.getString("contact");
if (contactUri!=null) {
contact=Uri.parse(contactUri);
}
}
}
}

Dans lensemble, ceci ressemble une activit normale... parce que cen est une. Initialement, le "modle" un contact dsign par une Uri vaut null. Il est initialis la suite
du lancement de la sous-activit ACTION_PICK. Sa reprsentation sous forme de chane est

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 269 Dimanche, 8. novembre 2009 12:23 12

Chapitre 26

Gestion de la rotation

269

sauvegarde dans onSaveInstanceState() et restaure dans restoreMe() (qui est appele partir donCreate()). Si le contact nest pas null, le bouton "Voir" est activ et
permet de visualiser la fiche du contact slectionn.
Le rsultat est prsent aux Figures 26.1 et 26.2.
Lavantage de cette implmentation est quelle gre un certain nombre dvnements
systme en plus de la rotation la fermeture de lapplication cause dun manque de
mmoire, par exemple.
Pour le plaisir, mettez en commentaire lappel de restoreMe() dans onCreate() et
essayez de lancer lapplication : vous constaterez quelle "oublie" un contact slectionn
dans lune des orientations lorsque vous faites tourner lmulateur ou le terminal.
Figure 26.1
Lapplication
RotationOne
en mode portrait.

Figure 26.2
Lapplication
RotationOne
en mode paysage.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 270 Dimanche, 8. novembre 2009 12:23 12

270

Lart du dveloppement Android

Il ny a pas de petites conomies !


Le problme avec onSaveInstanceState() est que lon est limit un Bundle car cette
mthode de rappel est galement appele lorsque tout le processus est arrt (comme
quand il ny a plus assez de mmoire) : les donnes sauvegarder doivent donc pouvoir
tre srialises et ne pas dpendre du processus en cours.
Pour certaines activits, cette restriction ne pose pas de problme mais, pour dautres, elle
peut tre assez ennuyeuse. Dans une application de chat, par exemple, vous devrez couper
la connexion au serveur et la rtablir car il ny a pas moyen de stocker une socket dans un
Bundle ; ceci ne pose pas seulement un problme de performances mais peut galement
affecter la discussion elle-mme : votre dconnexion et votre reconnexion apparatront
dans les journaux du chat.
Un moyen de contourner ce problme consiste utiliser onRetainNonConfigurationInstance() au lieu donSaveInstanceState() pour les "petites" modifications comme
les rotations. La mthode de rappel onRetainNonConfigurationInstance() de votre
activit peut en effet renvoyer un Object, que vous pourrez rcuprer plus tard avec
getLastNonConfigurationInstance(). Cet Object peut reprsenter peu prs tout ce
que vous voulez gnralement, ce sera un objet "contexte" contenant ltat de lactivit,
comme les threads en cours dexcution, les sockets ouvertes, etc. La mthode
onCreate() de votre activit peut appeler getLastNonConfigurationInstance() si
elle renvoie une valeur non null, vous disposez de vos sockets, de vos threads, etc. La
plus grande limitation est que vous ne pouvez pas mettre dans ce contexte sauvegard tout
ce qui pourrait faire rfrence une ressource qui sera supprime, comme un Drawable
charg partir dune ressource.
Le projet Rotation/RotationTwo utilise cette approche pour grer les rotations. Les
fichiers de description, et donc lapparence visuelle, sont identiques ceux de Rotation/
RotationOne. Les diffrences apparaissent uniquement dans le code Java :
public class RotationTwoDemo extends Activity {
static final int PICK_REQUEST=1337;
Button viewButton=null;
Uri contact=null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.pick);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(Intent.ACTION_PICK,
Uri.parse("content://contacts/people"));

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 271 Dimanche, 8. novembre 2009 12:23 12

Chapitre 26

Gestion de la rotation

271

startActivityForResult(i, PICK_REQUEST);
}
});
viewButton=(Button)findViewById(R.id.view);
viewButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startActivity(new Intent(Intent.ACTION_VIEW, contact));
}
});
restoreMe();
viewButton.setEnabled(contact!=null);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode==PICK_REQUEST) {
if (resultCode==RESULT_OK) {
contact=data.getData();
viewButton.setEnabled(true);
}
}
}
@Override
public Object onRetainNonConfigurationInstance() {
return(contact);
}
private void restoreMe() {
contact=null;
if (getLastNonConfigurationInstance()!=null) {
contact=(Uri)getLastNonConfigurationInstance();
}
}
}

Ici, nous redfinissons onRetainNonConfigurationInstance() pour quelle renvoie


lUri de notre contact au lieu de sa reprsentation textuelle. De son ct, restoreMe()
appelle getLastNonConfigurationInstance() : si cette mthode renvoie une valeur non
null, il sagit de notre contact et lon active le bouton "Voir".
Lavantage de cette approche est que lon transmet lUri au lieu dune reprsentation
textuelle. Ici, cela ne reprsente pas une grosse conomie, mais notre tat pourrait tre
bien plus compliqu et contenir des threads, des sockets ou dautres choses que nous ne
pourrions pas empaqueter dans un Bundle.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 272 Dimanche, 8. novembre 2009 12:23 12

272

Lart du dveloppement Android

Rotation maison
Ceci dit, mme cette approche peut tre trop intrusive pour votre application. Supposons,
par exemple, que vous dveloppiez un jeu en temps rel, comme un FPS (First Person
Shooter). Les "ralentissements" (lags) que vos utilisateurs constateront lorsque lactivit
est supprime et recre peuvent leur suffire se faire tuer, ce quils napprcieront srement pas. Bien que ce soit moins un problme avec le G1 puisquune rotation implique
douvrir le clavier chose que lutilisateur ne fera pas au milieu dun jeu , dautres terminaux effectuent une rotation simplement en fonction des informations de leurs acclromtres.
La troisime possibilit pour grer les rotations consiste donc indiquer Android que
vous vous en occuperez vous-mme et que vous ne voulez pas que le framework vous aide.
Pour ce faire :
1. Utilisez lattribut android:configChanges de llment activity dans votre fichier
AndroidManifest.xml pour numrer les modifications de configuration que vous
voulez grer vous-mme.
2. Dans votre classe Activity, implmentez la mthode onConfigurationChanged(),
qui sera appele lorsque lune des modifications numres dans android:configChanges aura lieu.
Vous pouvez dsormais outrepasser tout le processus de suppression dactivit pour
nimporte quelle modification de configuration et simplement laisser une mthode de
rappel vous prvenir de cette modification.
Le projet Rotation/RotationThree utilise cette technique. L encore, les fichiers de
description sont les mmes que prcdemment et lapplication aura donc le mme aspect
que RotationOne et RotationTwo. Le code Java est en revanche assez diffrent car nous
nous proccupons non plus de sauvegarder ltat mais plutt de mettre jour linterface
utilisateur pour grer les changements dorientation.
Nous devons dabord ajouter une petite modification notre manifeste :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.rotation.three"
android:versionCode="1"
android:versionName="1.0.0">
<application android:label="@string/app_name">
<activity android:name=".RotationThreeDemo"
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 273 Dimanche, 8. novembre 2009 12:23 12

Chapitre 26

Gestion de la rotation

273

</activity>
</application>
</manifest>

Ici, nous indiquons que nous traiterons nous-mmes les modifications de configuration
keyboardHidden et orientation, ce qui nous protge de toutes les modifications de
"rotation" une ouverture dun clavier ou une rotation physique. Notez que cette configuration sapplique une activit, pas lapplication si vous avez plusieurs activits, vous
devrez dcider pour chacune delles de la tactique employer.
Voici le code Java de ce projet :
public class RotationThreeDemo extends Activity {
static final int PICK_REQUEST=1337;
Button viewButton=null;
Uri contact=null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupViews();
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode==PICK_REQUEST) {
if (resultCode==RESULT_OK) {
contact=data.getData();
viewButton.setEnabled(true);
}
}
}
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setupViews();
}
private void setupViews() {
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.pick);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(Intent.ACTION_PICK,
Uri.parse("content://contacts/people"));
startActivityForResult(i, PICK_REQUEST);
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 274 Dimanche, 8. novembre 2009 12:23 12

274

Lart du dveloppement Android

});
viewButton=(Button)findViewById(R.id.view);
viewButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startActivity(new Intent(Intent.ACTION_VIEW, contact));
}
});
viewButton.setEnabled(contact!=null);
}
}

Limplmentation donCreate() dlgue lessentiel de son traitement la mthode


setupViews(), qui charge le layout et configure les boutons. Cette partie a t place dans
sa propre mthode car elle est galement appele partir donConfigurationChanged().

Forcer le destin
Dans les trois sections prcdentes, nous avons vu comment traiter les vnements de rotation. Il existe, bien sr, une alternative radicale : demander Android de ne jamais faire
pivoter votre activit car, si lactivit ne pivote pas, il nest plus ncessaire de se soucier
dcrire le code pour grer les rotations.
Pour empcher Android de faire pivoter votre activit, il suffit dajouter android:screenOrientation = "portrait" (ou "landscape") au fichier AndroidManifest.xml (voir
le projet Rotation/RotationFour) :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.rotation.four"
android:versionCode="1"
android:versionName="1.0.0">
<application android:label="@string/app_name">
<activity android:name=".RotationFourDemo"
android:screenOrientation="portrait"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Noubliez pas que, comme prcdemment, cette configuration est propre une activit.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 275 Dimanche, 8. novembre 2009 12:23 12

Chapitre 26

Gestion de la rotation

275

Avec elle, lactivit est verrouille dans lorientation que vous avez prcise, quoi que lon
fasse ensuite. Les copies dcran des Figures 26.3 et 26.4 montrent la mme activit que
celle des trois sections prcdentes mais utilisent le manifeste ci-dessus, avec lmulateur
en mode portrait et en mode paysage. Vous remarquerez que linterface utilisateur na pas
vari et quelle reste en mode portrait dans les deux cas.
Figure 26.3
Lapplication RotationFour en mode portrait.

Figure 26.4
Lapplication RotationFour en mode paysage.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 276 Dimanche, 8. novembre 2009 12:23 12

276

Lart du dveloppement Android

Tout comprendre
Tous ces scnarios supposent que vous faites pivoter lcran en ouvrant le clavier du terminal (ou en utilisant la combinaison de touches Ctrl+F12 avec lmulateur), ce qui est la
norme pour les applications Android.
Cependant, nous navons pas encore prsent le scnario utilis par liPhone.
Vous avez sans doute dj vu une ou plusieurs publicits pour liPhone montrant comment
lcran change dorientation lorsque lon fait pivoter le tlphone. Par dfaut, le G1 ne se
comporte pas de cette faon sur ce terminal, lorientation de lcran ne dpend que de
louverture ou de la fermeture du clavier.
Cependant, il est trs facile de modifier ce comportement : pour que laffichage pivote en
fonction de la position du tlphone, il suffit dajouter android:screenOrientation =
"sensor" au fichier AndroidManifest.xml (voir le projet Rotation/RotationFive) :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.rotation.five"
android:versionCode="1"
android:versionName="1.0.0">
<application android:label="@string/app_name">
<activity android:name=".RotationFiveDemo"
android:screenOrientation="sensor"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

La valeur "sensor" indique Android que vous souhaitez que ce soient les acclromtres qui contrlent lorientation de lcran, afin quil pivote en mme temps que le tlphone.
Au moins sur le G1, ceci semble ne fonctionner que lorsque lon passe de la position
portrait classique la position paysage en faisant pivoter le tlphone de 90 degrs dans
le sens inverse des aiguilles dune montre. Une rotation dans lautre sens ne modifie pas
lcran.
Notez galement que cette configuration dsactive la rotation de lcran par louverture du
clavier. Dans une activit "normale", avec le terminal en position portrait, lcran pivotera
si lon ouvre le clavier ; avec une activit utilisant la configuration android:screenOrientation = "sensor", lcran ne pivotera pas.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 277 Dimanche, 8. novembre 2009 12:23 12

Partie

Fournisseurs
de contenus et services
CHAPITRE 27.

Utilisation dun fournisseur de contenu (content provider)

CHAPITRE 28.

Construction dun fournisseur de contenu

CHAPITRE 29.

Demander et exiger des permissions

CHAPITRE 30.

Cration dun service

CHAPITRE 31.

Appel dun service

CHAPITRE 32.

Alerter les utilisateurs avec des notifications

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 278 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 279 Dimanche, 8. novembre 2009 12:23 12

27
Utilisation dun fournisseur
de contenu
(content provider)
Avec Android, toute Uri de schma content:// reprsente une ressource servie par un
fournisseur de contenu. Les fournisseurs de contenu encapsulent les donnes en utilisant
des instances dUri comme descripteurs on ne sait jamais do viennent les donnes
reprsentes par lUri et lon na pas besoin de le savoir : la seule chose qui compte est
quelles soient disponibles lorsquon en a besoin. Ces donnes pourraient tre stockes
dans une base de donnes SQLite ou dans des fichiers plats, voire rcupres partir dun
terminal ou stockes sur un serveur situ trs loin dici, sur Internet.
partir dune Uri, vous pouvez raliser les oprations CRUD de base (Create, Read,
Update, Delete) en utilisant un fournisseur de contenu. Les instances dUri peuvent reprsenter des collections ou des lments individuels. Grce aux premires, vous pouvez
crer de nouveaux contenus via des oprations dinsertion. Avec les secondes, vous
pouvez lire les donnes quelles reprsentent, les modifier ou les supprimer.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 280 Dimanche, 8. novembre 2009 12:23 12

280

Lart du dveloppement Android

Android permet dutiliser des fournisseurs de contenu existants ou de crer les vtres. Ce
chapitre est consacr leur utilisation ; le Chapitre 28 expliquera comment mettre disposition vos propres donnes laide du framework des fournisseurs de contenu.

Composantes dune Uri


Le modle simplifi de construction dune Uri est constitu du schma, de lespace de
noms des donnes et, ventuellement, de lidentifiant de linstance. Ces diffrents composants sont spars par des barres de fraction, comme dans une URL. Le schma dune Uri
de contenu est toujours content://.
LUri content://constants/5 reprsente donc linstance constants didentifiant 5.
La combinaison du schma et de lespace de noms est appele "Uri de base" dun fournisseur de contenu ou dun ensemble de donnes support par un fournisseur de
contenu. Dans lexemple prcdent, content://constants est lUri de base dun fournisseur de contenu qui sert des informations sur "constants" (en loccurrence, des constantes
physiques).
LUri de base peut tre plus complique. Celle des contacts est, par exemple, content://
contacts/people, car le fournisseur de contenu des contacts peut fournir dautres
donnes en utilisant dautres valeurs pour lUri de base.
LUri de base reprsente une collection dinstances. Combine avec un identifiant
dinstance (5, par exemple), elle reprsente une instance unique.
La plupart des API dAndroid sattendent ce que les URI soient des objets Uri, bien quil
soit plus naturel de les considrer comme des chanes. La mthode statique Uri.parse()
permet de crer une instance dUri partir de sa reprsentation textuelle.

Obtention dun descripteur


Do viennent ces instances dUri ?
Le point de dpart le plus courant, lorsque lon connat le type de donnes avec lequel on
souhaite travailler, consiste obtenir lUri de base du fournisseur de contenu lui-mme.
CONTENT_URI, par exemple, est lUri de base des contacts reprsents par des personnes
elle correspond content://contacts/people. Si vous avez simplement besoin de la
collection, cette Uri fonctionne telle quelle ; si vous avez besoin dune instance dont vous
connaissez lidentifiant, vous pouvez utiliser la mthode addId() dUri pour la lui ajouter,
afin dobtenir une Uri pour cette instance prcise.
Vous pourriez galement obtenir des instances dUri partir dautres sources vous
pouvez rcuprer des descripteurs dUri pour les contacts via des sous-activits rpondant
aux intentions ACTION_PICK, par exemple. Dans ce cas, lUri est vraiment un descripteur

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 281 Dimanche, 8. novembre 2009 12:23 12

Chapitre 27

Utilisation dun fournisseur de contenu (content provider)

281

opaque... jusqu ce que vous dcidiez de la dchiffrer laide des diffrentes mthodes
daccs de la classe Uri.
Vous pouvez galement utiliser des objets String cods en dur ("content:// contacts/
people", par exemple) et les convertir en instances Uri grce Uri.parse(). Ceci dit, ce
nest pas une solution idale car les valeurs des Uri de base sont susceptibles dvoluer au
cours du temps.

Cration des requtes


partir dune Uri de base, vous pouvez excuter une requte pour obtenir des donnes du
fournisseur de contenu li cette Uri. Ce mcanisme ressemble beaucoup SQL : on
prcise les "colonnes" qui nous intressent, les contraintes permettant de dterminer les
lignes du rsultat, un ordre de tri, etc. La seule diffrence est que cette requte sadresse
un fournisseur de contenu, pas directement un SGBDR comme SQLite.
Le point nvralgique de ce traitement est la mthode managedQuery(), qui attend cinq
paramtres :
1. LUri de base du fournisseur de contenu auquel sadresse la requte ou lUri
dinstance de lobjet interrog.
2. Un tableau des proprits dinstances que vous voulez obtenir de ce fournisseur de
contenu.
3. Une contrainte, qui fonctionne comme la clause WHERE de SQL.
4. Un ensemble ventuel de paramtres lier la contrainte, par remplacement des ventuels marqueurs demplacements quelle contient.
5. Une instruction de tri facultative, qui fonctionne comme la clause ORDER BY de
SQL.
Cette mthode renvoie un objet Cursor partir duquel vous pourrez ensuite rcuprer les
donnes produites par la requte.
Les "proprits" sont aux fournisseurs de contenu ce que sont les colonnes aux bases de
donnes. En dautres termes, chaque instance (ligne) renvoye par une requte est forme
dun ensemble de proprits (colonnes) reprsentant, chacune, un lment des donnes.
Tout ceci deviendra plus clair avec un exemple : voici lappel de la mthode managedQuery() de la classe ConstantsBrowser du projet ContentProvider/Constants :
constantsCursor=managedQuery(Provider.Constants.CONTENT_URI,
PROJECTION, null, null, null);

Les paramtres de cet appel sont :

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 282 Dimanche, 8. novembre 2009 12:23 12

282

Lart du dveloppement Android

lUri passe lactivit par lappelant (CONTENT_URI), qui reprsente ici lensemble
des constantes physiques gres par le fournisseur de contenu ;

la liste des proprits rcuprer (voir le code ci-aprs) ;

trois valeurs null, indiquant que nous nutilisons pas de contrainte (car lUri reprsente linstance que nous voulons), ni de paramtres de contrainte, ni de tri (nous ne
devrions obtenir quune seule ligne).
private static final String[] PROJECTION = new String[] {
Provider.Constants._ID, Provider.Constants.TITLE,
Provider.Constants.VALUE};

Le plus gros "tour de magie", ici, est la liste des proprits. La liste des proprits pour un
fournisseur de contenu donn devrait tre prcise dans la documentation (ou le code
source) de celui-ci. Ici, nous utilisons des valeurs logiques de la classe Provider qui
reprsentent les diffrentes proprits qui nous intressent (lidentifiant unique, le nom et
la valeur de la constante).

Sadapter aux circonstances


Lorsque managedQuery() nous a fourni un Cursor, nous avons accs au rsultat de la
requte et pouvons en faire ce que nous voulons. Nous pouvons, par exemple, extraire
manuellement les donnes du Cursor pour remplir des widgets ou dautres objets.
Cependant, si le but de la requte est dobtenir une liste dans laquelle lutilisateur pourra
choisir un lment, il est prfrable dutiliser la classe SimpleCursorAdapter, qui tablit
un pont entre un Cursor et un widget de slection comme une ListView ou un Spinner.
Pour ce faire, il suffit de copier le Cursor dans un SimpleCursorAdapter que lon passe
ensuite au widget ce widget montrera alors les options disponibles.
La mthode onCreate() de ConstantsBrowser, par exemple, prsente lutilisateur la
liste des constantes physiques :
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
constantsCursor=managedQuery(Provider.Constants.CONTENT_URI,
PROJECTION, null, null, null);
ListAdapter adapter=new SimpleCursorAdapter(this,
R.layout.row, constantsCursor,
new String[] {Provider.Constants.TITLE,
Provider.Constants.VALUE},
new int[] {R.id.title, R.id.value});
setListAdapter(adapter);
registerForContextMenu(getListView());
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 283 Dimanche, 8. novembre 2009 12:23 12

Chapitre 27

Utilisation dun fournisseur de contenu (content provider)

283

Aprs lexcution de managedQuery() et lobtention du Cursor, ConstantsBrowser cre


un SimpleCursorAdapter avec les paramtres suivants :

Lactivit (ou un autre Context) qui cre ladaptateur. Ici, il sagit de ConstantsBrowser
elle-mme.

Lidentifiant du layout utilis pour afficher les lments de la liste (R.layout.row).

Le curseur (constantsCursor).

Les proprits extraire du curseur et utiliser pour configurer les instances View des
lments de la liste (TITLE et VALUE).

Les identifiants correspondants des widgets TextView qui recevront ces proprits
(R.id.title et R.id.value).

Puis on place ladaptateur dans la ListView et lon obtient le rsultat prsent la


Figure 27.1.
Figure 27.1
ConstantsBrowser,
affichage dune liste de
constantes physiques.

Pour disposer de plus de contrle sur les vues, vous pouvez crer une sous-classe de
SimpleCursorAdapter et redfinir getView() afin de crer vos propres widgets pour la
liste, comme on la expliqu au Chapitre 9.

Gestion manuelle des curseurs


Vous pouvez, bien sr, extraire les donnes du curseur " la main". Linterface Cursor
ressemble ce que proposent les API daccs aux bases de donnes lorsquelles fournis-

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 284 Dimanche, 8. novembre 2009 12:23 12

284

Lart du dveloppement Android

sent des curseurs sous forme dobjets bien que, comme toujours, la diffrence rside dans
les dtails.

Position
Les instances de Cursor intgrent la notion de position, qui est semblable linterface
Iterator de Java. Pour accder aux lignes dun curseur, vous disposez des mthodes
suivantes :

moveToFirst() vous place sur la premire ligne de lensemble rsultat, tandis que
moveToLast() vous place sur la dernire.

moveToNext() vous place sur la ligne suivante et teste sil reste une ligne traiter
(auquel cas elle renvoie true ; false sinon).

moveToPrevious() vous place sur la ligne prcdente : cest la mthode inverse de


moveToNext().

moveToPosition() vous place sur la ligne lindice indiqu ; move() vous place sur
une ligne relativement la ligne courante (selon un dplacement qui peut tre positif
ou ngatif).

getPosition() renvoie lindice de la position courante.

Vous disposez galement dun ensemble de prdicats, dont isFirst(), isLast(),


isBeforeFirst() et isAfterLast().

Proprits
Lorsque le Cursor est positionn sur la ligne voulue, plusieurs mthodes supportant les
diffrents types possibles vous permettent dobtenir les proprits de cette ligne
(getString(), getInt(), getFloat(), etc.). Chacune delles prend en paramtre lindice
de la proprit voulue (en partant de zro).
Pour savoir si une proprit donne possde une valeur, vous pouvez utiliser la mthode
isNull() pour savoir si cette proprit est null ou non.

Insertions et suppressions
Les fournisseurs de contenu seraient assez peu intressants si vous ne pouviez pas ajouter
ou supprimer des donnes et que vous deviez vous contenter de modifier celles qui sy
trouvent.
Pour insrer des donnes dans un fournisseur de contenu, linterface ContentProvider
(que vous pouvez obtenir via un appel getContentProvider() dans votre activit) offre
deux possibilits :

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 285 Dimanche, 8. novembre 2009 12:23 12

Chapitre 27

Utilisation dun fournisseur de contenu (content provider)

285

La mthode insert() prend en paramtre une Uri de collection et une structure


ContentValues dcrivant lensemble de donnes placer dans la ligne.

La mthode bulkInsert() prend en paramtre une Uri de collection et un tableau de


structures ContentValues pour remplir plusieurs lignes la fois.

La mthode insert() renvoie une Uri que vous pourrez manipuler ensuite. La mthode
bulkInsert() renvoie le nombre de lignes cres ; vous devrez effectuer une requte pour
obtenir nouveau les donnes que vous venez dinsrer.
Voici, par exemple, un extrait de ConstantsBrowser qui insre une nouvelle constante au
fournisseur de contenu partir dun DialogWrapper fournissant le nom et la valeur de
cette constante :
private void processAdd(DialogWrapper wrapper) {
ContentValues values=new ContentValues(2);
values.put(Provider.Constants.TITLE, wrapper.getTitle());
values.put(Provider.Constants.VALUE, wrapper.getValue());
getContentResolver().insert(Provider.Constants.CONTENT_URI,
values);
constantsCursor.requery();
}

Comme nous avons dj un Cursor en suspens pour le contenu de ce fournisseur, nous


appelons requery() sur celui-ci pour mettre jour son contenu. Cet appel, son tour,
mettra jour le SimpleCursorAdapter qui enveloppe ventuellement le Cursor et ces
modifications seront rpercutes sur les widgets de slection (ListView, par exemple) qui
utilisent cet adaptateur.
Pour supprimer une ou plusieurs lignes dun fournisseur de contenu, utilisez la mthode
delete() de ContentResolver. Elle fonctionne comme linstruction DELETE de SQL et
prend trois paramtres :
1. LUri reprsentant la collection (ou linstance) que vous voulez supprimer.
2. Une contrainte fonctionnant comme une clause WHERE, qui sert dterminer les
lignes qui doivent tre supprimes.
3. Un ventuel ensemble de paramtres qui remplaceront les marqueurs demplacements
apparaissant dans la contrainte.

Attention aux BLOB !


Les BLOB (Binary large objects) existent dans de nombreux SGBDR, dont SQLite. Le
modle Android, cependant, prfre grer ces volumes de donnes via leurs propres Uri.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 286 Dimanche, 8. novembre 2009 12:23 12

286

Lart du dveloppement Android

Un fournisseur de contenu, par consquent, ne fournit pas daccs direct via un Cursor
aux donnes binaires comme les photos : la place, cest une proprit de ce fournisseur
qui vous donnera lUri de ce BLOB particulier. Pour lire et crire les donnes binaires,
utilisez les mthodes getInputStream() et getOutputStream() de votre fournisseur de
contenu.
Dans la mesure du possible, il vaut mieux minimiser les copies de donnes inutiles. Lutilisation principale dune photo dans Android, par exemple, consiste lafficher, ce que sait
parfaitement faire le widget ImageView si on lui fournit une Uri vers un fichier JPEG. En
stockant la photo de sorte quelle possde sa propre Uri, vous navez pas besoin de copier
des donnes du fournisseur de contenu vers une zone temporaire juste pour pouvoir lafficher : il suffit dutiliser lUri. On suppose, en fait, que peu dapplications Android feront
beaucoup plus que dposer des donnes binaires et utiliser des widgets ou des activits
prdfinies pour afficher ces donnes.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 287 Dimanche, 8. novembre 2009 12:23 12

28
Construction dun
fournisseur de contenu
La construction dun fournisseur de contenu est srement la partie la plus complique et la
plus ennuyeuse du dveloppement avec Android car un fournisseur de contenu a de
nombreuses exigences en termes dimplmentation de mthodes et dexposition
de donnes publiques. Malheureusement, tant que vous naurez pas utilis votre fournisseur, vous ne pourrez pas savoir si vous avez tout fait correctement (cest donc diffrent de
la construction dune activit, o le compilateur vous indique les erreurs que vous avez
commises).
Ceci tant dit, la cration dun fournisseur de contenu est dune importance capitale
lorsquune application souhaite mettre ses donnes disposition dautres applications. Si
elle ne les garde que pour elle-mme, vous pouvez viter la cration dun fournisseur de
contenu en vous contentant daccder directement aux donnes depuis vos activits. En
revanche, si vous souhaitez que vos donnes puissent tre utilises par dautres si, par
exemple, vous dveloppez un lecteur RSS et que vous vouliez autoriser les autres
programmes accder aux flux que vous avez tlchargs et mis en cache , vous aurez
besoin dun fournisseur de contenu.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 288 Dimanche, 8. novembre 2009 12:23 12

288

Lart du dveloppement Android

Dabord, une petite dissection


Comme on la expliqu au chapitre prcdent, lUri est le pilier de laccs aux donnes
dun fournisseur de contenu : cest la seule information quil faut rellement connatre.
partir de lUri de base du fournisseur, vous pouvez excuter des requtes ou construire
une Uri vers une instance prcise dont vous connaissez lidentifiant.
Cependant, pour construire un fournisseur de contenu, vous devez en savoir un peu plus
sur les dtails internes de lUri dun contenu. Celle-ci est compose de deux quatre
parties en fonction de la situation :

Elle comprend toujours un schma (content://), indiquant quil sagit dune Uri de
contenu, pas dune Uri vers une ressource web (http://), par exemple.

Elle a toujours une autorit, qui est la premire partie du chemin plac aprs le schma.
Lautorit est une chane unique identifiant le fournisseur qui gre le contenu associ
cette Uri.

Elle contient ventuellement un chemin de types de donnes, form de la liste des


segments de chemins situs entre lautorit et lidentifiant dinstance (sil y en a un).
Ce chemin peut tre vide si le fournisseur ne gre quun seul type de donnes. Il peut
tre form dun seul segment (truc) ou de plusieurs (truc/machin/chouette) pour
grer tous les scnarios daccs aux donnes requis par le fournisseur de contenu.

Elle peut contenir un identifiant dinstance, qui est un entier identifiant une information
particulire du contenu. Une Uri de contenu sans identifiant dinstance dsigne
lensemble du contenu reprsent par lautorit (et, sil est fourni, par le chemin des
donnes).

Une Uri de contenu peut tre aussi simple que content://sekrits, qui dsigne lensemble du contenu fourni par nimporte quel fournisseur li lautorit sekrits (SecretsProvider, par exemple), ou aussi complique que content://sekrits/card/pin/17,
qui dsigne linformation (identifie par 17) de type card/pin gre par le fournisseur de
contenu sekrits.

Puis un peu de saisie


Vous devez ensuite proposer des types MIME correspondant au contenu de votre fournisseur.
Android utilise la fois lUri de contenu et le type MIME pour identifier le contenu sur le
terminal. Une Uri de collection ou, plus prcisment, la combinaison dune autorit et
dun chemin de type de donnes doit correspondre deux types MIME, lun pour la
collection, lautre pour une instance donne. Ces deux types correspondent aux motifs
dUri que nous avons vus dans la section prcdente, respectivement pour les Uri avec et
sans identifiant. Comme on la vu aux Chapitres 24 et 25, on peut fournir un type MIME
une intention pour quelle se dirige vers lactivit adquate (lintention ACTION_PICK

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 289 Dimanche, 8. novembre 2009 12:23 12

Chapitre 28

Construction dun fournisseur de contenu

289

sur un type MIME de collection pour appeler une activit de slection permettant de
choisir une instance dans cette collection, par exemple).
Le type MIME de la collection doit tre de la forme vnd.X.cursor.dir/Y, o X est le nom
de votre socit, organisation ou projet et o Y est un nom de type dlimit par des points.
Vous pourriez, par exemple, utiliser le type MIME vnd.tlagency.cursor.dir/
sekrits.card.pin pour votre collection de secrets.
Le type MIME de l'instance doit tre de la forme vnd.X.cursor.item/Y o X et Y sont
gnralement les mmes valeurs que celles utilises pour le type MIME de la collection
(bien que ce ne soit pas obligatoire).

tape n 1 : crer une classe Provider


Un fournisseur de contenu est une classe Java, tout comme une activit et une intention. La
principale tape de la cration dun fournisseur consiste donc produire sa classe Java, qui
doit hriter de ContentProvider. Cette sous-classe doit implmenter six mthodes qui,
ensemble, assurent le service quun fournisseur de contenu est cens offrir aux activits
qui veulent crer, lire, modifier ou supprimer du contenu.

onCreate()
Comme pour une activit, le point dentre principal dun fournisseur de contenu est sa
mthode onCreate(), o lon ralise toutes les oprations dinitialisation que lon
souhaite. Cest l, notamment, que lon initialise le stockage des donnes. Si, par exemple,
on compte stocker les donnes dans tel ou tel rpertoire dune carte SD et utiliser un
fichier XML comme "table des matires", cest dans onCreate() que lon vrifiera que ce
rpertoire et ce fichier existent et, dans le cas contraire, quon les crera pour que le reste
du fournisseur de contenu sache quils sont disponibles.
De mme, si le fournisseur de contenu utilise SQLite pour son stockage, cest dans
onCreate() que lon testera si les tables existent en interrogeant la table sqlite_master.
Voici, par exemple, la mthode onCreate() de la classe Provider du projet ContentProvider/Constants :
@Override
public boolean onCreate() {
db=(new DatabaseHelper(getContext())).getWritableDatabase();
return (db == null) ? false : true;
}

Toute la "magie" de ce code se trouve dans lobjet priv DatabaseHelper, qui a t dcrit
au Chapitre 20.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 290 Dimanche, 8. novembre 2009 12:23 12

290

Lart du dveloppement Android

query()
Comme lon pourrait sy attendre, la mthode query() contient le code grce auquel le
fournisseur de contenu obtient des dtails sur une requte quune activit veut raliser.
Cest vous de dcider deffectuer ou non cette requte.
La mthode query() attend les paramtres suivants :

Une Uri reprsentant la collection ou linstance demande.

Un String[] contenant la liste des proprits renvoyer.

Un String reprsentant lquivalent dune clause WHERE de SQL et qui contraint le


rsultat de la requte.

Un String[] contenant les valeurs qui remplaceront les ventuels marqueurs demplacements dans le paramtre prcdent.

Un String quivalant une clause ORDER BY de SQL.

Cest vous qui tes responsable de linterprtation de ces paramtres et vous devez
renvoyer un Cursor qui pourra ensuite tre parcouru pour accder aux donnes.
Comme vous pouvez vous en douter, ces paramtres sont fortement orients vers lutilisation de SQLite comme moyen de stockage. Vous pouvez en ignorer certains (la clause
WHERE, par exemple) mais vous devez alors lindiquer pour que les activits ne vous
interrogent que par une Uri dinstance et nutilisent pas les paramtres que vous ne grez
pas. Pour les fournisseurs qui utilisent SQLite, toutefois, limplmentation de la mthode
query() devrait tre triviale : il suffit dutiliser un SQLiteQueryBuilder pour convertir
les diffrents paramtres en une seule instruction SQL, puis dappeler la mthode query()
de cet objet pour invoquer la requte et obtenir le Cursor qui sera ensuite renvoy par
votre propre mthode query().
Voici, par exemple, limplmentation de la mthode query() de Provider :
@Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
SQLiteQueryBuilder qb=new SQLiteQueryBuilder();
qb.setTables(getTableName());
if (isCollectionUri(url)) {
qb.setProjectionMap(getDefaultProjection());
}
else {
qb.appendWhere(getIdColumnName() + "=" +
url.getPathSegments().get(1));
}
String orderBy;
if (TextUtils.isEmpty(sort)) {
orderBy=getDefaultSortOrder();
} else {

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 291 Dimanche, 8. novembre 2009 12:23 12

Chapitre 28

Construction dun fournisseur de contenu

291

orderBy=sort;
}
Cursor c=qb.query(db, projection, selection, selectionArgs,
null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), url);
return c;
}

Nous crons un SQLiteQueryBuilder et nous insrons les dtails de la requte dans cet
objet. Vous remarquerez que la requte pourrait utiliser soit une Uri de collection, soit une
Uri dinstance dans ce dernier cas, nous devons ajouter lidentifiant dinstance la
requte. Puis nous utilisons la mthode query() de lobjet builder pour obtenir un Cursor
correspondant au rsultat.

insert()
Votre mthode insert() recevra une Uri reprsentant la collection et une structure
ContentValues contenant les donnes initiales de la nouvelle instance. Cest vous de la
crer, dy placer les donnes fournies et de renvoyer une Uri vers cette nouvelle instance.
Une fois encore, cette implmentation sera triviale pour un fournisseur de contenu qui
utilise SQLite : il suffit de vrifier que toutes les donnes requises ont t fournies par
lactivit, de fusionner les valeurs par dfaut avec les donnes fournies et dappeler la
mthode insert() de la base de donnes pour crer linstance.
Voici, par exemple, limplmentation de la mthode insert() de Provider :
@Override
public Uri insert(Uri url, ContentValues initialValues) {
long rowID;
ContentValues values;
if (initialValues!=null) {
values=new ContentValues(initialValues);
} else {
values=new ContentValues();
}
if (!isCollectionUri(url)) {
throw new IllegalArgumentException("URL inconnue" + url);
}
for (String colName : getRequiredColumns()) {
if (values.containsKey(colName) == false) {
throw new IllegalArgumentException("Colonne manquante : " +
colName);
}
}
populateDefaultValues(values);
rowID=db.insert(getTableName(), getNullColumnHack(), values);
if (rowID > 0) {
Uri uri=ContentUris.withAppendedId(getContentUri(), rowID);

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 292 Dimanche, 8. novembre 2009 12:23 12

292

Lart du dveloppement Android

getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
throw new SQLException("Echec de linsertion dans " + url);
}

La technique est la mme que prcdemment : pour raliser linsertion, on utilise les particularits du fournisseur, plus les donnes insrer. Les points suivants mritent dtre
nots :

Une insertion ne peut se faire que dans une Uri de collection, cest la raison pour
laquelle on teste le paramtre avec isCollectionUri().

Le fournisseur sachant quelles sont les colonnes requises (avec getRequiredColumns()),


nous les parcourons et nous vrifions que les valeurs fournies leur correspondent.

Cest au fournisseur de fournir les valeurs par dfaut (via populateDefaultValues())


pour les colonnes qui ne sont pas fournies lappel dinsert() et qui ne sont pas
automatiquement gres par la dfinition de la table SQLite.

update()
La mthode update() prend en paramtre lUri de linstance ou de la collection modifier, une structure ContentValues contenant les nouvelles valeurs, une chane correspondant une clause WHERE de SQL et un tableau de chanes contenant les valeurs qui
remplaceront les marqueurs demplacement dans la clause WHERE. Il vous appartient
didentifier linstance ou les instances modifier (en utilisant lUri et la clause WHERE)
puis de remplacer leurs valeurs actuelles par celles qui ont t fournies en paramtre.
Si vous utilisez SQLite comme systme de stockage, il suffit de transmettre tous les paramtres la mthode update() de la base, mme si cet appel variera lgrement selon que
vous modifiez une ou plusieurs instances.
Voici, par exemple, limplmentation de la mthode update() de Provider :
@Override
public int update(Uri url, ContentValues values, String where,
String[] whereArgs) {
int count;
if (isCollectionUri(url)) {
count=db.update(getTableName(), values, where, whereArgs);
}
else {
String segment=url.getPathSegments().get(1);
count=db
.update(getTableName(), values, getIdColumnName() + "="
+ segment

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 293 Dimanche, 8. novembre 2009 12:23 12

Chapitre 28

Construction dun fournisseur de contenu

293

+ (!TextUtils.isEmpty(where) ? " AND (" + where


+ ) : ""), whereArgs);
}
getContext().getContentResolver().notifyChange(url, null);
return count;
}

Ici, les modifications pouvant sappliquer une instance prcise ou toute la collection,
nous testons lUri avec isCollectionUri() : sil sagit dune modification de collection,
on se contente de la faire. Sil sagit dune modification dune seule instance, il faut ajouter
une contrainte la clause WHERE pour que la requte ne porte que sur la ligne concerne.

delete()
Comme update(), delete() reoit une Uri reprsentant linstance ou la collection
concerne, une clause WHERE et ses paramtres. Si lactivit supprime une seule
instance, lUri doit la reprsenter et la clause WHERE peut tre null. Lactivit peut
galement demander la suppression dun ensemble dinstances en utilisant la clause
WHERE pour indiquer les instances dtruire.
Comme pour update(), limplmentation de delete() est simple si lon utilise SQLite
car on peut laisser ce dernier le soin danalyser et dappliquer la clause WHERE la
seule chose faire est dappeler la mthode delete() de la base de donnes.
Voici, par exemple, limplmentation de la mthode delete() de Provider :
@Override
public int delete(Uri url, String where, String[] whereArgs) {
int count;
long rowId=0;
if (isCollectionUri(url)) {
count=db.delete(getTableName(), where, whereArgs);
}
else {
String segment=url.getPathSegments().get(1);
rowId=Long.parseLong(segment);
count=db
.delete(getTableName(), getIdColumnName() + "="
+ segment
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ) : ""), whereArgs);
}
getContext().getContentResolver().notifyChange(url, null);
return count;
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 294 Dimanche, 8. novembre 2009 12:23 12

294

Lart du dveloppement Android

Ce code est quasiment identique celui de la mthode update() que nous avons dcrite
prcdemment il supprime un sous-ensemble de la collection ou une instance unique (si
elle vrifie la clause WHERE passe en paramtre).

getType()
getType() est la dernire mthode quil faut implmenter. Elle prend une Uri en paramtre et renvoie le type MIME qui lui est associ. Cette Uri pouvant dsigner une
collection ou une instance, vous devez la tester pour renvoyer le type MIME correspondant.
Voici limplmentation de la mthode getType() de Provider :
@Override
public String getType(Uri url) {
if (isCollectionUri(url)) {
return(getCollectionType());
}
return(getSingleType());
}

Comme vous pouvez le constater, lessentiel de ce code est dlgu aux mthodes prives
getCollectionType() et getSingleType() :
private String getCollectionType() {
return("vnd.android.cursor.dir/vnd.commonsware.constant");
}
private String getSingleType() {
return("vnd.android.cursor.item/vnd.commonsware.constant");
}

tape n 2 : fournir une Uri


Vous devez galement ajouter un membre public statique contenant lUri de chaque
collection supporte par votre fournisseur de contenu. Gnralement, on utilise une Uri
constante, publique et statique dans la classe du fournisseur lui-mme :
public static final Uri CONTENT_URI=
Uri.parse("content://com.commonsware.android.tourit.Provider/tours");

Vous pouvez utiliser le mme espace de noms pour lUri de contenu que pour vos classes
Java, afin de rduire le risque de collisions de noms.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 295 Dimanche, 8. novembre 2009 12:23 12

Chapitre 28

Construction dun fournisseur de contenu

295

tape n 3 : dclarer les proprits


Pour dfinir les noms des proprits de votre fournisseur, vous devez crer une classe
publique statique implmentant linterface BaseColumns :
public static final class Constants implements BaseColumns {
public static final Uri CONTENT_URI
=Uri.parse("content://com.commonsware.android.constants.Provider/
constants");
public static final String DEFAULT_SORT_ORDER="title";
public static final String TITLE="title";
public static final String VALUE="value";
}

Si vous utilisez SQLite pour stocker les donnes, les valeurs des constantes correspondant
aux noms des proprits doivent tre les noms des colonnes respectives de la table, afin de
pouvoir simplement passer la projection (tableau de proprits) lors de lappel query()
ou le ContentValues lors dun appel insert() ou update().
Vous remarquerez que les types des proprits ne sont pas prciss. Il peut sagir de chanes, dentiers, etc. en ralit, ils sont limits par les types autoriss par les mthodes
daccs du Cursor. Le fait que rien ne permette de tester les types signifie que vous devez
documenter soigneusement vos proprits afin que les utilisateurs de votre fournisseur de
contenu sachent quoi sattendre.

tape n 4 : modifier le manifeste


Ce qui lie limplmentation du fournisseur de contenu au reste de lapplication se trouve
dans le fichier AndroidManifest.xml. Il suffit dajouter un lment <provider> comme
fils de llment <application> :
<provider
android:name=".Provider"
android:authorities="com.commonsware.android.tourit.Provider" />

La proprit android:name est le nom de la classe du fournisseur de contenu, prfix par


un point pour indiquer quil se trouve dans lespace de noms des classes de cette application.
La proprit android:authorities est une liste des autorits reconnues par le fournisseur de contenu, spares par des points-virgules. Plus haut dans ce chapitre, nous avons
vu quune Uri de contenu tait constitue dun schma, dune autorit, dun chemin de
types de donnes et dun identifiant dinstance. Chaque autorit de chaque valeur
CONTENT_URI devrait tre incluse dans la liste android:authorities.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 296 Dimanche, 8. novembre 2009 12:23 12

296

Lart du dveloppement Android

Quand Android rencontre une Uri de contenu, il peut dsormais passer en revue les fournisseurs enregistrs via les manifestes afin de trouver une autorit qui correspond. Il saura
alors quelle application et quelle classe implmentent le fournisseur de contenu et pourra
ainsi tablir le lien entre lactivit appelante et le fournisseur de contenu appel.

Avertissements en cas de modifications


Votre fournisseur de contenu peut ventuellement avertir ses clients lorsque des modifications
ont t apportes aux donnes dune Uri de contenu particulire.
Supposons, par exemple, que vous ayez cr un fournisseur de contenu qui rcupre des
flux RSS et Atom sur Internet en fonction des abonnements de lutilisateur (via OPML,
par exemple). Le fournisseur offre un accs en lecture seule au contenu des flux et garde
un il vers les diffrentes applications du tlphone qui les utilisent (ce qui est prfrable
une solution o chacun implmenterait son propre systme de rcupration des flux).
Vous avez galement implment un service qui obtient de faon asynchrone les mises
jour de ces flux et qui modifie les donnes stockes sous-jacentes. Votre fournisseur de
contenu pourrait alerter les applications qui utilisent les flux que tel ou tel flux a t mis
jour, afin que celles qui utilisent ce flux prcis puissent rafrachir ses donnes pour disposer
de la dernire version.
Du ct du fournisseur de contenu, cela ncessite dappeler notifyChange() sur votre
instance de ContentResolver (que vous pouvez obtenir via un appel getContext().getContentResolver()). Cette mthode prend deux paramtres : lUri de
la partie du contenu qui a t modifie et le ContentObserver qui a initi la modification. Dans de nombreux cas, ce deuxime paramtre vaudra null ; une valeur non null
signifie simplement que lobservateur qui a lanc la modification ne sera pas prvenu de
sa propre modification.
Du ct du consommateur de contenu, une activit peut appeler registerContentObserver() sur son ContentResolver (obtenu via getContentResolver()). Cet appel lie une
instance de ContentObserver lUri fournie lobservateur sera prvenu chaque appel
de notifyChange() pour cette Uri. Lorsque le consommateur en a fini avec lUri, un
appel unregisterContentObserver() permet de librer la connexion.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 297 Dimanche, 8. novembre 2009 12:23 12

29
Demander et exiger
des permissions
la fin des annes 1990, une vague de virus sest rpandue sur Internet via les courriers
lectroniques. Ces virus utilisaient les informations stockes dans le carnet dadresses de
Microsoft Outlook : ils senvoyaient simplement en copie tous les contacts Outlook
dune machine infecte. Cette pidmie a t rendue possible parce que, cette poque,
Outlook ne prenait aucune mesure pour protger ses donnes des programmes qui utilisaient son API puisque celle-ci avait t conue pour des dveloppeurs ordinaires, pas
pour des auteurs de virus.
Aujourdhui, de nombreuses applications qui utilisent des contacts les scurisent en
exigeant quun utilisateur donne le droit dy accder. Ces droits peuvent tre octroys au
cas par cas ou une fois pour toutes, lors de linstallation.
Android fonctionne de la mme faon : il exige que les applications qui souhaitent lire ou
crire les contacts aient les droits ncessaires. Cependant, son systme de permission va
bien au-del des donnes de contact et sapplique aux fournisseurs de contenu et aux services
qui ne font pas partie du framework initial.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 298 Dimanche, 8. novembre 2009 12:23 12

298

Lart du dveloppement Android

En tant que dveloppeur Android, vous devrez frquemment vous assurer que vos applications aient les permissions adquates pour manipuler les donnes des autres applications.
Vous pouvez galement choisir dexiger des permissions pour que les autres applications
aient le droit dutiliser vos donnes ou vos services si vous les mettez disposition des
autres composants dAndroid.

Mre, puis-je ?
Demander dutiliser les donnes ou les services dautres applications exige dajouter
llment uses-permission au fichier AndroidManifest.xml. Votre manifeste peut
ainsi contenir zro ou plusieurs de ces lments comme fils directs de llment racine
manifest.
Llment uses-permission na quun seul attribut, android:name, qui est le nom de la
permission exige par votre application :
<uses-permission
android:name="android.permission.ACCESS_LOCATION" />

Les permissions systme de base commencent toutes par android.permission et sont


numres dans la documentation du SDK la rubrique Manifest.permission. Les
applications tierces peuvent possder leurs propres permissions, qui seront sans doute
galement documentes. Voici quelques-unes des permissions prdfinies les plus importantes :

INTERNET, si votre application souhaite accder Internet par quelque moyen que ce
soit, des sockets brutes de Java au widget WebView.

READ_CALENDAR, READ_CONTACTS, etc. pour la lecture des donnes partir des fournisseurs
de contenu intgrs.

WRITE_CALENDAR, WRITE_CONTACTS, etc. pour la modification des donnes dans les


fournisseurs de contenu intgrs.

Lutilisateur devra confirmer ces permissions lors de linstallation de lapplication. Cependant,


cette confirmation nest pas disponible dans la version actuelle de lmulateur.
Si vous ne possdez pas une permission donne et que vous tentiez deffectuer une opration qui lexige, lexception SecurityException vous informera du problme, bien que
ce ne soit pas une garantie cet chec peut prendre dautres formes si cette exception est
capture et traite par ailleurs.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 299 Dimanche, 8. novembre 2009 12:23 12

Chapitre 29

Demander et exiger des permissions

299

Halte ! Qui va l ?
Lautre ct de la mdaille est, bien sr, la scurisation de votre propre application. Si
celle-ci est uniquement constitue dactivits et de rcepteurs dintention, la scurit peut
tre simplement tourne "vers lextrieur", lorsque vous demandez le droit dutiliser les
ressources dautres applications. Si, en revanche, votre application comprend des fournisseurs de contenu ou des services, vous voudrez mettre en place une scurit "vers lintrieur", pour contrler les applications qui peuvent accder vos donnes et le type de ces
accs.
Le problme, ici, est moins les "perturbations" que peuvent causer les autres applications
vos donnes que la confidentialit des informations ou lutilisation de services qui pourraient vous coter cher. Les permissions de base dAndroid sont conues pour cela
pouvez-vous lire ou modifier des contacts, envoyer des SMS, etc. Si votre application
stocke des informations susceptibles dtre prives, la scurit est moins un problme.
Mais, si elle stocke des donnes prives comme des informations mdicales, la scurit
devient bien plus importante.
La premire tape pour scuriser votre propre application laide de permissions consiste
les dclarer dans le fichier AndroidManifest.xml. Au lieu dutiliser uses-permission,
vous ajoutez alors des lments permission. L encore, il peut y avoir zro ou plusieurs
de ces lments, qui seront tous des fils directs de manifest.
Dclarer une permission est un peu plus compliqu quutiliser une permission car vous
devez donner trois informations :
1. Le nom symbolique de la permission. Pour viter les collisions de vos permissions
avec celles des autres applications, utilisez lespace de noms Java de votre application
comme prfixe.
2. Un label pour la permission : un texte court et comprhensible par les utilisateurs.
3. Une description de la permission : un texte un peu plus long et comprhensible par les
utilisateurs.
<permission
android:name="vnd.tlagency.sekrits.SEE_SEKRITS"
android:label="@string/see_sekrits_label"
android:description="@string/see_sekrits_description" />

Cet lment nimpose pas la permission. Il indique seulement quil sagit dune permission possible ; votre application doit quand mme savoir dtecter les violations de scurit
lorsquelles ont lieu.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 300 Dimanche, 8. novembre 2009 12:23 12

300

Lart du dveloppement Android

Imposer les permissions via le manifeste


Une application a deux moyens dimposer des permissions, en prcisant o et dans quelles
circonstances elles sont requises. Le plus simple consiste indiquer dans le manifeste les
endroits o elles sont requises.
Les activits, les services et les rcepteurs dintentions peuvent dclarer un attribut
android:permission dont la valeur est le nom de la permission exige pour accder ces
lments :
<activity
android:name=".SekritApp"
android:label="Top Sekrit"
android:permission="vnd.tlagency.sekrits.SEE_SEKRITS">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Seules les applications qui ont demand la permission indique pourront accder au
composant ainsi scuris. Ici, "accder" la signification suivante :

Les activits ne peuvent pas tre lances sans cette permission.

Les services ne peuvent pas tre lancs, arrts ou lis une activit sans cette permission.

Les rcepteurs dintention ignorent les messages envoys via sendBroadcast() si


lexpditeur ne possde pas cette permission.

Les fournisseurs de contenu offrent deux attributs distincts, android:readPermission et


android:writePermission :
<provider
android:name=".SekritProvider"
android:authorities="vnd.tla.sekrits.SekritProvider"
android:readPermission="vnd.tla.sekrits.SEE_SEKRITS"
android:writePermission="vnd.tla.sekrits.MOD_SEKRITS" />

Dans ce cas, readPermission contrle laccs linterrogation du fournisseur de


contenu, tandis que writePermission contrle laccs aux insertions, aux modifications
et aux suppressions de donnes dans le fournisseur.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 301 Dimanche, 8. novembre 2009 12:23 12

Chapitre 29

Demander et exiger des permissions

301

Imposer les permissions ailleurs


Il y a deux moyens supplmentaires dimposer les permissions dans votre code.
Vos services peuvent vrifier les permissions chaque appel, grce checkCallingPermission(), qui renvoie PERMISSION_GRANTED ou PERMISSION_DENIED selon que
lappelant a ou non la permission indique. Si, par exemple, votre service implmente
des mthodes de lecture et dcriture spares, vous pourriez obtenir le mme effet que
readPermission et writePermission en vrifiant que ces mthodes ont les permissions
requises.
Vous pouvez galement inclure une permission dans lappel sendBroadcast(). Ceci
signifie que les rcepteurs possibles doivent possder cette permission : sans elle, ils ne
pourront pas recevoir lintention. Le sous-systme dAndroid inclut probablement la
permission RECEIVE_SMS lorsquil diffuse linformation quun SMS est arriv, par exemple ceci restreint les rcepteurs de cette intention aux seuls qui sont autoriss recevoir
des messages SMS.

Vos papiers, sil vous plat !


Il ny a pas de dcouverte automatique des permissions lors de la compilation ; tous les
checs lis aux permissions ont lieu lexcution. Il est donc important de documenter
les permissions requises pour votre API publique, ce qui comprend les fournisseurs de
contenu, les services et les activits conues pour tre lances partir dautres activits.
Dans le cas contraire, les programmeurs voulant sinterfacer avec votre application
devront batailler pour trouver les rgles de permissions que vous avez mises en place.
En outre, vous devez vous attendre ce que les utilisateurs de votre application soient
interrogs pour confirmer les permissions dont votre application a besoin. Vous devez
donc leur indiquer quoi sattendre, sous peine quune question pose par le tlphone ne
les dcide ne pas installer ou ne pas utiliser lapplication.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 302 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 303 Dimanche, 8. novembre 2009 12:23 12

30
Cration dun service
Comme on la dj mentionn, les services Android sont destins aux processus de longue
haleine, qui peuvent devoir continuer de sexcuter mme lorsquils sont spars de toute
activit. titre dexemple, citons la musique, qui continue dtre joue mme lorsque
lactivit de "lecture" a t supprime, la rcupration des mises jour des flux RSS sur
Internet et la persistance dune session de chat, mme lorsque le client a perdu le focus
cause de la rception dun appel tlphonique.
Un service est cr lorsquil est lanc manuellement (via un appel lAPI) ou quand une
activit tente de sy connecter via une communication interprocessus (IPC). Il perdure
jusqu ce quil ne soit plus ncessaire et que le systme ait besoin de rcuprer de la
mmoire. Cette longue excution ayant un cot, les services doivent prendre garde ne
pas consommer trop de CPU sous peine duser trop rapidement la batterie du terminal.
Dans ce chapitre, vous apprendrez crer vos propres services ; le chapitre suivant sera
consacr lutilisation de ces services partir de vos activits ou dautres contextes. Tous
les deux utiliseront comme exemple le projet Service/WeatherPlus, dont limplmentation sera essentiellement prsente dans ce chapitre. Ce projet tend Internet/Weather
en lempaquetant dans un service qui surveille les modifications de lemplacement du
terminal, afin que les prvisions soient mises jour lorsque celui-ci "se dplace".

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 304 Dimanche, 8. novembre 2009 12:23 12

304

Lart du dveloppement Android

Service avec classe


Limplmentation dun service partage de nombreuses caractristiques avec la construction
dune activit. On hrite dune classe de base fournie par Android, on redfinit certaines
mthodes du cycle de vie et on accroche le service au systme via le manifeste.
La premire tape de cration dun service consiste tendre la classe Service en drivant
une classe WeatherPlusService dans le cadre de notre exemple.
Tout comme les activits peuvent redfinir les mthodes onCreate(), onResume(),
onPause() et apparentes, les implmentations de Service peuvent redfinir trois mthodes
du cycle de vie :
1. onCreate(), qui, comme pour les activits, sera appele lorsque le processus du
service est cr.
2. onStart(), qui est appele lorsquun service est lanc manuellement par un autre
processus, ce qui est diffrent dun lancement implicite par une requte IPC (voir
Chapitre 31).
3. onDestroy(), qui est appele lorsque le service est teint.
Voici, par exemple, la mthode onCreate() de WeatherPlusService :
@Override
public void onCreate() {
super.onCreate();
client=new DefaultHttpClient();
format=getString(R.string.url);
myLocationManager=
(LocationManager)getSystemService(Context.LOCATION_SERVICE);
myLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
10000,
10000.0f,
onLocationChange);
singleton=this;
}

On appelle dabord la mthode onCreate() de la superclasse afin quAndroid puisse


effectuer correctement son travail dinitialisation. Puis on cre notre HttpClient et la
chane de format, comme nous lavions fait dans le projet Weather. On obtient ensuite
linstance de LocationManager pour notre application et on lui demande de rcuprer
les mises jour mesure que notre emplacement volue, via LocationManager.GPS_MANAGER, qui sera dtaill au Chapitre 33.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 305 Dimanche, 8. novembre 2009 12:23 12

Chapitre 30

Cration dun service

305

La mthode onDestroy() est bien plus simple :


@Override
public void onDestroy() {
super.onDestroy();
singleton=null;
myLocationManager.removeUpdates(onLocationChange);
}

On se contente ici de stopper la surveillance des dplacements, aprs avoir appel la


mthode onDestroy() de la superclasse pour quAndroid puisse effectuer les travaux de
nettoyage ncessaires.
Outre ces mthodes du cycle de vie, votre service doit galement implmenter la mthode
onBind(), qui renvoie un IBinder, la composante fondamentale du mcanisme dIPC.
Pour les services locaux ce qui nous intresse dans ce chapitre , il suffit que cette
mthode renvoie null.

Il ne peut en rester quun !


Par dfaut, les services sexcutent dans le mme processus que tous les autres composants de lapplication les activits, par exemple. On peut donc appeler les mthodes de
lAPI sur lobjet service... condition de mettre la main dessus. Dans lidal, il devrait
exister un moyen de demander Android de nous donner lobjet du service local ; malheureusement, ce moyen nexiste pas encore et nous en sommes donc rduits tricher.
Il ne peut y avoir, au plus, quune seule copie dun mme service qui sexcute en
mmoire. Il peut ny en avoir aucune si le service na pas t lanc mais, mme si
plusieurs activits tentent dutiliser le service, un seul sexcutera vraiment. Il sagit donc
dune implmentation du patron de conception singleton il suffit donc que lon expose le
singleton lui-mme pour que les autres composants puissent accder lobjet.
Dans le cas de WeatherPlusService, nous utilisons un membre statique public, singleton, pour stocker ce singleton (nous pourrions videmment rendre ce membre priv et
fournir une mthode daccs). Dans onCreate(), nous initialisons singleton avec lobjet
lui-mme, tandis que, dans onDestroy(), nous le rinitialisons null. Cette dernire
tape est trs importante. Les donnes statiques sont dangereuses car elles peuvent provoquer des fuites mmoire : si nous oublions de remettre singleton null dans onDestroy(), lobjet WeatherPlusService restera indfiniment en mmoire, bien quil soit
dconnect du reste dAndroid. Assurez-vous de toujours rinitialiser null les rfrences
statiques de vos services !
Comme nous le verrons au chapitre suivant, les activits peuvent dsormais accder aux
mthodes publiques de votre objet service grce ce singleton.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 306 Dimanche, 8. novembre 2009 12:23 12

306

Lart du dveloppement Android

Destine du manifeste
Enfin, vous devez ajouter le service votre fichier AndroidManifest.xml pour quil soit
reconnu comme un service utilisable. Il suffit pour cela dajouter un lment service
comme fils de llment application en utilisant lattribut android:name pour dsigner
la classe du service.
Voici, par exemple, le fichier AndroidManifest.xml du projet WeatherPlus :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.service">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application android:label="@string/app_name">
<activity android:name=".WeatherPlus" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".WeatherPlusService" />
</application>
</manifest>

La classe du service tant dans le mme espace de noms que tout ce qui se trouve dans
cette application, vous pouvez utiliser la notation pointe simplifie (".WeatherPlusService") pour dsigner votre classe.
Si vous voulez exiger certaines permissions pour lancer ou lier le service, ajoutez un attribut android:permission prcisant la permission demande voir Chapitre 29 pour plus
de dtails.

Sauter la clture
Parfois, le service doit prvenir de faon asynchrone une activit dun certain vnement.
La thorie derrire limplmentation de WeatherPlusService, par exemple, est que le
service est prvenu lorsque le terminal (ou lmulateur) change demplacement. Le service
appelle alors le service web et produit une nouvelle page de prvisions. Puis il doit prvenir lactivit que ces nouvelles prvisions sont disponibles, afin quelle puisse les charger
et les afficher.
Pour interagir de cette faon avec les composants, deux possibilits sont votre disposition : les mthodes de rappel et les intentions diffuses.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 307 Dimanche, 8. novembre 2009 12:23 12

Chapitre 30

Cration dun service

307

Notez que, si votre service doit simplement alerter lutilisateur dun certain vnement,
vous pouvez utiliser une notification, qui est le moyen le plus classique de grer ce type de
scnario.

Mthodes de rappel
Une activit pouvant travailler directement avec un service local, elle peut fournir une
sorte dobjet "couteur" au service, que ce dernier pourra ensuite appeler au besoin. Pour
que ceci fonctionne, vous devez agir comme suit :
1. Dfinissez une interface Java pour cet objet couteur.
2. Donnez au service une API publique pour enregistrer et supprimer des couteurs.
3. Faites en sorte que le service utilise ces couteurs au bon moment, pour prvenir ceux
qui ont enregistr lcouteur dun vnement donn.
4. Faites en sorte que lactivit enregistre et supprime un couteur en fonction de ses
besoins.
5. Faites en sorte que lactivit rponde correctement aux vnements grs par les couteurs.
La plus grande difficult consiste sassurer que lactivit inactive les couteurs
lorsquelle se termine. Les objets couteurs connaissent gnralement leur activit, soit
explicitement (via un membre) soit implicitement (en tant implments comme une
classe interne). Si le service repose sur des objets couteurs qui ont t inactivs, les activits correspondantes resteront en mmoire, mme si elles ne sont plus utilises par
Android. Ceci cre donc une fuite mmoire importante. Vous pouvez utiliser des WeakReference, des SoftReference ou des constructions similaires pour garantir que les couteurs enregistrs par une activit pour votre service ne maintiendront pas cette dernire en
mmoire lorsquelle est supprime.

Intentions diffuses
Une autre approche que nous avions dj mentionne au chapitre consacr aux filtres
dintention consiste faire en sorte que le service lance une intention diffuse qui pourra
tre capture par lactivit, en supposant que cette dernire existe encore et ne soit pas en
pause. Nous examinerons le ct client de cet change au prochain chapitre mais, pour le
moment, intressons-nous la faon dont un service peut envoyer une telle intention.
Limplmentation de haut niveau du flux est empaquete dans FetchForecastTask, une
implmentation dAsyncTask qui nous permet de dplacer laccs Internet vers un thread
en arrire-plan :
class FetchForecastTask extends AsyncTask<Location, Void, Void> {
@Override
protected Void doInBackground(Location... locs) {
Location loc=locs[0];

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 308 Dimanche, 8. novembre 2009 12:23 12

308

Lart du dveloppement Android

String url=String.format(format, loc.getLatitude(),


loc.getLongitude());
HttpGet getMethod=new HttpGet(url);
try {
ResponseHandler<String> responseHandler=new BasicResponseHandler();
String responseBody=client.execute(getMethod, responseHandler);
String page=generatePage(buildForecasts(responseBody));
synchronized(this) {
forecast=page;
}
sendBroadcast(broadcast);
}
catch (Throwable t) {
android.util.Log.e("WeatherPlus",
"Exception dans updateForecast()", t);
}
return(null);
}
@Override
protected void onProgressUpdate(Void... inutilis) {
// Inutile ici
}
@Override
protected void onPostExecute(Void inutilis) {
// Inutile ici
}
}

Lessentiel de ce code ressemble au code du projet Weather original il effectue la


requte HTTP, convertit sa rponse en un ensemble dobjets Forecast quil transforme
ensuite en page web. La premire diffrence, outre lintroduction dAsyncTask, est que la
page web est simplement mise en cache dans le service car celui-ci ne peut pas directement la placer dans le WebView de lactivit. La seconde est que lon appelle sendBroadcast(), qui prend une intention en paramtre et lenvoie toutes les parties concernes.
Cette intention a t dclare dans le prologue de la classe :
private Intent broadcast=new Intent(BROADCAST_ACTION);

Ici, BROADCAST_ACTION est simplement une chane statique dont la valeur distingue cette
intention de toutes les autres :
public static final String BROADCAST_ACTION=
"com.commonsware.android.service.ForecastUpdateEvent";
"com.commonsware.android.service.ForecastUpdateEvent";

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 309 Dimanche, 8. novembre 2009 12:23 12

31
Appel dun service
Les services peuvent tre utiliss par nimporte quel composant de lapplication capable
dattendre pendant un certain temps. Ceci inclut les activits, les fournisseurs de contenu
et les autres services. Par contre, ceci ne comprend pas les rcepteurs dintention purs (les
couteurs qui ne font pas partie dune activit) car ils sont automatiquement supprims
aprs le traitement dune intention.
Pour utiliser un service local, vous devez le lancer, obtenir un accs lobjet service, puis
appeler ses mthodes. Vous pouvez ensuite arrter le service lorsque vous nen avez plus
besoin ou, ventuellement, le laisser sarrter de lui-mme. Lutilisation des services
distants est un peu plus complexe et ne sera pas prsente dans ce livre.
Dans ce chapitre, nous tudierons la partie cliente de lapplication Service/WeatherPlus. Lactivit WeatherPlus ressemble normment lapplication Weather originale
comme le montre la Figure 31.1, il sagit simplement dune page web qui affiche les prvisions
mtorologiques.
La diffrence est quici ces prvisions changent lorsque le terminal "se dplace", en refltant
les modifications fournies par le service.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 310 Dimanche, 8. novembre 2009 12:23 12

310

Lart du dveloppement Android

Figure 31.1
Le client du service
WeatherPlus.

Transmission manuelle
Pour dmarrer un service, il suffit dappeler startService() en lui passant lintention qui
indique le service lancer (l encore, le plus simple consiste prciser la classe du service
sil sagit de votre propre service).
Inversement, on larrte par un appel la mthode stopService(), laquelle on passe
lintention fournie lappel startService() correspondant.
Lorsque le service est lanc, on doit communiquer avec lui. Cette communication peut tre
exclusivement ralise via les "extras" que lon a fournis dans lintention ou, sil sagit
dun service local qui offre un singleton, vous pouvez utiliser ce dernier.
Dans le cas de WeatherPlus et de WeatherPlusService, le client utilise le singleton du
service chaque fois quil veut obtenir de nouvelles prvisions :
private void updateForecast() {
try {
String page=WeatherPlusService
.singleton
.getForecastPage();
browser.loadDataWithBaseURL(null, page, "text/html",
"UTF-8", null);
}
catch (final Throwable t) {
goBlooey(t);
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 311 Dimanche, 8. novembre 2009 12:23 12

Chapitre 31

Appel dun service

311

Une partie pineuse de ce code consiste sassurer que le singleton est bien l quand on a
besoin de lui. Lappel startService() tant asynchrone, vous reprenez le contrle tout
de suite, or le service dmarrera bientt, mais pas immdiatement. Dans le cas de
WeatherPlus, on peut sen contenter car on nessaie pas dutiliser le singleton tant que
le service ne nous a pas prvenus de la disponibilit dune prvision, via une intention
de diffusion. Cependant, dans dautres situations, il faudra peut-tre appeler la mthode
postDelayed() dun Handler afin de reporter dune seconde ou deux lutilisation du
service, en esprant que le singleton sera devenu disponible entre-temps.

Capture de lintention
Au chapitre prcdent, nous avons vu comment le service diffusait une intention pour
signaler lactivit WeatherPlus quun mouvement du terminal avait provoqu une modification des prvisions. Nous pouvons maintenant tudier la faon dont lactivit reoit et
utilise cette intention.
Voici les implmentations donResume() et donPause() de WeatherPlus :
@Override
public void onResume() {
super.onResume();
registerReceiver(receiver,
new IntentFilter(WeatherPlusService.BROADCAST_ACTION));
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(receiver);
}

Dans onResume(), nous enregistrons un BroadcastReceiver statique pour recevoir les


intentions qui correspondent laction dclare par le service. Dans onPause(), nous
dsactivons ce rcepteur car nous ne recevrons plus ces intentions pendant que nous sommes
en pause.
Le BroadcastReceiver, de son ct, met simplement les prvisions jour :
private BroadcastReceiver receiver=new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
updateForecast();
}
};

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 312 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 313 Dimanche, 8. novembre 2009 12:23 12

32
Alerter les utilisateurs
avec des notifications
Les messages qui surgissent, les icnes et les "bulles" qui leur sont associes, les icnes
qui bondissent dans la barre dtat, etc. sont utiliss par les programmes pour attirer votre
attention, et parfois pour de bonnes raisons.
Votre tlphone vous alerte aussi probablement pour dautres motifs que la rception dun
appel : batterie faible, alarme programme, notifications de rendez-vous, rception de
SMS ou de courrier lectronique, etc.
Il nest donc pas tonnant quAndroid dispose dun framework complet pour grer ce
genre dvnements, dsigns sous le terme de notifications.

Types davertissements
Un service qui sexcute en arrire-plan doit pouvoir attirer lattention des utilisateurs
lorsquun vnement survient la rception dun courrier, par exemple. En outre, le
service doit galement diriger lutilisateur vers une activit lui permettant dagir en
rponse cet vnement lire le message reu, par exemple. Pour ce type daction,

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 314 Dimanche, 8. novembre 2009 12:23 12

314

Lart du dveloppement Android

Android fournit des icnes dans la barre dtat, des avertissements lumineux et dautres
indicateurs que lon dsigne globalement par le terme de notifications.
Votre tlphone actuel peut possder ce type dicne pour indiquer le niveau de charge de
la batterie, la force du signal, lactivation de Bluetooth, etc. Avec Android, les applications
peuvent ajouter leurs propres icnes dans la barre dtat et faire en sorte quelles napparaissent que lorsque cela est ncessaire (lorsquun message est arriv, par exemple).
Vous pouvez lancer des notifications via le NotificationManager, qui est un service du
systme. Pour lutiliser, vous devez obtenir lobjet service via un appel la mthode
getSystemService (NOTIFICATION_SERVICE) de votre activit. Le NotificationManager
vous offre trois mthodes : une pour avertir (notify()) et deux pour arrter davertir
(cancel() et cancelAll()).
La mthode notify() prend en paramtre une Notification, qui est une structure dcrivant la forme que doit prendre lavertissement. Les sections qui suivent dcrivent tous les
champs publics mis votre disposition (mais souvenez-vous que tous les terminaux ne
les supportent pas ncessairement tous).

Notifications matrielles
Vous pouvez faire clignoter les LED du terminal en mettant lights true et en prcisant
leur couleur (sous la forme dune valeur #ARGB dans ledARGB). Vous pouvez galement
prciser le type de clignotement (en indiquant les dures dallumage et dextinction en
millisecondes dans ledOnMS et ledOffMS).
Vous pouvez faire retentir un son en indiquant une Uri vers un contenu situ, par exemple,
dans un ContentManager (sound). Considrez ce son comme une sonnerie pour votre
application.
Enfin, vous pouvez faire vibrer le terminal en indiquant les alternances de la vibration
(vibrate) en millisecondes dans un tableau de valeurs long. Cette vibration peut tre le
comportement par dfaut ou vous pouvez laisser le choix lutilisateur, au cas o il aurait
besoin dun avertissement plus discret quune sonnerie.

Icnes
Alors que les lumires, les sons et les vibrations sont destins faire en sorte que lutilisateur regarde son terminal, les icnes constituent ltape suivante consistant signaler ce
qui est si important.
Pour configurer une icne pour une Notification, vous devez initialiser deux champs
publics : icon, qui doit fournir lidentifiant dune ressource Drawable reprsentant licne
voulue, et contentIntent, qui indique le PendingIntent qui devra tre dclench
lorsquon clique sur licne. Vous devez vous assurer que le PendingIntent sera captur

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 315 Dimanche, 8. novembre 2009 12:23 12

Chapitre 32

Alerter les utilisateurs avec des notifications

315

ventuellement par le code de votre application pour prendre les mesures ncessaires
afin que lutilisateur puisse grer lvnement qui a dclench la notification.
Vous pouvez galement fournir un texte qui apparatra lorsque licne est place sur la
barre dtat (tickerText).
Pour configurer ces trois composantes, lapproche la plus simple consiste appeler la
mthode setLatestEventInfo(), qui enveloppe leurs initialisations dans un seul appel.

Les avertissements en action


Examinons le projet Notifications/Notify1, notamment sa classe NotifyDemo :
public class NotifyDemo extends Activity {
private static final int NOTIFY_ME_ID=1337;
private Timer timer=new Timer();
private int count=0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.notify);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
TimerTask task=new TimerTask() {
public void run() {
notifyMe();
}
};
timer.schedule(task, 5000);
}
});
btn=(Button)findViewById(R.id.cancel);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
NotificationManager mgr=
(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
mgr.cancel(NOTIFY_ME_ID);
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 316 Dimanche, 8. novembre 2009 12:23 12

316

Lart du dveloppement Android

});
}
private void notifyMe() {
final NotificationManager mgr=
(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification note=new Notification(R.drawable.red_ball,
"Message detat!",
System. currentTimeMillis());
PendingIntent i=PendingIntent.getActivity(this, 0,
new Intent(this, NotifyMessage.class),
0);
note.setLatestEventInfo(this, "Titre de la notification",
"Message de notification", i);
note.number=++count;
mgr.notify(NOTIFY_ME_ID, note);
}
}

Cette activit fournit deux gros boutons, un pour lancer une notification aprs un dlai de
5 secondes, lautre pour annuler cette notification si elle est active. La Figure 32.1 montre
laspect de son interface lorsquelle vient dtre lance.
Figure 32.1
La vue principale de
NotifyDemo.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 317 Dimanche, 8. novembre 2009 12:23 12

Chapitre 32

Alerter les utilisateurs avec des notifications

317

La cration de la notification dans notifyMe() se droule en cinq tapes :


1. Obtenir laccs linstance de NotificationManager.
2. Crer un objet Notification avec une icne (une boule rouge), un message qui
clignote sur la barre dtat lorsque la notification est lance et le temps associ cet
vnement.
3. Crer un PendingIntent qui dclenchera laffichage dune autre activit (NotifyMessage).
4. Utiliser setLatestEventInfo() pour prciser que, lorsquon clique sur la notification, on affiche un titre et un message et que, lorsquon clique sur ce dernier, on lance
le PendingIntent.
5. Demander au NotificationManager dafficher la notification.
Par consquent, si lon clique sur le bouton du haut, la boule rouge apparatra dans la barre
de menu aprs un dlai de 5 secondes, accompagne brivement du message dtat,
comme le montre la Figure 32.2. Aprs la disparition du message, un chiffre saffichera
sur la boule rouge (initialement 1) qui pourrait par exemple indiquer le nombre de
messages non lus.
En cliquant sur la boule rouge, un tiroir apparatra sous la barre dtat. Louverture de
ce tiroir montrera les notifications en suspens, dont la ntre, comme le montre la
Figure 32.3.
Figure 32.2
Notre notification apparat dans la barre dtat,
avec notre message dtat.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 318 Dimanche, 8. novembre 2009 12:23 12

318

Lart du dveloppement Android

Figure 32.3
Le tiroir des notifications
compltement ouvert,
contenant notre notification.

En cliquant sur lentre de la notification dans la barre de notification, on dclenche une


activit trs simple se bornant afficher un message dans une vraie application, cette
activit raliserait un traitement tenant compte de lvnement qui est survenu (prsenter
lutilisateur les nouveaux messages de courrier, par exemple).
Si lon clique sur le bouton dannulation ou sur le bouton "Effacer les notifications" du
tiroir, la balle rouge disparat de la barre dtat.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 319 Dimanche, 8. novembre 2009 12:23 12

Partie

VI

Autres fonctionnalits
dAndroid
CHAPITRE 33.

Accs aux services de localisation

CHAPITRE 34.

Cartographie avec MapView et MapActivity

CHAPITRE 35.

Gestion des appels tlphoniques

CHAPITRE 36.

Recherches avec SearchManager

CHAPITRE 37.

Outils de dveloppement

CHAPITRE 38.

Pour aller plus loin

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 320 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 321 Dimanche, 8. novembre 2009 12:23 12

33
Accs aux services
de localisation
Le GPS est une fonctionnalit trs apprcie des terminaux mobiles actuels car il permet
de vous indiquer votre emplacement gographique tout moment. Bien que lutilisation la
plus frquente du GPS soit la cartographie et lorientation, connatre votre emplacement
vous ouvre de nombreux autres horizons. Vous pouvez, par exemple, mettre en place une
application de chat dynamique o vos contacts sont classs selon leurs emplacements
gographiques, afin de choisir ceux qui sont les plus prs de vous. Vous pouvez galement
"gotaguer" automatiquement les articles que vous postez sur Twitter ou dautres services
similaires.
Cependant, le GPS nest pas le seul moyen didentifier un emplacement gographique :

Lquivalent europen de GPS, Galileo, est encore en cours de mise au point.

La triangulation permet de dterminer votre position en fonction de la force du signal


des antennes relais proches de vous.

La proximit des "hotspots" Wifi, dont les positions gographiques sont connues.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 322 Dimanche, 8. novembre 2009 12:23 12

322

Lart du dveloppement Android

Les terminaux Android peuvent utiliser un ou plusieurs de ces services. En tant que
dveloppeur, vous pouvez demander au terminal de vous indiquer votre emplacement,
ainsi que des dtails sur les fournisseurs disponibles. Vous pouvez mme simuler votre
localisation avec lmulateur pour tester les applications qui ont besoin de cette fonctionnalit.

Fournisseurs de localisation : ils savent o vous vous


cachez
Les terminaux Android peuvent utiliser plusieurs moyens pour dterminer votre emplacement gographique. Certains ont une meilleure prcision que dautres ; certains sont
gratuits, tandis que dautres sont payants ; certains peuvent vous donner des informations
supplmentaires, comme votre altitude par rapport au niveau de la mer ou votre vitesse
courante.
Android a donc abstrait tout cela en un ensemble dobjets LocationProvider. Votre environnement utilisera zro ou plusieurs instances de LocationProvider, une par service de
localisation disponible sur le terminal. Ces fournisseurs ne connaissent pas seulement
votre emplacement mais possdent galement leurs propres caractristiques prcision,
prix, etc.
Vous aurez donc besoin dun LocationManager contenant lensemble des LocationProvider pour savoir quel est le LocationProvider qui convient votre cas particulier. Votre
application devra galement disposer de la permission ACCESS_LOCATION ; sinon les diffrentes API de localisation choueront cause dune violation de scurit. Selon les fournisseurs de localisation que vous voulez utiliser, vous pourrez galement avoir besoin
dautres permissions, comme ACCESS_COARSE_LOCATION ou ACCESS_FINE_LOCATION.

Se trouver soi-mme
Lopration la plus vidente dun fournisseur de localisation consiste trouver votre
emplacement actuel. Pour ce faire, vous avez besoin dun LocationManager, que vous
obtiendrez par un appel getSystemService(LOCATION_SERVICE) partir de votre activit ou service, en transtypant le rsultat pour obtenir un LocationManager.
Ltape suivante consiste obtenir le nom du LocationProvider que vous voulez utiliser.
Pour ce faire, deux possibilits soffrent vous :

demander lutilisateur de choisir un fournisseur ;

trouver le fournisseur qui convient le mieux en fonction dun ensemble de critres.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 323 Dimanche, 8. novembre 2009 12:23 12

Chapitre 33

Accs aux services de localisation

323

Si vous choisissez la premire approche, un appel la mthode getProviders() du


LocationManager vous donnera une liste de fournisseurs que vous pouvez prsenter
lutilisateur pour quil fasse son choix.
Vous pouvez galement crer et initialiser un objet Criteria, en prcisant ce que vous
attendez dun LocationProvider. Par exemple :

setAltitudeRequired() pour indiquer si vous avez besoin ou non de connatre votre


altitude ;

setAccuracy() pour fixer un niveau de prcision minimal de la position, en mtres ;

setCostAllowed() pour indiquer si le fournisseur doit tre gratuit ou non (cest--dire


sil peut impliquer un paiement de la part de lutilisateur du terminal).

Lorsque lobjet Criteria a t rempli, appelez la mthode getBestProvider() de votre


LocationManager et Android passera les critres en revue pour vous donner la meilleure
rponse. Tous ces critres peuvent ne pas tre vrifis part celui concernant le prix, ils
peuvent tous tre ignors si rien ne correspond.
Pour effectuer des tests, vous pouvez galement indiquer directement dans votre code le
nom dun LocationProvider (gps, par exemple).
Lorsque vous connaissez le nom du LocationProvider, vous pouvez appeler getLastKnownPosition() pour trouver votre dernire position. Notez, cependant, que cette
"dernire position" peut tre obsolte (si, par exemple, le tlphone tait teint) ou valoir
null si aucune position na encore t enregistre pour ce fournisseur. En revanche,
getLastKnownPosition() est gratuite et ne consomme pas de ressource car le fournisseur
na pas besoin dtre activ pour connatre cette valeur.
Ces mthodes renvoient un objet Location qui vous indiquera la latitude et la longitude
du terminal en degrs des valeurs double de Java. Si le fournisseur donne dautres informations, vous pouvez les rcuprer laide des mthodes suivantes :

hasAltitude() indique sil y a une valeur pour laltitude et getAltitude() renvoie


laltitude en mtres.

hasBearing() indique sil y a une information dorientation (une valeur de compas) et


getBearing() renvoie cette valeur en degrs par rapport au vrai nord.

hasSpeed() indique si la vitesse est connue et getSpeed() la renvoie en mtres par


seconde.

Ceci dit, une approche plus frquente pour obtenir lobjet Location partir dun LocationProvider consiste senregistrer pour les modifications de la position du terminal,
comme expliqu dans la section suivante.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 324 Dimanche, 8. novembre 2009 12:23 12

324

Lart du dveloppement Android

Se dplacer
Tous les fournisseurs de localisation ne rpondent pas immdiatement. GPS, par exemple,
ncessite lactivation dun signal et la rception des satellites (cest ce que lon appelle un
"fix GPS") avant de pouvoir connatre sa position. Cest la raison pour laquelle Android ne
fournit pas de mthode getMeMyCurrentLocationNow(). Ceci combin avec le fait que
les utilisateurs puissent vouloir que leurs mouvements soient pris en compte dans lapplication, vous comprendrez pourquoi il est prfrable denregistrer les modifications de la
position et les utiliser pour connatre la position courante.
Les applications Weather et WeatherPlus montrent comment enregistrer ces mises jour
en appelant la mthode requestLocationUpdates() de lobjet LocationManager.
Cette mthode prend quatre paramtres :
1. Le nom du fournisseur de localisation que vous souhaitez utiliser.
2. Le temps, en millisecondes, qui doit scouler avant que lon puisse obtenir une mise
jour de la position.
3. Le dplacement minimal du terminal en mtres pour que lon puisse obtenir une mise
jour de la position.
4. Un LocationListener qui sera prvenu des vnements lis la localisation, comme
le montre le code suivant :
LocationListener onLocationChange=new LocationListener() {
public void onLocationChanged(Location location) {
updateForecast(location);
}
public void onProviderDisabled(String provider) {
// Exige par linterface, mais inutilise
}
public void onProviderEnabled(String provider) {
// Exige par linterface, mais inutilise
}
public void onStatusChanged(String provider, int status,
Bundle extras) {
// Exige par linterface, mais inutilise
}
};

Ici, nous appelons simplement updateForecast() en lui passant lobjet Location fourni
lappel de la mthode de rappel onLocationChanged(). Comme on la vu au Chapitre 30, limplmentation dupdateForecast() construit une page web contenant les
prvisions mtorologiques pour lemplacement courant et envoie un message de diffusion
afin que lactivit sache quune mise jour est disponible.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 325 Dimanche, 8. novembre 2009 12:23 12

Chapitre 33

Accs aux services de localisation

325

Lorsque lon na plus besoin des mises jour, on appelle removeUpdates() avec le
LocationListener que lon avait enregistr.

Est-on dj arriv ? Est-on dj arriv ?


Est-on dj arriv ?
Parfois, on veut savoir non pas o lon se trouve ni mme o lon va, mais si lon est l o
lon voulait aller. Il pourrait sagir dune destination finale ou dune tape dans un ensemble
de directions pour pouvoir indiquer le virage suivant, par exemple.
Dans ce but, LocationManager fournit la mthode addProximityAlert(), qui enregistre
un PendingIntent qui sera dclench lorsque le terminal se trouvera une certaine
distance dun emplacement donn. La mthode addProximityAlert() attend les paramtres
suivants :

La latitude et la longitude de la position qui nous intresse.

Un rayon prcisant la proximit avec la position pour que lintention soit leve.

Une dure denregistrement en millisecondes passe cette priode, lenregistrement


expirera automatiquement. Une valeur de 1 indique que lenregistrement sera maintenu jusqu ce que vous le supprimiez manuellement via un appel removeProximityAlert().

Le PendingIntent quil faudra lever lorsque le terminal se trouve dans la "zone de tir"
dfinie par la position et le rayon.

Notez quil nest pas garanti que vous receviez une intention sil y a eu une interruption
dans les services de localisation ou si le terminal nest pas dans la zone cible pendant le
temps o lalerte de proximit est active. Si la position, par exemple, est trop proche du but
et que le rayon est un peu trop rduit, le terminal peut ne faire que longer le bord de la
zone cible ou y passer si rapidement que sa position ne sera pas enregistre pendant quil
est dans la zone.
Il vous appartient de faire en sorte quune activit ou un rcepteur dintention rponde
lintention que vous avez enregistre pour lalerte de proximit. Cest galement vous de
dterminer ce qui doit se passer lorsque cette intention arrive : configurer une notification
(faire vibrer le terminal, par exemple), enregistrer linformation dans un fournisseur de
contenu, poster un message sur un site web, etc. Notez que vous recevrez lintention
chaque fois que la position est enregistre et que vous tes dans la zone cible pas
simplement lorsque vous y entrez. Par consquent, vous la recevrez plusieurs fois le
nombre doccurrences dpend de la taille de la zone et de la vitesse de dplacement du
terminal.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 326 Dimanche, 8. novembre 2009 12:23 12

326

Lart du dveloppement Android

Tester... Tester...
Lmulateur dAndroid ne permet pas dobtenir un "fix GPS", de trianguler votre position
partir des antennes relais ni de dduire votre position partir des signaux Wifi voisins. Si
vous voulez simuler un terminal qui se dplace, il faut donc trouver un moyen de fournir
lmulateur des donnes de localisation simules.
Pour une raison inconnue, ce domaine a subi des changements importants au cours de
lvolution dAndroid. une poque, il tait possible de fournir des donnes de localisation simules une application, ce qui tait trs pratique pour les tests et les dmonstrations
mais, malheureusement, cette possibilit a disparu partir dAndroid 1.0.
Ceci dit, DDMS (Dalvik Debug Monitor Service) permet de fournir ce type de donnes.
Il sagit dun programme externe, spar de lmulateur, qui peut fournir ce dernier des
points demplacements ou des routes compltes, dans diffrents formats. DDMS est dcrit
en dtail au Chapitre 37.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 327 Dimanche, 8. novembre 2009 12:23 12

34
Cartographie avec
MapView et MapActivity
Google Maps est lun des services les plus connus de Google aprs le moteur de recherche, bien entendu. Avec lui, vous pouvez tout trouver, de la pizzeria la plus proche au trajet
menant de Toulouse Paris en passant par les vues dtailles des rues (Street View) et les
images satellites.
Android intgre Google Maps : cette activit de cartographie est directement disponible
partir du menu principal mais, surtout, les dveloppeurs ont leur disposition les classes
MapView et MapActivity pour intgrer des cartes gographiques dans leurs applications.
Grce elles, ils peuvent non seulement contrler le niveau du zoom, permettre aux utilisateurs de faire dfiler la carte, mais galement utiliser les services de localisation pour
marquer lemplacement du terminal et indiquer son dplacement.
Heureusement, cette intgration est assez simple et vous pouvez exploiter toute sa puissance si vous le souhaitez.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 328 Dimanche, 8. novembre 2009 12:23 12

328

Lart du dveloppement Android

Termes dutilisation
Google Maps, notamment lorsquil est intgr dans des applications tierces, ncessite le
respect dun assez grand nombre de termes juridiques. Parmi ceux-ci se trouvent des clauses que vous trouverez peut-tre insupportables. Si vous dcidez dutiliser Google Maps,
prenez soin de bien lire tous ces termes afin dtre sr que lutilisation que vous comptez
en faire ne les viole pas. Nous vous conseillons fortement de demander lavis dun
conseiller juridique en cas de doute.
En outre, ne dlaissez pas les autres possibilits de cartographie qui reposent sur dautres
sources de donnes gographiques, comme OpenStreetMap1.

Empilements
partir dAndroid 1.5, Google Maps ne fait plus partie du SDK proprement parler mais
a t dplac dans les API supplmentaires de Google, qui sont des extensions au SDK de
base. Ce systme dextension fournit des points dentre aux autres sous-systmes qui
peuvent se trouver sur certains terminaux mais pas sur dautres.
En ralit, Google Maps ne fait pas partie du projet open-source Android, et il existera
ncessairement des terminaux qui nen disposeront pas cause des problmes de licence.
Dans lensemble, le fait que Google Maps soit une extension naffectera pas votre dveloppement habituel condition de ne pas oublier les points suivants :

Vous devrez crer votre projet pour quil utilise la cible 3 (-t 3), afin dtre sr que les
API de Google Maps sont disponibles.

Pour tester lintgration de Google Maps, vous aurez galement besoin dun AVD qui
utilise la cible 3 (-t 3).

Inversement, pour tester votre application dans un environnement Android 1.5 sans
Google Maps, vous devrez crer un AVD qui utilise la cible 2 (-t 2).

Les composants essentiels


Pour insrer une carte gographique dans une application, le plus simple consiste crer
une sous-classe de MapActivity. Comme ListActivity, qui enveloppe une partie des
dtails cachs derrire une activit domine par une ListView, MapActivity gre une
partie de la configuration dune activit domine par une MapView.

1. http://www.openstreetmap.org/.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 329 Dimanche, 8. novembre 2009 12:23 12

Chapitre 34

Cartographie avec MapView et MapActivity

329

Dans le fichier de layout de la sous-classe de MapActivity, vous devez ajouter un lment


qui, actuellement, sappelle com.google.android.maps.MapView. Il sagit ici dun nom
totalement dvelopp qui ajoute le nom de paquetage complet au nom de la classe (cette
notation est ncessaire car MapView ne se trouve pas dans lespace de noms
com.google.android.widget). Vous pouvez donner la valeur que vous souhaitez
lattribut android:id du widget MapView et grer tous les dtails lui permettant de safficher
correctement ct des autres widgets.
Vous devez cependant prciser les attributs suivants :

android:apiKey. Dans une version de lapplication en production, cet attribut doit


contenir une cl de lAPI Google Maps (voir plus loin).

android:clickable = "true". Si vous voulez que les utilisateurs puissent cliquer


sur la carte et la faire dfiler.

Voici, par exemple, le contenu du fichier layout principal de lapplication Maps/NooYawk :


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.google.android.maps.MapView android:id="@+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="<VOTRE_CL_API>"
android:clickable="true" />
<LinearLayout android:id="@+id/zoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" />
</RelativeLayout>

Nous prsenterons ces mystrieux LinearLayout zoom et apiKey plus bas dans ce chapitre.
Vous devez galement ajouter deux informations supplmentaires votre fichier AndroidManifest.xml :

Les permissions INTERNET et ACCESS_COARSE_LOCATION.

Dans llment <application>, ajoutez un lment <uses-library> avec lattribut


android:name = "com.google.android.maps" pour indiquer que vous utilisez lune
des API facultatives dAndroid.

Voici le fichier AndroidManifest.xml du projet NooYawk:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.maps">
<uses-permission android:name="android.permission.INTERNET" />

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 330 Dimanche, 8. novembre 2009 12:23 12

330

Lart du dveloppement Android

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />


<application android:label="@string/app_name">
<uses-library android:name="com.google.android.maps" />
<activity android:name=".NooYawk" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Avec la sous-classe de MapActivity, cest peu prs tout ce dont vous avez besoin pour
dbuter. Si vous ne faites rien dautre, que vous compiliez ce projet et que vous linstalliez
dans lmulateur, vous obtiendrez une belle carte du monde. Notez, cependant, que
MapActivity est une classe abstraite et que vous devez donc implmenter la mthode
isRouteDisplayed() pour prciser si vous fournissez ou non une gestion des itinraires.
En thorie, lutilisateur doit pouvoir faire dfiler la carte en utilisant le pad directionnel.
Cependant, ce nest pas trs pratique lorsque lon a le monde entier dans sa main...
Une carte du monde ntant pas trs utile en elle-mme, nous devons lui ajouter quelques
fonctionnalits.

Testez votre contrle


Pour trouver votre widget MapView, il suffit, comme dhabitude, dappeler la mthode
findViewById(). Le widget lui-mme fournit la mthode getMapController(). Entre le
MapView et le MapController, vous disposez dun bon nombre de possibilits pour dterminer ce quaffiche la carte et la faon dont elle se comporte ; les sections suivantes
prsentent le zoom et le centrage, qui sont srement celles que vous utiliserez le plus.

Zoom
La carte du monde avec laquelle vous dmarrez est plutt vaste. Sur un tlphone, on
prfre gnralement consulter une carte ayant une tendue plus rduite quelques pts
de maisons, par exemple.
Vous pouvez contrler directement le niveau du zoom grce la mthode setZoom() de
MapController. Celle-ci attend un paramtre entier reprsentant le niveau du zoom, o 1
reprsente la vue du monde entier et 21, le plus fort grossissement que vous pouvez obtenir. Chaque niveau double la rsolution effective par rapport au niveau prcdent : au
niveau 1, lquateur fait 256 pixels de large et il en fait 268 435 456 au niveau 21. Lcran
du tlphone nayant srement pas autant de pixels ; lutilisateur ne verra donc quune
petite partie de la carte centre sur un endroit du globe. Le niveau 16 montrera plusieurs

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 331 Dimanche, 8. novembre 2009 12:23 12

Chapitre 34

Cartographie avec MapView et MapActivity

331

pts de maisons dans chaque dimension et constitue gnralement un bon point de dpart
pour vos essais.
Si vous souhaitez que les utilisateurs aient le droit de changer le niveau du zoom, utilisez
lappel setBuiltInZoomControls(true) : il pourra alors utiliser les contrles de zoom
qui se trouvent en bas de la carte.

Centrage
Gnralement, quel que soit le niveau du zoom, vous voudrez contrler ce qui est affich
sur la carte : la position courante de lutilisateur ou un emplacement sauvegard avec
dautres donnes de votre activit, par exemple. Pour changer la position de la carte, appelez
la mthode setCenter() de MapController.
Cette mthode prend un objet GeoPoint en paramtre. Un GeoPoint reprsente un emplacement exprim par une latitude et une longitude. En ralit, il les stocke sous la forme
dentiers en multipliant leurs vraies valeurs par 1E6, ce qui permet dconomiser un peu de
mmoire par rapport des float ou des double et dacclrer un peu la conversion dun
GeoPoint en position sur la carte. En revanche, vous ne devez pas oublier ce facteur de
1E6.

Terrain accident
Tout comme sur votre ordinateur de bureau, vous pouvez afficher les images satellites avec
Google Maps et Android.
MapView offre la mthode toggleSatellite(), qui, comme son nom lindique, permet
dactiver ou de dsactiver la vue satellite de la surface prsente sur la carte. Vous pouvez
faire en sorte de laisser lutilisateur le soin de faire ce choix partir dun menu ou,
comme dans NooYawk, via des touches :
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_S) {
map.setSatellite(!map.isSatellite());
return(true);
}
else if (keyCode == KeyEvent.KEYCODE_Z) {
map.displayZoomControls(true);
return(true);
}
return(super.onKeyDown(keyCode, event));
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 332 Dimanche, 8. novembre 2009 12:23 12

332

Lart du dveloppement Android

Couches sur couches


Si vous avez dj utilis la version complte de Google Maps, vous avez srement dj vu
que lon pouvait dposer des choses sur la carte elle-mme : les "repres", par exemple,
qui indiquent les emplacements des points dintrt proches de la position que vous avez
demande. En termes de carte et galement pour la plupart des diteurs graphiques
srieux , ces repres sont placs sur une couche distincte de celle de la carte elle-mme et
ce que vous voyez au final est la superposition de ces deux couches.
Android permet de crer de telles couches, afin de marquer les cartes en fonction des choix
de lutilisateur et des besoins de votre application. NooYawk, par exemple, utilise une
couche pour montrer les emplacements des immeubles slectionns dans Manhattan.

Classes Overlay
Toute couche ajoute votre carte doit tre implmente comme une sous-classe dOverlay.
Si vous voulez simplement ajouter des repres, vous pouvez utiliser la sous-classe ItemizedOverlay, qui vous simplifiera la tche.
Pour attacher une couche votre carte, il suffit dappeler la mthode getOverlays() de
votre objet MapView et dajouter votre instance dOverlay avec add() :
marker.setBounds(0, 0, marker.getIntrinsicWidth(),
marker.getIntrinsicHeight());
map.getOverlays().add(new SitesOverlay(marker));

Nous expliquerons un peu plus loin le rle de marker.

Affichage dItemizedOverlay
Comme son nom lindique, ItemizedOverlay permet de fournir une liste de points dintrt (des instances dOverlayItem) pour les afficher sur la carte. La couche gre ensuite
lessentiel du dessin pour vous, mais vous devez toutefois effectuer les oprations
suivantes :

Drivez votre sous-classe (SitesOverlay, dans notre exemple) dItemizedOverlay<OverlayItem>.

Dans le constructeur, mettez en place la liste des instances OverlayItem et appelez


populate() lorsquelles sont prtes tre utilises par la couche.

Implmentez size() pour quelle renvoie le nombre dlments qui devront tre grs
par la couche.

Redfinissez createItem() pour quelle renvoie linstance OverlayItem correspondant


lindice qui lui est pass en paramtre.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 333 Dimanche, 8. novembre 2009 12:23 12

Chapitre 34

Cartographie avec MapView et MapActivity

333

Lors de linstanciation de la sous-classe dItemizedOverlay, fournissez-lui un objet


Drawable reprsentant licne par dfaut de chaque lment (une pinglette, par exemple).

Le marker que lon passe au constructeur de NooYawk est le Drawable utilis en dernier
recours il affiche une pinglette.
Vous pouvez galement redfinir draw() pour mieux grer lombre de vos marqueurs.
Bien que la carte fournisse une ombre, il peut tre utile de laider un peu en lui indiquant
o se trouve le bas de licne, afin quelle puisse en tenir compte pour lombrage.
Voici, par exemple, le code de la classe SitesOverlay :
private class SitesOverlay extends ItemizedOverlay<OverlayItem> {
private List<OverlayItem> items=new ArrayList<OverlayItem>();
private Drawable marker=null;
public SitesOverlay(Drawable marker) {
super(marker);
this.marker=marker;
items.add(new OverlayItem(getPoint(40.748963847316034,
-73.96807193756104),
"UN", "Nations Unies"));
items.add(new OverlayItem(getPoint(40.76866299974387,
-73.98268461227417),
"Lincoln Center",
"La maison du Jazz"));
items.add(new OverlayItem(getPoint(40.765136435316755,
-73.97989511489868),
"Carnegie Hall",
"Entranezvous avant dy jouer !"));
items.add(new OverlayItem(getPoint(40.70686417491799,
-74.01572942733765),
"The Downtown Club",
"Le lieu dorigine du trophe Heisman"));
populate();
}
@Override
protected OverlayItem createItem(int i) {
return(items.get(i));
}
@Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
super.draw(canvas, mapView, shadow);
boundCenterBottom(marker);
}
@Override
protected boolean onTap(int i) {
Toast.makeText(NooYawk.this,
items.get(i).getSnippet(),

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 334 Dimanche, 8. novembre 2009 12:23 12

334

Lart du dveloppement Android

Toast.LENGTH_SHORT). show();
return(true);
}
@Override
public int size() {
return(items.size());
}
}

Gestion de lcran tactile


Une sous-classe dOverlay peut galement implmenter onTap()pour tre prvenue lorsque lutilisateur touche la carte afin que la couche ajuste ce quelle affiche. Dans Google
Maps, cliquer sur une pinglette fait surgir une bulle dinformation consacre lemplacement marqu, par exemple : grce onTap(), vous pouvez obtenir le mme rsultat avec
Android.
La mthode onTap() dItemizedOverlay prend en paramtre lindice de lobjet OverlayItem sur lequel on a cliqu. Cest ensuite vous de traiter cet vnement.
Dans le cas de la classe SitesOverlay que nous venons de prsenter, le code donTap()
est le suivant :
@Override
protected boolean onTap(int i) {
Toast.makeText(NooYawk.this,
items.get(i).getSnippet(),
Toast.LENGTH_SHORT).show();
return(true);
}

Ici, on lve simplement un Toast contenant le texte associ lOverlayItem et lon


renvoie true pour indiquer que lon a gr le toucher de cet objet.

Moi et MyLocationOverlay
Android dispose dune couche intgre permettant de grer deux scnarios classiques :

laffichage de votre position sur la carte, en fonction du GPS ou dun autre fournisseur
de localisation ;

laffichage de la direction vers laquelle vous vous dirigez, en fonction de la boussole


intgre lorsquelle est disponible.

Il vous suffit pour cela de crer une instance de MyLocationOverlay, de lajouter la liste
des couches de votre MapView et dactiver et de dsactiver ces fonctionnalits aux
moments opportuns.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 335 Dimanche, 8. novembre 2009 12:23 12

Chapitre 34

Cartographie avec MapView et MapActivity

335

La notion de "moments opportuns" est lie lconomie de la batterie. Comme il ny a


aucune raison de mettre jour des emplacements ou des directions lorsque lactivit est en
pause, il est conseill dactiver ces fonctionnalits dans onResume() et de les dsactiver
dans onPause().
Pour que NooYawk affiche une boussole dans MyLocationOverlay, par exemple, nous
devons dabord crer la couche et lajouter la liste des couches :
me=new MyLocationOverlay(this, map);
map.getOverlays().add(me);

Puis nous activons et dsactivons cette boussole lorsque cela est ncessaire :
@Override
public void onResume() {
super.onResume();
me.enableCompass();
}
@Override
public void onPause() {
super.onPause();
me.disableCompass();
}

La cl de tout
Si vous compilez le projet NooYawk et que vous linstalliez dans votre mulateur, vous
verrez srement un cran montrant une grille et deux pinglettes, mais pas de carte.
La raison en est que la cl de lAPI dans le code source nest pas valide pour votre
machine de dveloppement. Vous devez donc produire votre propre cl pour lutiliser avec
votre application.
Le site web dAndroid1 donne toutes les instructions ncessaires pour produire ces cls,
que ce soit pour le dveloppement ou pour la production. Pour rester brefs, nous nous intresserons ici au cas particulier de lexcution de NooYawk dans votre mulateur. Vous
devez effectuer les tapes suivantes :
1. Allez sur la page dinscription pour la cl de lAPI et lisez les termes dutilisation.
2. Relisez ces termes et soyez absolument sr que vous les approuvez.
3. Recherchez la signature MD5 du certificat utilis pour signer vos applications en
mode debug (voir ci-aprs).
1. http://code.google.com/android/toolbox/apis/mapkey.html.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 336 Dimanche, 8. novembre 2009 12:23 12

336

Lart du dveloppement Android

4. Sur la page dinscription pour la cl de lAPI, collez cette signature MD5 et envoyez le
formulaire.
5. Sur la page de rponse, copiez la cl de lAPI et collez-la dans la valeur de lattribut
android:apiKey du layout de votre MapView.
La partie la plus complique consiste trouver la signature MD5 du certificat utilis pour
signer vos applications en mode debug... et une bonne partie de cette complexit consiste
comprendre le concept.
Toutes les applications Android sont signes laide dune signature numrique produite
partir dun certificat. Vous recevez automatiquement un certificat de dbogage lorsque
vous installez le SDK et il faut suivre un autre processus pour crer un certificat autosign
utilisable avec vos applications en production. Ce processus ncessite dutiliser les outils
keytool et jarsigner de Java. Pour obtenir votre cl dAPI, vous navez besoin que de
keytool.
Si vous utilisez OS X ou Linux, faites la commande suivante pour obtenir la signature MD5 de votre certificat de dbogage :
keytool -list -alias androiddebugkey -keystore ~/.android/debug.keystore
-storepass android -keypass android

Sur les autres plates-formes de dveloppement, vous devrez remplacer la valeur de -keystore
par lemplacement sur votre machine et votre compte utilisateur :

Windows XP :
C:\Documents
et
Settings\<utilisateur>\Local
Settings\ApplicationData\Android\debug.keystore.

Windows Vista : C:\Users\<utilisateur>\AppData\Local\Android\debug.keystore (o <utilisateur> est le nom de votre compte).

La seconde ligne du rsultat qui saffiche contient votre signature MD5, qui est une suite
de paires de chiffres hexadcimaux spares par des caractres deux-points.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 337 Dimanche, 8. novembre 2009 12:23 12

35
Gestion des appels
tlphoniques
La plupart des terminaux Android, si ce nest tous, sont des tlphones. Leurs utilisateurs
sattendent donc pouvoir tlphoner et recevoir des appels et, si vous le souhaitez, vous
pouvez les y aider :

Vous pourriez crer une interface pour une application de gestion des ventes ( la
Salesforce.com) en offrant la possibilit dappeler les vendeurs dun simple clic, sans
que lutilisateur soit oblig de mmoriser ces contacts la fois dans lapplication et
dans son rpertoire tlphonique.

Vous pourriez dvelopper une application de rseau social avec une liste de numros
de tlphone qui volue constamment : au lieu de "synchroniser" ces contacts avec
ceux du tlphone, lutilisateur pourrait les appeler directement partir de cette application.

Vous pourriez crer une interface personnalise pour le systme de contacts existants,
ventuellement pour que les utilisateurs mobilit rduite (telles les personnes ges)
puissent disposer de gros boutons pour faciliter la composition des appels.

Quoi quil en soit, Android vous permet de manipuler le tlphone comme nimporte
quelle autre composante du systme.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 338 Dimanche, 8. novembre 2009 12:23 12

338

Lart du dveloppement Android

Le Manager
Pour tirer le meilleur parti de lAPI de tlphonie, utilisez la classe TelephonyManager,
qui permet notamment de :

dterminer si le tlphone est en cours dutilisation, via sa mthode getCallState(),


qui renvoie les valeurs CALL_STATE_IDLE (tlphone non utilis), CALL_STATE_RINGING
(appel en cours de connexion) et CALL_STATE_OFFHOOK (appel en cours) ;

trouver lidentifiant de la carte SIM avec getSubscriberId();

connatre le type du tlphone (GSM, par exemple) avec getPhoneType() ou celui de


la connexion (comme GPRS, EDGE) avec getNetworkType().

Appeler
Pour effectuer un appel partir d'une application, en utilisant par exemple un numro que
vous avez obtenu par votre propre service web, crez une intention ACTION_DIAL avec une
Uri de la forme tel:NNNNN (o NNNNN est le numro de tlphone appeler) et utilisez
cette intention avec startActivity(). Cela ne lancera pas l'appel, mais activera l'activit
du combin, partir duquel l'utilisateur pourra alors appuyer sur un bouton pour effectuer
l'appel.
Voici, par exemple, un fichier de disposition simple mais efficace, extrait du projet Phone/
Dialer :
<?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"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Numero : "
/>
<EditText android:id="@+id/number"
android:layout_width="fill_parent"
android:layout_height="wrap_content"

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 339 Dimanche, 8. novembre 2009 12:23 12

Chapitre 35

Gestion des appels tlphoniques

339

android:cursorVisible="true"
android:editable="true"
android:singleLine="true"
/>
</LinearLayout>
<Button android:id="@+id/dial"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Appeler !"
/>
</LinearLayout>

Nous utilisons simplement un champ de saisie pour entrer un numro de tlphone et un


bouton pour appeler ce numro.
Le code Java se contente de lancer le combin en utilisant le numro saisi dans le champ :
package com.commonsware.android.dialer;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class DialerDemo extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
final EditText number=(EditText)findViewById(R.id.number);
Button dial=(Button)findViewById(R.id.dial);
dial.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
String toDial="tel:" + number.getText().toString();
startActivity(new Intent(Intent.ACTION_DIAL,
Uri.parse(toDial)));
}
});
}
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 340 Dimanche, 8. novembre 2009 12:23 12

340

Lart du dveloppement Android

Comme le montre la Figure 35.1, linterface de cette activit nest pas trs impressionnante.
Figure 35.1
Lapplication
DialerDemo lors
de son lancement.

Cependant, le combin tlphonique que lon obtient en cliquant sur le bouton "Appeler !"
est plus joli, comme le montre la Figure 35.2.
Figure 35.2
Lactivit Dialer
dAndroid lance partir
de DialerDemo.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 341 Dimanche, 8. novembre 2009 12:23 12

36
Recherches avec
SearchManager
Lune des socits lorigine de lalliance Open Handset Google dispose dun petit
moteur de recherche dont vous avez sans doute entendu parler. Il nest donc pas tonnant
quAndroid intgre quelques fonctionnalits de recherche.
Plus prcisment, les recherches avec Android ne sappliquent pas seulement aux donnes
qui se trouvent sur lappareil, mais galement aux sources de donnes disponibles sur
Internet.
Vos applications peuvent participer ce processus en dclenchant elles-mmes des recherches
ou en autorisant que lon fouille dans leurs donnes.
Info

Cette fonctionnalit tant assez rcente dans Android, les API risquent dtre
modifies : surveillez les mises jour.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 342 Dimanche, 8. novembre 2009 12:23 12

342

Lart du dveloppement Android

La chasse est ouverte


Android dispose de deux types de recherches : locales et globales. Les premires effectuent la recherche dans lapplication en cours tandis que les secondes utilisent le moteur de
Google pour faire une recherche sur le Web. Chacune delles peut tre lance de diffrentes
faons :

Vous pouvez appeler onSearchRequested() partir dun bouton ou dun choix de


menu afin de lancer une recherche locale (sauf si vous avez redfini cette mthode dans
votre activit).

Vous pouvez appeler directement startSearch() pour lancer une recherche locale ou
globale en fournissant ventuellement une chane de caractres comme point de dpart.

Vous pouvez faire en sorte quune saisie au clavier dclenche une recherche locale avec
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL) ou globale avec setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL).

Dans tous les cas, la recherche apparat comme un ensemble de composants graphiques disposs en haut de lcran, votre activit apparaissant en flou derrire eux (voir
Figures 36.1 et 36.2).
Figure 36.1
Les composants
de la recherche locale
dAndroid.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 343 Dimanche, 8. novembre 2009 12:23 12

Chapitre 36

Recherches avec SearchManager

343

Figure 36.2
Les composants de la
recherche globale
dAndroid avec une liste
droulante montrant les
recherches prcdentes.

Recherches personnelles
terme, il existera deux variantes de recherches disponibles :

les recherches de type requte, o la chane recherche par lutilisateur est passe une
activit qui est responsable de la recherche et de laffichage des rsultats ;

les recherches de type filtre, o la chane recherche par lutilisateur est passe une
activit chaque pression de touche et o lactivit est charge de mettre jour une
liste des correspondances.

Cette dernire approche tant encore en cours de dveloppement, intressons-nous la


premire.

Cration de lactivit de recherche


Pour quune application puisse proposer des recherches de type requte, la premire chose
faire consiste crer une activit de recherche. Bien quil soit possible quune mme
activit puisse tre ouverte partir du lanceur et partir dune recherche, il savre que
cela trouble un peu les utilisateurs. En outre, utiliser une activit spare est plus propre
dun point de vue technique.
Lactivit de recherche peut avoir laspect que vous souhaitez. En fait, part examiner les
requtes, elle ressemble, se comporte et rpond comme toutes les autres activits du
systme. La seule diffrence est quune activit de recherche doit vrifier les intentions

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 344 Dimanche, 8. novembre 2009 12:23 12

344

Lart du dveloppement Android

fournies onCreate() (via getIntent()) et onNewIntent() pour savoir si lune delles


est une recherche, auquel cas elle effectue la recherche et affiche le rsultat.
Lapplication Search/Lorem, par exemple, commence comme un clone de lapplication
du Chapitre 8, qui affichait une liste des mots pour dmontrer lutilisation du conteneur
ListView. Ici, nous la modifions pour pouvoir rechercher les mots qui contiennent une
chane donne.
Lactivit principale et lactivit de recherche partagent un layout form dune ListView
et dun TextView montrant lentre slectionne :
<?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/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
/>
</LinearLayout>

Lessentiel du code des activits se trouve dans une classe abstraite LoremBase :
abstract public class LoremBase extends ListActivity {
abstract ListAdapter makeMeAnAdapter(Intent intent);
private static final int LOCAL_SEARCH_ID = Menu.FIRST+1;
private static final int GLOBAL_SEARCH_ID = Menu.FIRST+2;
private static final int CLOSE_ID = Menu.FIRST+3;
TextView selection;
ArrayList<String> items=new ArrayList<String>();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
try {
XmlPullParser xpp=getResources().getXml(R.xml.words);
while (xpp.getEventType()!=XmlPullParser.END_DOCUMENT) {

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 345 Dimanche, 8. novembre 2009 12:23 12

Chapitre 36

Recherches avec SearchManager

345

if (xpp.getEventType()==XmlPullParser.START_TAG) {
if (xpp.getName().equals("word")) {
items.add(xpp.getAttributeValue(0));
}
}
xpp.next();
}
}
catch (Throwable t) {
Toast
.makeText(this, "Echec de la requete : " + t.toString(), 4000)
.show();
}
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
onNewIntent(getIntent());
}
@Override
public void onNewIntent(Intent intent) {
ListAdapter adapter=makeMeAnAdapter(intent);
if (adapter==null) {
finish();
}
else {
setListAdapter(adapter);
}
}
public void onListItemClick(ListView parent, View v, int position,
long id) {
selection.setText(items.get(position).toString());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, LOCAL_SEARCH_ID, Menu.NONE, "Recherche locale")
.setIcon(android.R.drawable.ic_search_category_default);
menu.add(Menu.NONE, GLOBAL_SEARCH_ID, Menu.NONE, "Recherche globale")
.setIcon(R.drawable.search)
.setAlphabeticShortcut(SearchManager.MENU_KEY);
menu.add(Menu.NONE, CLOSE_ID, Menu.NONE, "Fermeture")
.setIcon(R.drawable.eject)
.setAlphabeticShortcut(f);
return(super.onCreateOptionsMenu(menu));
}

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 346 Dimanche, 8. novembre 2009 12:23 12

346

Lart du dveloppement Android

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case LOCAL_SEARCH_ID:
onSearchRequested();
return(true);
case GLOBAL_SEARCH_ID:
startSearch(null, false, null, true);
return(true);
case CLOSE_ID:
finish();
return(true);
}
return(super.onOptionsItemSelected(item));
}
}

Cette activit prend en charge tout ce qui est li laffichage dune liste de mots, y
compris lextraction des mots partir du fichier XML. En revanche, elle ne fournit pas le
ListAdapter placer dans la ListView cette tche est dlgue aux sous-classes.
Lactivit principale LoremDemo utilise simplement un ListAdapter pour la liste de mots :
package com.commonsware.android.search;
import android.content.Intent;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
public class LoremDemo extends LoremBase {
@Override
ListAdapter makeMeAnAdapter(Intent intent) {
return(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items));
}
}

Lactivit de recherche, cependant, fonctionne un peu diffremment. Elle commence par


inspecter lintention fournie la mthode makeMeAnAdapter() ; cette intention provient
soit donCreate(), soit donNewIntent(). Sil sagit dACTION_SEARCH, on sait que cest
une recherche : on peut donc rcuprer la requte et, dans le cas de notre exemple stupide,
drouler la liste des mots chargs pour ne conserver que ceux qui contiennent la chane
recherche. La liste ainsi obtenue est ensuite enveloppe dans un ListAdapter que lon
renvoie pour quil soit affich :
package com.commonsware.android.search;
import android.app.SearchManager;
import android.content.Intent;

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 347 Dimanche, 8. novembre 2009 12:23 12

Chapitre 36

Recherches avec SearchManager

347

import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import java.util.ArrayList;
import java.util.List;
public class LoremSearch extends LoremBase {
@Override
ListAdapter makeMeAnAdapter(Intent intent) {
ListAdapter adapter=null;
if (intent.getAction().equals(Intent.ACTION_SEARCH)) {
String query=intent.getStringExtra(SearchManager.QUERY);
List<String> results=searchItems(query);
adapter=new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
results);
setTitle("LoremSearch de : " + query);
}
return(adapter);
}
private List<String> searchItems(String query) {
List<String> results=new ArrayList<String>();
for (String item : items) {
if (item.indexOf(query)>-1) {
results.add(item);
}
}
return(results);
}
}

Modification du manifeste
Bien que ce code implmente la recherche, il nest pas intgr au systme de recherche
dAndroid. Pour ce faire, vous devez modifier le fichier AndroidManifest.xml :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.commonsware.android.search">
<application>
<activity android:name=".LoremDemo" android:label="LoremDemo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 348 Dimanche, 8. novembre 2009 12:23 12

348

Lart du dveloppement Android

<meta-data android:name="android.app.default_searchable"
android:value=".LoremSearch" />
</activity>
<activity
android:name=".LoremSearch"
android:label="LoremSearch"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
</application>
</manifest>

Les modifications ncessaires sont les suivantes :


1. Lactivit LoremDemo reoit un lment meta-data avec un attribut android:name
valant android.app.default_searchable et un attribut android:value contenant
la classe qui implmente la recherche (.LoremSearch).
2. Lactivit LoremSearch reoit un filtre dintention pour android.intent.action.SEARCH, afin que les intentions de recherche puissent tre slectionnes.
3. Lactivit LoremSearch reoit lattribut android:launchMode = "singleTop", ce
qui signifie quune seule instance de cette activit sera ouverte un instant donn, afin
dviter que tout un lot de petites activits de recherche encombre la pile des activits.
4. Lactivit LoremSearch reoit un lment meta-data dot dun attribut
android:name valant android.app.searchable et dun attribut android:value
pointant vers une ressource XML contenant plus dinformations sur la fonctionnalit
de recherche offerte par lactivit (@xml/searchable).
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/searchLabel"
android:hint="@string/searchHint" />

Actuellement, cette ressource XML fournit deux informations :

le nom qui doit apparatre dans le bouton du domaine de recherche droite du champ
de saisie, afin dindiquer lutilisateur lendroit o il recherche (android:label) ;

le texte qui doit apparatre dans le champ de saisie, afin de donner lutilisateur un
indice sur ce quil doit taper (android:hint).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 349 Dimanche, 8. novembre 2009 12:23 12

Chapitre 36

Recherches avec SearchManager

349

Effectuer une recherche


Android sait dsormais que votre application peut tre consulte, connat le domaine de
recherche utiliser lors dune recherche partir de lactivit principale et lactivit sait
comment effectuer la recherche.
Le menu de cette application permet de choisir une recherche locale ou une recherche
globale. Pour effectuer la premire, on appelle simplement onSearchRequested() ; pour
la seconde, on appelle startSearch() en lui passant true dans son dernier paramtre,
afin dindiquer que la porte de la recherche est globale.
En tapant une lettre ou deux, puis en cliquant sur le bouton, on lance lactivit de recherche et le sous-ensemble des mots contenant le texte recherch saffiche. Le texte tap
apparat dans la barre de titre de lactivit, comme le montre la Figure 36.3.
Figure 36.3
Lapplication Lorem,
montrant une recherche
locale.

Vous pouvez obtenir le mme effet en commenant taper dans lactivit principale car
elle est configure pour dclencher une recherche locale.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 350 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 351 Dimanche, 8. novembre 2009 12:23 12

37
Outils de dveloppement
Le SDK Android nest pas quune bibliothque de classes Java et dAPI, il contient
galement un certain nombre doutils permettant de faciliter le dveloppement des
applications.
Nous avons surtout voqu le plug-in Eclipse, qui intgre le processus de dveloppement
Android dans cet IDE et nous avons galement cit les plug-in quivalents des autres environnements, ainsi que les outils en ligne de commande, comme adb, qui permet de
communiquer avec lmulateur.
Dans ce chapitre, nous nous intresserons aux autres outils.

Gestion hirarchique
Android est fourni avec un outil permettant de visualiser une hirarchie, conu pour vous
aider consulter vos layouts tels quils sont vus par une activit en cours dexcution dans
un mulateur. Vous pouvez ainsi savoir lespace quoccupe un widget ou trouver un widget
particulier.
Pour utiliser cet outil, vous devez dabord lancer lmulateur, installer votre application,
lancer lactivit et naviguer vers lendroit que vous souhaitez examiner. Comme le montre

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 352 Dimanche, 8. novembre 2009 12:23 12

352

Lart du dveloppement Android

la Figure 37.1, nous utiliserons titre dexemple lapplication ReadWriteFileDemo que


nous avions prsente au Chapitre 18.
Pour lancer le visualisateur, utilisez le programme hierarchyviewer qui se trouve dans le
rpertoire tools/ de votre installation du SDK. Vous obtiendrez alors la fentre prsente
la Figure 37.2.
Figure 37.1
Lapplication
ReadWriteFileDemo.

Figure 37.2
Fentre principale du visualisateur hirarchique.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 353 Dimanche, 8. novembre 2009 12:23 12

Chapitre 37

Outils de dveloppement

353

La liste gauche montre les diffrents mulateurs que vous avez chargs. Le nombre aprs
le tiret devrait correspondre au nombre entre parenthses situ dans la barre de titre de
lmulateur. Si vous cliquez sur un mulateur, la liste des fentres accessibles apparat
droite, comme le montre la Figure 37.3.

Figure 37.3
Hirarchie des fentres disponibles.

Vous remarquerez quoutre lactivit ouverte apparaissent de nombreuses autres fentres,


dont celle du lanceur (lcran daccueil), celle du "Keyguard" (lcran noir "Appuyez sur
Menu pour dverrouiller le tlphone" qui apparat lorsque vous ouvrez lmulateur pour
la premire fois), etc. Votre activit est identifie par le nom du paquetage et de la classe de
lapplication (com.commonsware.android.files/..., ici).
Les choses commencent devenir intressantes lorsque vous slectionnez lune de ces
fentres et que vous cliquez sur le bouton "Load View Hierarchy". Aprs quelques secondes,
les dtails apparaissent dans une fentre appele "Layout View" (voir Figure 37.4).
La zone principale de cette "Layout View" est occupe par une arborescence des diffrentes vues qui composent votre activit, en partant de la fentre principale du systme et en
descendant vers les diffrents widgets graphiques que verra lutilisateur. Dans la branche
infrieure droite de cet arbre, vous retrouverez les widgets LinearLayout, Button et
EditText utiliss par lapplication. Les autres vues sont toutes fournies par le systme, y
compris la barre de titre.
Si vous cliquez sur lune de ces vues, des informations supplmentaires apparaissent dans
le visualisateur, comme le montre la Figure 37.5.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 354 Dimanche, 8. novembre 2009 12:23 12

354

Lart du dveloppement Android

Figure 37.4
Layout View de lapplication ReadWrite FileDemo.

Figure 37.5
Affichage des proprits dune vue.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 355 Dimanche, 8. novembre 2009 12:23 12

Chapitre 37

Outils de dveloppement

355

Dans la rgion suprieure droite du visualisateur, nous pouvons maintenant voir les
proprits du widget slectionn ici, le bouton. Malheureusement, ces proprits ne sont
pas modifiables.
En outre, le widget slectionn est surlign en rouge dans la reprsentation schmatique de
lactivit qui apparat sous la liste des proprits (par dfaut, les vues sont reprsentes par
des contours blancs sur un fond noir) : ceci permet de vrifier que vous avez slectionn le
bon widget lorsque, par exemple, il y a plusieurs boutons.
Si vous double-cliquez sur une vue de larborescence, un panneau apparat pour ne vous
montrer que cette vue (et ses fils), isole du reste de lactivit.
Dans le coin infrieur gauche de la fentre principale se trouvent deux boutons celui qui
reprsente une arborescence est choisi par dfaut. Si vous cliquez sur le bouton qui reprsente une grille, le visualisateur affiche une autre reprsentation, appele "Pixel Perfect
View" (voir Figure 37.6).

Figure 37.6
Visualisateur hirarchique en mode "Pixel Perfect View".

La partie gauche contient une reprsentation arborescente des widgets et des autres vues
de votre activit. Au milieu se trouve votre activit ("Normal View") et, sur la droite, vous
pouvez voir une version zoome ("Loupe View") de celle-ci.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 356 Dimanche, 8. novembre 2009 12:23 12

356

Lart du dveloppement Android

Il faut bien comprendre que cette visualisation est en direct : lactivit est interroge selon la
frquence choisie par le curseur "Refresh Rate". Tout ce que vous faites avec cette activit se
refltera donc dans les vues "Normal" et "Loupe" de la fentre "Pixel Perfect View".
Les lignes fines de couleur cyan places au-dessus de lactivit montrent la position sur laquelle
le zoom sapplique il suffit de cliquer sur une nouvelle zone pour changer lendroit inspect
par la "Loupe View". Un autre curseur permet de rgler la puissance du grossissement.

DDMS (Dalvik Debug Monitor Service)


Lautre outil de larsenal du dveloppeur Android sappelle DDMS (Dalvik Debug Monitor Service). Cest une sorte de "couteau suisse" qui vous permet de parcourir les fichiers
journaux, de modifier la position GPS fournie par lmulateur, de simuler la rception
dappels et de SMS et de parcourir le contenu de lmulateur pour y placer ou en extraire
des fichiers. Nous ne prsenterons ici que les fonctionnalits les plus utiles.
Pour utiliser DDMS, lancez le programme ddms qui se trouve dans le rpertoire tools/
de votre installation du SDK. Au dpart, vous ne verrez dans la partie gauche quune arborescence des mulateurs avec les programmes quils excutent (voir Figure 37.7).
Figure 37.7
Vue initiale
de DDMS.

Cliquer sur un mulateur permet de parcourir le journal des vnements qui apparat dans
la zone du bas et de manipuler lmulateur via un onglet qui se trouve droite (voir
Figure 37.8).

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 357 Dimanche, 8. novembre 2009 12:23 12

Chapitre 37

Outils de dveloppement

357

Figure 37.8
mulateur
slectionn
dans DDMS.

Journaux
la diffrence dadb logcat, DDMS vous permet dexaminer le contenu du journal dans
un tableau dot dune barre de dfilement. Il suffit de cliquer sur lmulateur ou le terminal
que vous voulez surveiller pour que le bas de la fentre affiche le contenu du journal.
En outre, vous pouvez agir comme suit :
Filtrer les entres du journal selon lun des cinq niveaux reprsents par les boutons E
V dans la barre doutils.
Crer un filtre personnalis pour ne voir que les entres correspondantes. Pour ce faire,
cliquez sur le bouton + et remplissez le formulaire : le nom que vous choisirez pour ce
filtre sera utilis pour nommer un autre onglet qui apparatra ct du contenu du
journal (voir Figure 37.9).
Sauvegarder les entres du journal dans un fichier texte, afin de pouvoir les rutiliser
plus tard.
Figure 37.9
Filtrage des entres du
journal avec DDMS.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 358 Dimanche, 8. novembre 2009 12:23 12

358

Lart du dveloppement Android

Stockage et extraction de fichiers


Bien que vous puissiez utiliser adb pull et adb push pour, respectivement, extraire ou stocker
des fichiers sur un mulateur ou un terminal, DDMS permet de le faire de faon plus visuelle.
Il suffit, pour cela, de slectionner lmulateur ou le terminal concern, puis de choisir
loption Device > File Explorer... partir du menu principal : une fentre de dialogue typique
comme celle de la Figure 37.10 permet alors de parcourir larborescence des fichiers.
Figure 37.10
Explorateur
de fichiers
de DDMS.

Slectionnez simplement le fichier concern et cliquez sur le bouton dextraction (


gauche) ou de stockage (au milieu) de la barre doutils pour le transfrer vers ou partir de
votre machine de dveloppement. Le bouton de suppression ( droite) permet de supprimer
le fichier slectionn.
Info

Cet outil ne permet pas de crer de rpertoire : pour cela, vous devez soit utiliser
la commande adb shell, soit les crer partir de votre application.
Bien que vous puissiez parcourir la plupart des fichiers dun mulateur, les
restrictions de scurit dAndroid limitent beaucoup laccs en dehors de
larborescence /sdcard sur un vrai terminal.

Copies dcran
Pour faire une copie dcran de lmulateur ou dun terminal Android, faites simplement
Ctrl+S ou choisissez Device > Screen capture... dans le menu principal. Ceci ouvrira une
bote de dialogue contenant une image de lcran courant, comme la Figure 37.11.
partir de l, vous pouvez cliquer sur "Save" pour sauvegarder limage au format PNG
sur votre machine de dveloppement, rafrachir limage partir de ltat courant de
lmulateur ou du terminal, ou cliquer sur "Done" pour fermer la bote de dialogue.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 359 Dimanche, 8. novembre 2009 12:23 12

Chapitre 37

Outils de dveloppement

359

Figure 37.11
Capture dcran
avec DDMS.

Mise jour de la position


Pour que DDMS mette jour la position de votre application, vous devez dabord faire en
sorte que lapplication utilise le fournisseur de localisation gps, car cest le seul que
DDMS sait modifier. Puis cliquez sur longlet "Emulator Control" et recherchez la section
"Location Controls". Dans celle-ci, vous trouverez un cadre avec trois onglets permettant
de choisir le format des coordonnes : "Manual", "GPX" et "KML" (voir Figure 37.12).
Figure 37.12
Contrle
de la position
avec DDMS.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 360 Dimanche, 8. novembre 2009 12:23 12

360

Lart du dveloppement Android

Lutilisation de longlet "Manual" est assez vidente : on fournit une latitude et une
longitude et lon clique sur le bouton "Send" pour envoyer cet emplacement
lmulateur. Ce dernier, son tour, prviendra les couteurs de localisation de la nouvelle
position.
La prsentation des formats GPX et KML sort du cadre de ce livre.

Appels tlphoniques et SMS


DDMS sait galement simuler la rception dappels tlphoniques et de SMS via le
groupe "Telephony Actions" de longlet "Emulator Control" (voir Figure 37.13).

Figure 37.13
Contrle de la tlphonie avec DDMS.

Pour simuler un appel tlphonique, saisissez un numro, cochez le bouton radio "Voice"
puis cliquez sur le bouton "Call". Comme le montre la Figure 37.14, lmulateur affichera
lappel entrant et vous demandera si vous lacceptez (avec le bouton vert du tlphone) ou
si vous le rejetez (avec le bouton rouge).
Pour simuler la rception dun SMS, saisissez un numro tlphonique, cochez le bouton
radio "SMS", saisissez un message dans la zone de texte et cliquez sur "Send". Le message
apparatra sous la forme dune notification, comme la Figure 37.15.
En cliquant sur la notification, vous pourrez voir le contenu intgral du message, comme le
montre la Figure 37.16.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 361 Dimanche, 8. novembre 2009 12:23 12

Chapitre 37

Outils de dveloppement

361

Figure 37.14
Simulation de
la rception dun appel.

Figure 37.15
Simulation de
la rception dun SMS.

Figure 37.16
Simulation de
la rception dun SMS
dans lapplication
SMS/MMS.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 362 Dimanche, 8. novembre 2009 12:23 12

362

Lart du dveloppement Android

Gestion des cartes amovibles


Le G1 dispose dun emplacement pour carte microSD et de nombreux autres terminaux
Android disposent dune forme similaire de stockage amovible, dsign de faon gnrique
par le terme "Carte SD".
Les cartes SD servent stocker les gros fichiers, comme les images, les clips vido, les
fichiers musicaux, etc. La mmoire interne du G1, notamment, est relativement peu importante et il est prfrable de stocker un maximum de donnes sur une carte SD.
Bien que le G1 ait une carte SD par dfaut, le problme, videmment, est que lmulateur
nen a pas. Pour que ce dernier se comporte comme le G1, vous devez donc crer et "insrer" une carte SD dans lmulateur.

Cration dune image de carte


Au lieu dexiger que les mulateurs aient accs un vrai lecteur de carte SD pour utiliser
de vraies cartes, Android est configur pour utiliser des images de cartes. Une image est
simplement un fichier que lmulateur traitera comme sil sagissait dun volume de
carte SD : il sagit en fait du mme concept que celui utilis par les outils de virtualisation
(comme VirtualBox) Android utilise une image disque pour reprsenter le contenu dune
carte SD.
Pour crer cette image, utilisez le programme mksdcard qui se trouve dans le rpertoire
tools/ de votre installation du SDK. Ce programme attend au moins deux paramtres :
1. La taille de limage et donc de la "carte". Si vous fournissez un nombre, celui-ci sera
interprt comme un nombre doctets. Vous pouvez galement le faire suivre de K ou M
pour prciser que cette taille est, respectivement, exprime en kilo-octets ou en
mgaoctets.
2. Le nom du fichier dans lequel stocker limage.
Pour, par exemple, crer limage dune carte SD de 1 Go afin de simuler celle du GI dans
lmulateur, faites :
mksdcard 1024M sdcard.img

Insertion de la carte
Pour que lmulateur utilise cette image de carte, lancez-le avec loption -sdcard suivie
du chemin complet vers le fichier image cr avec mksdcard. Bien que cette option nait
pas deffet visible aucune icne dAndroid ne montrera quune carte est monte , le
rpertoire /sdcard sera dsormais accessible en lecture et en criture.
Pour placer et lire des fichiers dans /sdcard, utilisez lexplorateur de fichiers de DDMS
ou les commandes adb push et adb pull partir de la console.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 363 Dimanche, 8. novembre 2009 12:23 12

38
Pour aller plus loin
Ce livre ne couvre videmment pas tous les sujets possibles et, bien que la ressource
numro un (en dehors de cet ouvrage) soit la documentation du SDK Android, vous aurez
srement besoin dinformations qui se trouvent ailleurs.
Une recherche web sur le mot "android" et un nom de classe est un bon moyen de trouver
des didacticiels pour une classe donne. Cependant, noubliez pas que les documents
crits avant fin 2008 concernent probablement le SDK M5 et ncessiteront donc des modifications trs importantes pour fonctionner correctement avec les SDK actuels.
Ce chapitre vous donnera donc quelques pistes explorer.

Questions avec, parfois, des rponses


Les groupes Google consacrs Android sont les endroits officiels pour obtenir de laide.
Trois groupes sont consacrs au SDK :

Android Beginners1 est le meilleur endroit pour poster des questions de dbutant.

1. http://groups.google.com/group/android-beginners.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 364 Dimanche, 8. novembre 2009 12:23 12

364

Lart du dveloppement Android

Android Developers1 est consacr aux questions plus compliques ou celles qui relvent
de parties plus exotiques du SDK.

Android Discuss2 est rserv aux discussions btons rompus sur tout ce qui est li
Android, pas ncessairement aux problmes de programmation.

Vous pouvez galement consulter :

les didacticiels et les forums consacrs Android sur le site anddev.org 3 ;

le canal IRC #android sur freenode.

Aller la source
Le code source dAndroid est dsormais disponible, essentiellement pour ceux qui
souhaitent amliorer le systme ou jouer avec ses dtails internes. Toutefois, vous
pouvez aussi y trouver les rponses que vous recherchez, notamment si vous voulez
savoir comment fonctionne un composant particulier.
Le code source et les ressources qui y sont lies se trouvent sur le site web du projet
Android4. partir de ce site, vous pouvez :

tlcharger5 ou parcourir6 le code source ;

signaler des bogues7 du systme lui-mme ;

proposer des patchs8 et apprendre comment ces patchs sont valus et approuvs ;

rejoindre un groupe Google particulier9 pour participer au dveloppement de la plateforme Android.

Citons galement quelques ressources francophones :

les sites www.frandroid.com et www.pointgphone.com, qui proposent des articles et


des forums de discussion.

groups.google.com/group/android-fr, qui est un groupe Google francophone consacr


Android.

1.
2.
3.
4.
5.
6.
7.
8.
9.

http://groups.google.com/group/android-developers.
http://groups.google.com/group/android-discuss.
http://anddev.org/.
http://source.android.com.
http://source.android.com/download.
http://git.source.android.com/.
http://source.android.com/report-bugs.
http://source.android.com/submit-patches.
http://source.android.com/discuss.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 365 Dimanche, 8. novembre 2009 12:23 12

Chapitre 38

Pour aller plus loin

365

Lire les journaux


Ed Burnette, qui a crit son propre livre sur Android, est galement le gestionnaire de
Planet Android1, un agrgateur de flux pour un certain nombre de blogs consacrs
Android. En vous abonnant ce flux, vous pourrez ainsi surveiller un grand nombre darticles,
pas ncessairement consacrs la programmation.
Pour surveiller plus prcisment les articles lis la programmation dAndroid, faites une
recherche sur le mot-cl "android" sur Dzone ; vous pouvez galement vous abonner un
flux2 qui rsume cette recherche.

1. http://www.planetandroid.com/.
2. http://www.dzone.com/links/feed/search/android/rss.xml.

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 366 Dimanche, 8. novembre 2009 12:23 12

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 367 Dimanche, 8. novembre 2009 12:23 12

Index

A
aapt, outil 30
AbsoluteLayout, conteneur 128
Acclromtres 265
Activity, classe 25, 165, 272
activity, lment 272
activity, lment du manifeste 15
ActivityAdapter, adaptateur 67
ActivityAdapter, classe 264
activityCreator, script 203
ActivityIconAdapter, adaptateur 67
ActivityManager, classe 161
Adaptateur 65
adb, programme 224, 351, 357, 358, 362
add(), mthode 130, 332
addId(), mthode 280
addIntentOptions(), mthode 131, 263, 264
addMenu(), mthode 131
addProximityAlert(), mthode 325
addSubMenu(), mthode 131
addTab(), mthode 116
AlertDialog, classe 156
AnalogClock, widget 111
android
alphabeticShortcut, attribut 140
apiKey, attribut 329, 336

authorities, attribut 295


autoText, attribut de widget 38
background, attribut 44
capitalize, attribut de widget 38
clickable, attribut 329
collapseColumns, proprit 59
columnWidth, proprit 74
completionThreshold, proprit 77
configChanges, attribut 272
content, attribut 127
digits, attribut de widget 38
drawSelectorOnTop, proprit 71, 81
ellipsize, attribut 145
enabled, attribut 139
handle, attribut 127
hint, attribut 348
horizontalSpacing, proprit 74
icon, attribut 139
id, attribut 114, 138, 329
id, attribut de main.xml 31
inputMethod, attribut de widget 38
label, attribut 348
launchMode, attribut 348
layout_above, proprit 54
layout_alignBaseline, proprit 54
layout_alignBottom, proprit 54

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 368 Dimanche, 8. novembre 2009 12:23 12

368

Lart du dveloppement Android

android (suite)
layout_alignLeft, proprit 54
layout_alignParentBottom, proprit 53
layout_alignParentLeft, proprit 53
layout_alignParentRight, proprit 53
layout_alignParentTop, proprit 53, 56
layout_alignRight, proprit 54
layout_alignTop, proprit 54
layout_below, proprit 54
layout_centerHorizontal, proprit 53
layout_centerInParent, proprit 53
layout_centerVertical, proprit 53
layout_column, proprit 58
layout_gravity, proprit 47
layout_height, attribut 114
layout_span, proprit 58
layout_toLeftOf, proprit 54
layout_toRightOf, proprit 54
layout_weight, proprit 47
layout_width, attribut de main.xml 31
layout_width, proprit 46, 55
menuCategory, attribut 139
name, attribut 295, 298, 306, 348
name, attribut du manifeste 15
nextFocusDown, attribut 43
nextFocusLeft, attribut 43
nextFocusRight, attribut 43
nextFocusUp, attribut 43
numColumns, proprit 74
numeric, attribut de widget 38
numericShortcut, attribut 140
orderInCategory, attribut 139
orientation, proprit 46
padding, proprit 47
paddingLeft, proprit 48
paddingTop, proprit 114
password, attribut de widget 38
permission, attribut 300, 306
phoneNumber, attribut de widget 38
readPermission, attribut 300
screenOrientation, attribut 274
shrinkColumns, proprit 59
singleLine, attribut de widget 38

spacing, proprit 81
spinnerSelector, proprit 81
src, attribut de widget 37
stretchColumns, proprit 59
stretchMode, proprit 74
text, attribut de main.xml 31
text, attribut de widget 36
typeface, attribut 143
value, attribut 348
versionCode, attribut du manifeste 17
verticalSpacing, proprit 74
visibility, attribut 44
visible, attribut 139
writePermission, attribut 300
Android Scripting Environment (ASE) 233
android, paquetage 25
android, script 22, 23
AndroidManifest 182
AndroidManifest.xml, fichier 9, 13, 148,
295, 298, 306, 347
animateClose(), mthode 128
animateOpen(), mthode 128
animateToggle(), mthode 128
Animations 124
apk, fichier 11
appendWhere(), mthode 223
application, lment 295, 306
application, lment du manifeste 14
Arborescence de rpertoires 9
ArrayAdapter, adaptateur 66, 69, 85, 96
ArrayAdapter, classe 169, 194
ArrayList, classe 194
AssetManager, classe 143
AsyncTask, classe 166, 307
AutoCompleteTextView, widget 39, 77
Auto-compltion 77
AVD (Android Virtual Device) 22

B
BaseColumns, interface 295
BeanShell, programme 229
beforeTextChanged(), mthode 79

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 369 Dimanche, 8. novembre 2009 12:23 12

Index

bindView(), mthode 105


BLOB (Binary Large Object) 286
BroadcastReceiver, classe 251, 311
BroadcastReceiver, interface 247
build.xml, fichier 10
Builder, classe 156
buildQuery(), mthode 223
bulkInsert(), mthode 285
Bundle, classe 176, 252, 266, 271
Bundle, objet 174
Button, widget 36

369

convertView, paramtre de getView() 89


create(), mthode 157
createDatabase(), mthode 225
createFromAsset(), mthode 143
createItem(), mthode 332
createTabContent(), mthode 118
Criteria, classe 323
Cursor, classe 221
Cursor, interface 281, 290
Cursor, widget 67
CursorAdapter, adaptateur 67, 105
CursorFactory, classe 224

C
Calendar, classe 110
cancel(), mthode 314
cancelAll(), mthode 314
canGoBack(), mthode 151
canGoBackOrForward(), mthode 151
canGoForward(), mthode 151
Catgories d'activits 244
check(), mthode 42
CheckBox, widget 40
CheckBoxPreference, lment 181
checkCallingPermission(), mthode 301
clear(), mthode 180
clearCache(), mthode 151
clearCheck(), mthode 42
clearHistory(), mthode 151
close(), mthode 128, 195, 219, 224
color, lment 211
commit(), mthode 180
ComponentName, classe 264
CompoundButton, widget 42
ContentManager, classe 314
ContentObserver, classe 296
ContentProvider, classe 289
ContentProvider, interface 285
ContentResolver, classe 285, 296
ContentValues, classe 220, 285, 291, 292
Context, classe 179, 195
ContextMenuInfo, classe 132

D
DatabaseHelper, classe 289
DateFormat, classe 110
DatePicker, widget 108
DatePickerDialog, widget 108
DDMS (Dalvik Debug Monitor Service) 326
ddms, programme 356
default.properties, fichier 10
DefaultHttpClient, classe 236
delete(), mthode 220, 285, 293
dex, programme 229
DialogWrapper, classe 285
DigitalClock, widget 111
dimen, lment 211
doInBackground(), mthode 167
draw(), mthode 333
Drawable, classe 333
Drawable, interface 205

E
edit(), mthode 180
EditText, widget 38
EditTextPreference, lment 188
Espace de noms 14
execSQL(), mthode 219
execute(), mthode 166, 171, 236
ExpandableListView, classe 128

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 370 Dimanche, 8. novembre 2009 12:23 12

370

Lart du dveloppement Android

F
fill_parent, valeur de remplissage 46
findViewById (), mthode 32
findViewById() 91
findViewById(), mthode 44, 89, 116,
191, 330
finish(), mthode 175, 197
Forecast, classe 239, 308
format(), mthode 201
FrameLayout, conteneur 114, 121
fromHtml(), mthode 202

G
Gallery, widget 81
GeoPoint, classe 331
getAltitude(), mthode 323
getAsInteger(), mthode 220
getAssets(), mthode 143
getAsString(), mthode 220
getAttributeCount(), mthode 209
getAttributeName(), mthode 209
getBearing(), mthode 323
getBestProvider(), mthode 323
getBoolean(), mthode 180
getCallState(), mthode 338
getCheckedItemPositions(), mthode 71
getCheckedRadioButtonId(), mthode 42
getColor(), mthode 211
getColumnIndex(), mthode 224
getColumnNames(), mthode 224
getContentProvider(), mthode 285
getContentResolver(), mthode 296
getCount(), mthode 223
getDefaultSharedPreferences(), mthode 180
getDimen(), mthode 211
getFloat(), mthode 284
getInputStream(), mthode 286
getInt(), mthode 224, 284
getIntent(), mthode 344

getItemId(), mthode 131


getLastKnownPosition(), mthode 323
getLastNonConfigurationInstance(), mthode
270
getLatitude(), mthode 237
getListView(), mthode 69
getLongitude(), mthode 237
getMapController(), mthode 330
getMenuInfo(), mthode 132
getNetworkType(), mthode 338
getOutputStream(), mthode 286
getOverlays(), mthode 332
getPackageManager(), mthode 264
getParent(), mthode 44
getPhoneType(), mthode 338
getPosition(), mthode 284
getPreferences(), mthode 179
getProgress(), mthode 113
getProviders(), mthode 323
getReadableDatabase(), mthode 219
getRequiredColumns(), mthode 292
getResources(), mthode 191, 194, 208
getRootView(), mthode 44
getSettings(), mthode 149, 153
getSharedPreferences(), mthode 179
getSpeed(), mthode 323
getString(), mthode 201, 224, 284
getStringArray(), mthode 212
getSubscriberId(), mthode 338
getSystemService(), mthode 314, 322
getTableName(), mthode 223
getTag(), mthode 91, 97
getType(), mthode 294
getView(), mthode 66, 86, 89, 105, 283
getWriteableDatabase(), mthode 219
getXml(), mthode 207
goBack(), mthode 151
goBackOrForward(), mthode 151
goForward(), mthode 151
GPS (Global Positioning System) 321
group, lment 138

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 371 Dimanche, 8. novembre 2009 12:23 12

Index

H
handleMessage(), mthode 162
Handler, classe 162, 311
hasAltitude(), mthode 323
hasBearing(), mthode 323
hasSpeed(), mthode 323
hierarchyviewer, programme 352
htmlEncode(), mthode 204
HttpClient, classe 304
HttpClient, interface 236
HttpComponents, bibliothque 236
HttpGet, classe 236
HttpPost, classe 236
HttpRequest, interface 236
HttpResponse, classe 236

I
IBinder, classe 305
ImageView, classe 286
ImageView, widget 37
incrementProgressBy(), mthode 113
indiquant 182
InputMethod, interface 38
InputStream, classe 191, 195, 239
InputStreamReader, classe 195
insert(), mthode 220, 285, 291
instrumentation, lment du manifeste 14
Intent, classe 244
intent-filter, lment 245
Internationalisation (I18N) 200, 212
Interpreter, classe de BeanShell 229
isAfterLast(), mthode 223, 284
isBeforeFirst(), mthode 284
isChecked(), mthode 40
isCollectionUri(), mthode 292, 293
isEnabled(), mthode 44
isFirst(), mthode 284
isLast(), mthode 284
isNull(), mthode 284
isRouteDisplayed(), mthode 330
item, lment 138, 212

371

ItemizedOverlay, classe 332


Items, classe 264
Iterator, interface 284

J
JavaScript, et WebView 149
JRuby, langage de script 233
Jython, langage de script 233

K
keytool, utilitaire 336

L
LayoutInflater, classe 87, 103
LinearLayout, conteneur 46, 84, 97, 103
ListActivity, classe 67, 168
ListAdapter, adaptateur 100
ListPreference, lment 188
ListView, widget 67, 83
loadData(), mthode 150
loadUrl(), mthode 148
Localisation (L10N) 200, 212
Location, classe 237, 323
LocationListener, classe 324
LocationManager, classe 304, 322
LocationProvider, classe 322
lock(), mthode 128

M
makeText(), mthode 156
managedQuery(), mthode 281
manifest, lment 298
manifest, lment racine du manifeste 14
MapActivity, classe 327, 328
MapView, classe 327, 328
Menu, classe 130, 140, 263
menu, lment 138
MenuInflater, classe 140
MenuItem, classe 130
Message, classe 162

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 372 Dimanche, 8. novembre 2009 12:23 12

372

Lart du dveloppement Android

meta-data, lment 348


mthode 91
MIME, types 288
mksdcard, programme 362
move(), mthode 284
moveToFirst(), mthode 223, 284
moveToLast(), mthode 284
moveToNext(), mthode 223, 284
moveToPosition(), mthode 284
moveToPrevious(), mthode 284
MyLocationOverlay, classe 334

N
name, attribut 200, 211, 212
newCursor(), mthode 224
newTabSpec(), mthode 115
newView(), mthode 105
next(), mthode 208
Notification, classe 172
NotificationManager, classe 314
notify(), mthode 314
notifyChange(), mthode 296

O
obtainMessage(), mthode 162
onActivityResult(), mthode 251, 260
onBind(), mthode 305
OnCheckedChangeListener, interface 50
onClick(), mthode 26
OnClickListener(), mthode 158
OnClickListener, classe 110, 253
OnClickListener, interface 26
onConfigurationChanged(), mthode 272
onContextItemSelected(), mthode 132, 134
onCreate(), mthode 26, 134, 174, 176, 184,
194, 219, 266, 282, 289, 304, 344, 346
onCreateContextMenu(), mthode 132, 134
onCreateOptionsMenu(), menu 134
onCreateOptionsMenu(), mthode 130, 131

onCreatePanelMenu(), mthode 131


OnDateChangedListener, classe 108
OnDateSetListener, classe 108
onDestroy(), mthode 174, 304
OnItemSelectedListener, interface 72
onListItemClick(), mthode 69, 97
onLocationChanged(), mthode 324
onNewIntent(), mthode 344, 346
onOptionsItemSelected(), mthode 130, 131,
134
onPageStarted(), mthode 151
onPause(), mthode 175, 197, 247, 304, 311,
335
onPostExecute(), mthode 167
onPreExecute(), mthode 167
onPrepareOptionsMenu(), mthode 130
onProgressUpdate(), mthode 168
onRatingChanged(), mthode 97
onReceive(), mthode 247
onReceivedHttpAuthRequest(), mthode 151
onRestart(), mthode 175
onRestoreInstanceState(), mthode 176, 266
onResume(), mthode 175, 184, 197, 247,
304, 311, 335
onRetainNonConfigurationInstance(), mthode
270
onSaveInstanceState(), mthode 174, 176,
248, 266, 267
onSearchRequested(), mthode 342, 349
onStart(), mthode 164, 175, 304
onStop(), mthode 175
onTap(), mthode 334
onTextChanged(), mthode 79
OnTimeChangedListener, classe 108
OnTimeSetListener, classe 108
onTooManyRedirects(), mthode 151
onUpgrade(), mthode 219
open(), mthode 128
openFileInput(), mthode 195, 197
openFileOutput(), mthode 195, 197
openRawResource(), mthode 191, 194
OpenStreetMap, cartographie 328
OutputStream, classe 195

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 373 Dimanche, 8. novembre 2009 12:23 12

Index

OutputStreamWriter, classe 195


Overlay, classe 332
OverlayItem, classe 332

P
package, attribut du manifeste 14
PackageManager, classe 264
parse(), mthode 280, 281
PendingIntent, classe 314, 325
permission, lment 299
permission, lment du manifeste 14
populate(), mthode 332
populateDefaultValues(), mthode 292
post(), mthode 165
postDelayed(), mthode 165, 311
PreferenceCategory, lment 185
PreferenceScreen, lment 181, 185
PreferencesManager, classe 180
ProgressBar, widget 113, 163
provider, lment 295
provider, lment du manifeste 16
publishProgress(), mthode 168

Q
query(), mthode 221, 290
queryIntentActivityOptions(), mthode 264
queryWithFactory(), mthode 224

R
R.java, fichier 10
RadioButton, widget 42
RadioGroup, widget 42
RatingBar, widget 94
rawQuery(), mthode 221
rawQueryWithFactory(), mthode 224
receiver, lment 247
receiver, lment du manifeste 16
registerContentObserver(), mthode 296
registerForContextMenu(), mthode 131
registerReceiver(), mthode 247

373

RelativeLayout, conteneur 52
reload(), mthode 151
remove(), mthode 180
removeProximityAlert(), mthode 325
removeUpdates(), mthode 325
requery(), mthode 224, 285
requestFocus(), mthode 44
requestLocationUpdates(), mthode 324
Resources, classe 191, 207
resources, lment 210
ResponseHandler, classe 236
ressources, lment 200
RingtonePreference, lment 181
RowModel, classe 96
Runnable, classe 165
runOnUiThread(), mthode 165

S
ScrollView, conteneur 61
SecurityException, exception 298
sendBroadcast(), mthode 251, 301, 308
sendMessage(), mthode 162
sendMessageAtFrontOfQueue(), mthode 162
sendMessageAtTime(), mthode 162
sendMessageDelayed(), mthode 162
sendOrderedBroadcast(), mthode 251
Service, classe 304
service, lment 306
service, lment du manifeste 16
setAccuracy(), mthode 323
setAdapter(), mthode 67, 71
setAlphabeticShortcut(), mthode 131
setAltitudeRequired(), mthode 323
setBuiltInZoomControls(), mthode 331
setCenter(), mthode 331
setCheckable(), mthode 131
setChecked(), mthode 40, 43
setChoiceMode(), mthode 69
setColumnCollapsed(), mthode 59
setColumnStretchable(), mthode 59
setContent(), mthode 115, 118
setContentView(), mthode 32

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 374 Dimanche, 8. novembre 2009 12:23 12

374

Lart du dveloppement Android

setCostAllowed(), mthode 323


setCurrentTab(), mthode 116
setDefaultFontSize(), mthode 153
setDefaultKeyMode(), mthode 342
setDropDownViewResource(), mthode 71, 72
setDuration(), mthode 156
setEnabled(), mthode 139
setFantasyFontFamily(), mthode 153
setFlipInterval(), mthode 125
setGravity(), mthode 47
setGroupCheckable(), mthode 130, 131
setGroupEnabled(), mthode 139
setGroupVisible(), mthode 139
setIcon(), mthode 157
setImageURI(), mthode 37
setIndeterminate(), mthode 113
setIndicator(), mthode 115
setJavaScriptCanOpenWindowsAutomatically(), mthode 153
setJavaScriptEnabled(), mthode 149, 153
setLatestEventInfo(), mthode 315
setListAdapter(), mthode 69
setMax(), mthode 113, 164
setMessage(), mthode 156
setNegativeButton(), mthode 157
setNeutralButton(), mthode 157
setNumericShortcut(), mthode 131
setOnCheckedChanged(), mthode 41
setOnCheckedChangeListener(), mthode 41
setOnClickListener(), mthode 26, 197
setOnItemSelectedListener(), mthode 67, 71
setOrientation(), mthode 46
setProgress(), mthode 113
setProjectionMap(), mthode 223
setQwertyMode(), mthode 131
setResult(), mthode 252
setTables(), mthode 223
setTag(), mthode 91, 97
setText(), mthode 27
setTextSize(), mthode 153
setTitle(), mthode 157
setTypeface(), mthode 143

setup(), mthode 116


setUserAgent(), mthode 154
setView(), mthode 156
setVisible(), mthode 139
setWebViewClient(), mthode 151
setZoom(), mthode 330
SharedPreferences, classe 180, 188
shouldOverrideUrlLoading(), mthode 151
show(), mthode 156
showNext(), mthode 122
SimpleAdapter, adaptateur 67
SimpleCursorAdapter, classe 282
Singleton, patron de conception 305
size(), mthode 332
SlidingDrawer, widget 126
SOAP, protocole 236
SoftReference, classe 307
Spanned, interface 201
Spinner, widget 71
SQLite Manager, extension Firefox 225
sqlite3, programme 224
SQLiteDatabase, classe 219
SQLiteOpenHelper, classe 219
SQLiteQueryBuilder, classe 221, 290
SSL et HttpClient 240
startActivity(), mthode 251, 338
startActivityForResult(), mthode 251, 260
startFlipping(), mthode 125
startSearch(), mthode 342, 349
startService(), mthode 310
stopService(), mthode 310
string, lment 200
string-array, lment 212
SystemClock, classe 162

T
TabActivity, classe 114, 255
TabContentFactory(), mthode 118
TabHost, classe 256
TabHost, conteneur 113
TableLayout, conteneur 57, 184
TableRow, conteneur 57

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Livre Android.book Page 375 Dimanche, 8. novembre 2009 12:23 12

Index

TabSpec, classe 115


TabView, conteneur 255
TabWidget, widget 114
TelephonyManager, classe 338
TextView, widget 35, 67, 76, 85, 143
TextWatcher, interface 77
TimePicker, widget 108
TimePickerDialog, widget 108
Toast, classe 156
toggle(), mthode 40, 128
toggleSatellite(), mthode 331
TrueType, polices 143
Typeface, classe 143

U
unlock(), mthode 128
unregisterContentObserver(), mthode 296
unregisterReceiver(), mthode 247
update(), mthode 220, 292
uptimeMillis(), mthode 162
Uri, classe 244, 260, 279, 338
uses-library, lment du manifeste 14

375

uses-permission, lment 298


uses-sdk, lment du manifeste 14, 16

V
Versions du SDK 16
View, classe 26
View, widget 59, 86, 87
ViewFlipper, conteneur 120
Virus 297

W
WeakReference, classe 307
WebKit, widget 201, 235
WebSettings, classe 153
WebView, widget 147
WebViewClient, classe 151
wrap_content, valeur de remplissage 46

X
XmlPullParser, classe 208
XML-RPC, protocole 236

customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>

Lart du dveloppement

Android
laide dexemples simples et faciles excuter,
apprenez dvelopper des applications pour
terminaux Android.
Smartphones, PDA et autres terminaux mobiles
connaissent aujourdhui une vritable explosion. Dans
ce contexte, Android, le systme dexploitation mobile
cr par Google, prsente le double avantage dtre
gratuit et open-source. Libre donc tout un chacun
den exploiter lnorme potentiel !
Dans cet ouvrage, Mark Murphy, dveloppeur et
membre actif de la communaut Android, vous explique tout ce que vous avez besoin de savoir pour
programmer des applications de la cration des
interfaces graphiques lutilisation de GPS, en passant
par laccs aux services web et bien dautres choses
encore ! Vous y trouverez une mine dastuces et de
conseils pour raliser vos premires applications Android
mais aussi pour accder facilement aux squences de
code qui vous intressent.
travers des dizaines dexemples de projets, vous
assimilerez les points techniques les plus dlicats et
apprendrez crer rapidement des applications
convaincantes.
Les codes sources du livre sont disponibles sur
www.pearson.fr.
propos de lauteur
Mark Murphy programme depuis plus de 25 ans et a travaill sur des platesformes allant du TRS-80 aux derniers modles de terminaux mobiles. Il est
le rdacteur des rubriques Building Droids de AndroidGuys et Android
Angle de NetworkWorld.

Tour dhorizon
Structure dun projet
Contenu du manifeste
Cration dun squelette dapplication
Utilisation des layouts XML
Utilisation des widgets de base
Conteneurs
Widgets de slection
Samuser avec les listes
Utiliser de jolis widgets et de beaux conteneurs
Utilisation des menus
Polices de caractres
Intgrer le navigateur de WebKit
Affichage de messages surgissant
Utilisation des threads
Gestion des vnements du cycle de vie dune
activit
Utilisation des prfrences
Accs aux fichiers
Utilisation des ressources
Accs et gestion des bases de donnes locales
Tirer le meilleur parti des bibliothques Java
Communiquer via Internet
Cration de filtres dintentions
Lancement dactivits et de sous-activits
Trouver les actions possibles grce lintrospection
Gestion de la rotation
Utilisation dun fournisseur de contenu (content
provider)
Construction dun fournisseur de contenu
Demander et exiger des permissions
Cration dun service
Appel dun service
Alerter les utilisateurs avec des notifications
Accs aux services de localisation
Cartographie avec MapView et MapActivity
Gestion des appels tlphoniques
Recherches avec SearchManager
Outils de dveloppement
Pour aller plus loin

Niveau : Intermdiaire / Avanc


Catgorie : Dveloppement mobile

ISBN : 978-2-7440-4094-8

Pearson Education France


47 bis, rue des Vinaigriers 75010 Paris
Tl. : 01 72 74 90 00
Fax : 01 42 05 22 17
www.pearson.fr
customer 27921 at Fri Mar 11 19:19:45 +0100 2011 Proprit de Albiri Sigue <tag.tog@gmail.com>