Le framework Android fournit, de base, un ensemble de composants graphiques appelés widgets. Ces widgets, à l'aide de l'éditeur de layout intégré à Eclipse, permettent de créer des interfaces graphiques facilement et sans trop perdre de temps. On retrouve dans la bibliothèque d'Android (android.widget
) des composants simples tels que Button
, TextView
ou ImageView
mais également d'autres widgets considérés comme plus sophistiqués : Gallery
, SlidingDrawer
, DatePicker
, etc.
Le développement sur Android peut parfois nécessiter l'utilisation de composants non disponibles, de base, dans android.widget
. Imaginons par exemple que vous souhaitiez définir un widget de type barre de progression circulaire. Le composant ProgressBar
ne permet pas d'afficher une progression de façon circulaire. Il n'existe aucun moyen d'afficher ce genre de composants et il est donc nécessaire soit d'en trouver une implémentation libre sur le web soit de le créer vous-même. L'objectif de ce tutorial est de s'initier à la création d'une View
personnalisée (custom view en anglais).
Note : La définition d'une vue personnalisée aborde un ensemble de points techniques. Expliquer de façon exhaustive l'ensemble de ces points nécessite plusieurs paragraphes de description. Pour éviter de surcharger et donc de rendre illisible cette partie, j'ai préféré séparer la création de la vue en 2 parties. Cette première partie se concentrera sur la création de la vue (par le code uniquement), la gestion des évènements tactiles et du “dessin” de la vue. La seconde partie s'attachera à optimiser et surtout rendre générique (et donc réutilisable) la vue. On pourra par exemple instancier la vue via XML. Au vu de cette séparation, il est donc important de conserver à l'esprit que cette première partie n'est pas “finie”. Ne prenez pas cette partie pour modèle exact mais attendez plutôt la version finale de notre vue.
L'objectif de cette partie sera de créer une vue nommée TrashView
. Cette dernière affichera simplement une poubelle et un fichier. Le fichier pourra être “draggué” dans la poubelle qui passera alors de l'état vide à l'état plein. Une première capture d'écran est donnée ci-dessous. Il est également possible de télécharger les sources de l'application grâce à ce zip
Comme mentionné ci-dessus, nous souhaitons créer une vue personnalisée instanciable par le code uniquement, gérant le dessin et les évènements tactiles. La gestion de ces possibilités se fait en créant une classe héritant de android.view.View
puis en redéfinissant void onDraw(Canvas canvas)
(gestion du dessin de la vue) et boolean onTouchEvent(MotionEvent event)
(gestion des évènements tactiles). Le squelette ci-dessous montre la forme globale de notre vue personnalisée et inclut le constructeur par le code TrashView(Context context)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Notre vue nécessite, bien évidemment, plusieurs variables définies en private
car elles ne reflètent aucunement l'état de la vue et permette de conserver l'encapsulation des données. Notez que les variables LOG_ENABLED
et LOG_TAG
sont déclarées dans une optique de déboguage optimisé et sont utilisées dans la méthode static void log(String log)
. Pour mieux comprendre cette technique de déboguage, je vous conseille de lire cet article rédigé par mes soins et détaillant l'astuce utilisée.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|
Les variables sont maintenant prêtes et nous pouvons donc créer le constructeur de notre vue. La vue est instanciable, pour l'instant, via le code uniquement. Nous implémentons donc le constructeur de la forme public View(Context context)
créant les différentes Bitmap
utilisées dans la vue :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Ce code n'a rien de compliqué mais la méthode static Bitmap prepareBitmap(Drawable drawable, int width, int height)
peut paraitre obscure pour les débutants. Android permet de récupérer facilement des ressources graphiques présentes dans le dossier res/drawable
via le fichier R.java
et sous forme d'un objet de type Drawable
. Ce type, extrêmement utile puisqu'il généralise totalement la notion d'objets “dessinables” dispose en contre partie d'inconvénients : son utilisation dans un Canvas
(surface sur laquelle nous dessinons notre vue) est un peu particulière et son affichage est assez lent (utilisation d'une interface et redimensionnement à la volée de l'image). Pour optimiser le dessin, il convient de déclarer des objets de type Bitmap
qui sont plus “naturels” à dessiner sur un Canvas
et sont également plus rapide à afficher puisque leur taille est définie de façon statique à l'initialisation. Cette méthode consiste donc simplement à mettre le contenu de drawable
à la taille (width
, height
) dans un objet Bitmap
qui sera retourné comme résultat de la méthode statique.
Modifions maintenant notre vue afin qu'elle puisse afficher les images. On corrige donc la méthode onDraw(Canvas canvas) qui est appelée par le système à la suite d'un invalidate()
(méthode informant le système que la vue est “sale” et nécessite d'être mise à jour). Le contenu de cette méthode est très succinct et se passe, à mon avis, de commentaires puisqu'elle ne consiste qu'à afficher les différentes images représentée dans la vue suivant l'état actuel de la poubelle :
1 2 3 4 5 6 7 8 9 |
|
L'objectif de cette partie est maintenant de gérer les évènements tactiles afin de faire en sorte que l'utilisateur puisse glisser le fichier sur la corbeille. Cette partie est probablement la partie la plus technique de ce tutorial. C'est probablement la raison pour laquelle il y a de nombreux points à détailler. J'ai donc préféré détailler les points techniques en commentant directement le code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
|
Dans le code donné ci-dessus, une méthode optimizedInvalidate()
est appelée. Cette méthode permet d'optimiser le rafraichissement de la vue. Il aurait, en effet, été possible d'utiliser invalidate()
en lieu et place d'optimizedInvalidate()
. Malheureusement, invalidate()
oblige le système à redessiner l'intégralité de la vue. Travaillant sur un terminal mobile, nous devons respecter des règles d'optimisations strictes. Une des principales règles, bien qu'évidente, mentionne qu'il ne faut rien faire d'inutile. Il serait donc stupide de redessiner l'intégralité de la vue alors qu'une faible partie seulement a besoin d'être actualisée. La méthode optimizedInvalidate()
s'occupe d'optimiser la surface invalidée :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Voilà, la première ébauche de notre vue personnalisée est maintenant terminée. Vous êtes maintenant prêt(e)s à enrichir formidablement le framework Android et vos interfaces graphiques. Tous à vos SDK !