Android propose un modèle à composants sophistiqués et puissant pour créer votre UI. Il est basé sur les classes de mise en page fondamentales View
et ViewGroup
. La plate-forme comprend diverses sous-classes View
et ViewGroup
prédéfinies (respectivement appelées widgets et mises en page) que vous pouvez utiliser pour créer votre UI.
Une liste partielle des widgets disponibles inclut Button
, TextView
, EditText
, ListView
, CheckBox
, RadioButton
, Gallery
, Spinner
, ainsi que les widgets plus spécifiques AutoCompleteTextView
, ImageSwitcher
et TextSwitcher
.
Parmi les mises en page disponibles, citons LinearLayout
, FrameLayout
, RelativeLayout
et d'autres. Pour plus d'exemples, consultez Mises en page courantes.
Si aucun des widgets ni mises en page prédéfinis ne répond à vos besoins, vous pouvez créer votre propre sous-classe View
. Si vous n'avez besoin d'apporter que de petits ajustements à un widget ou à une mise en page existants, vous pouvez sous-classer le widget ou la mise en page et remplacer ses méthodes.
La création de vos propres sous-classes View
vous permet de contrôler précisément l'apparence et la fonction d'un élément à l'écran. Voici quelques exemples de ce que vous pouvez faire avec les vues personnalisées:
-
Vous pouvez créer un type
View
entièrement personnalisé, par exemple une commande de contrôle du volume, rendue à l'aide de graphiques 2D, semblable à une commande électronique analogique. -
Vous pouvez combiner un groupe de composants
View
dans un nouveau composant unique, par exemple pour créer une boîte combinée (combinaison d'une liste pop-up et d'un champ de saisie libre), une commande de sélection à deux volets (un volet gauche et droit avec une liste dans chacun où vous pouvez réaffecter l'élément dans quelle liste), et ainsi de suite. -
Vous pouvez ignorer la façon dont un composant
EditText
est affiché à l'écran. L'application exemple NotePad l'utilise à bon escient pour créer une page de bloc-notes linéaire. - Vous pouvez enregistrer d'autres événements, comme les pressions sur les touches, et les gérer de manière personnalisée, par exemple pour un jeu.
Les sections suivantes expliquent comment créer des vues personnalisées et les utiliser dans votre application. Pour en savoir plus, consultez la classe View
.
L'approche de base
Voici une présentation générale de ce que vous devez savoir pour créer vos propres composants View
:
-
Étendez une classe ou une sous-classe
View
existante avec votre propre classe. -
Remplace certaines méthodes de la super-classe. Les méthodes de super-classe à remplacer commencent par
on
(par exemple,onDraw()
,onMeasure()
etonKeyDown()
). Ce processus est semblable aux événementson
dansActivity
ouListActivity
que vous remplacez pour le cycle de vie et d'autres hooks de fonctionnalité. - Utilisez votre nouvelle classe d'extension. Une fois l'opération terminée, vous pouvez utiliser votre nouvelle classe d'extension à la place de la vue sur laquelle elle était basée.
Composants entièrement personnalisés
Vous pouvez créer des composants graphiques entièrement personnalisés qui s'affichent comme vous le souhaitez. Peut-être souhaitez-vous utiliser un VU-mètre graphique ressemblant à une vieille jauge analogique, ou un affichage de texte qui chante en chœur dans lequel une balle rebondissante se déplace pendant que vous chantez avec une machine de karaoké. Vous voudrez peut-être quelque chose que les composants intégrés ne peuvent pas faire, quelle que soit la façon dont vous les combinez.
Heureusement, vous pouvez créer des composants qui ont l'apparence et le comportement de votre choix, limités uniquement par votre imagination, la taille de l'écran et la puissance de traitement disponible, en gardant à l'esprit que votre application devra peut-être s'exécuter sur un environnement bien moins puissant que votre poste de travail de bureau.
Pour créer un composant entièrement personnalisé, tenez compte des points suivants:
-
La vue la plus générique que vous pouvez étendre est
View
. Vous commencez donc généralement par l'étendre pour créer votre nouveau super composant. - Vous pouvez fournir un constructeur qui peut utiliser des attributs et des paramètres du fichier XML, et utiliser vos propres attributs et paramètres, tels que la couleur et la plage du VU-mètre, ou la largeur et l'amortissement de l'aiguille.
- Vous souhaiterez probablement créer vos propres écouteurs d'événements, accesseurs de propriété et modificateurs, ainsi qu'un comportement plus sophistiqué dans votre classe de composant.
-
Vous souhaiterez certainement remplacer
onMeasure()
. Vous devrez peut-être également ignoreronDraw()
si vous souhaitez que le composant affiche quelque chose. Bien que les deux aient un comportement par défaut, l'élémentonDraw()
par défaut n'a aucun effet, et l'élémentonMeasure()
par défaut définit toujours une taille de 100 x 100, ce qui n'est probablement pas souhaitable. -
Vous pouvez également remplacer d'autres méthodes
on
, si nécessaire.
Étendre onDraw() et onMeasure()
La méthode onDraw()
fournit un Canvas
sur lequel vous pouvez implémenter tout ce que vous souhaitez: graphismes 2D, autres composants standards ou personnalisés, texte stylisé, ou tout autre élément auquel vous pouvez penser.
onMeasure()
est un peu plus complexe. onMeasure()
est un élément essentiel du contrat de rendu entre votre composant et son conteneur. onMeasure()
doit être remplacé pour générer des rapports efficaces et précis sur les mesures des parties qu'il contient. Cette situation est rendue légèrement plus complexe par les exigences de limite du parent, qui sont transmises à la méthode onMeasure()
, et par l'obligation d'appeler la méthode setMeasuredDimension()
avec la largeur et la hauteur mesurées une fois qu'elles ont été calculées. Si vous n'appelez pas cette méthode à partir d'une méthode onMeasure()
remplacée, une exception sera générée au moment de la mesure.
De manière générale, l'implémentation de onMeasure()
se présente comme suit:
-
La méthode
onMeasure()
remplacée est appelée avec des spécifications de largeur et de hauteur, qui sont considérées comme des exigences pour les restrictions sur les mesures de largeur et de hauteur que vous produisez. Les paramètreswidthMeasureSpec
etheightMeasureSpec
sont tous deux des codes entiers représentant des dimensions. Pour en savoir plus sur les types de restrictions auxquels s'appliquent ces spécifications, consultez la documentation de référence sousView.onMeasure(int, int)
. Cette documentation de référence explique également l'ensemble des opérations de mesure. -
La méthode
onMeasure()
de votre composant calcule la largeur et la hauteur de mesure, qui sont nécessaires pour afficher le composant. Elle doit essayer de respecter les spécifications transmises, même si elle peut les dépasser. Dans ce cas, le parent peut choisir l'action à effectuer : rogner, faire défiler, générer une exception ou demander àonMeasure()
de réessayer, éventuellement avec des spécifications de mesure différentes. -
Lorsque la largeur et la hauteur sont calculées, appelez la méthode
setMeasuredDimension(int width, int height)
avec les mesures calculées. Sinon, vous obtiendrez une exception.
Voici un résumé des autres méthodes standards que le framework appelle sur les vues:
Catégorie | Méthodes | Description |
---|---|---|
Création | Constructeurs | Un type de constructeur est appelé lorsque la vue est créée à partir du code, tandis qu'un formulaire est appelé lorsque la vue est gonflée à partir d'un fichier de mise en page. La seconde analyse et applique les attributs définis dans le fichier de mise en page. |
|
Appelée après qu'une vue et tous ses enfants ont été gonflés à partir de XML. | |
Mise en page |
|
Appelée pour déterminer les exigences de taille pour cette vue et tous ses enfants. |
|
Appelée lorsque cette vue doit attribuer une taille et une position à tous ses enfants. | |
|
Appelée lorsque la taille de cette vue est modifiée. | |
Dessin |
|
Appelée lorsque la vue doit afficher son contenu. |
Traitement des événements |
|
Appelée lorsqu'un événement de touche enfoncée se produit. |
|
Appelée lorsqu'un événement "key up" se produit. | |
|
Appelée lorsqu'un événement de mouvement de trackball se produit. | |
|
Appelée lorsqu'un événement de mouvement de l'écran tactile se produit. | |
Objet |
|
Appelée lorsque la vue acquiert ou perd l'attention. |
|
Appelée lorsque la fenêtre contenant la vue gagne ou perd le focus. | |
Association... |
|
Appelée lorsque la vue est associée à une fenêtre. |
|
Appelée lorsque la vue est dissociée de sa fenêtre. | |
|
Appelée lorsque la visibilité de la fenêtre contenant la vue est modifiée. |
Commandes combinées
Si vous ne souhaitez pas créer un composant entièrement personnalisé, mais plutôt un composant réutilisable composé d'un groupe de commandes existantes, il peut être préférable de créer un composant composé (ou commande composé). En résumé, cela rassemble un certain nombre de commandes ou de vues plus atomiques dans un groupe logique d'éléments pouvant être traités comme une seule et même chose.
Par exemple, une boîte combinée peut être une combinaison d'un champ EditText
à une seule ligne et d'un bouton adjacent avec une liste pop-up associée. Si l'utilisateur appuie sur le bouton et sélectionne un élément dans la liste, le champ EditText
est renseigné. Toutefois, il peut également saisir du texte directement dans EditText
s'il le souhaite.
Dans Android, deux autres vues sont facilement disponibles pour effectuer cette opération: Spinner
et AutoCompleteTextView
. Quoi qu'il en soit, ce concept de boîte combinée est un bon exemple.
Pour créer un composant composé, procédez comme suit:
-
Comme pour
Activity
, utilisez l'approche déclarative (basée sur XML) pour créer les composants contenus ou imbriquez-les par programmation à partir de votre code. Le point de départ habituel est uneLayout
. Vous devez donc créer une classe qui étend unLayout
. Dans le cas d'une boîte combinée, vous pouvez utiliser unLinearLayout
à orientation horizontale. Vous pouvez imbriquer d'autres mises en page, de sorte que le composant composé puisse être arbitrairement complexe et structuré. -
Dans le constructeur de la nouvelle classe, prenez tous les paramètres attendus par la super-classe et transmettez-les d'abord au constructeur de super-classe. Vous pouvez ensuite configurer les autres vues à utiliser dans votre nouveau composant. C'est ici que vous créez le champ
EditText
et la liste pop-up. Vous pouvez introduire vos propres attributs et paramètres dans le fichier XML, que votre constructeur peut extraire et utiliser. -
Vous pouvez également créer des écouteurs pour les événements que les vues qu'elles contiennent sont susceptibles de générer. Vous pouvez par exemple utiliser une méthode d'écouteur pour l'écouteur de clics de l'élément de liste afin de mettre à jour le contenu de
EditText
si une liste est sélectionnée. -
Vous pouvez également créer vos propres propriétés avec des accesseurs et des modificateurs. Par exemple, laissez la valeur
EditText
être définie initialement dans le composant et interrogez son contenu si nécessaire. -
Vous pouvez éventuellement remplacer
onDraw()
etonMeasure()
. Ce n'est généralement pas nécessaire lors de l'extension d'unLayout
, car la mise en page présente un comportement par défaut qui fonctionne probablement correctement. -
Vous pouvez éventuellement remplacer d'autres méthodes
on
, telles queonKeyDown()
, par exemple pour choisir certaines valeurs par défaut dans la liste pop-up d'une zone combinée lorsque l'utilisateur appuie sur une touche donnée.
L'utilisation d'un élément Layout
comme base d'une commande personnalisée présente les avantages suivants:
- Vous pouvez spécifier la mise en page à l'aide de fichiers XML déclaratifs, comme pour un écran d'activité. Vous pouvez également créer des vues par programmation et les imbriquer dans la mise en page à partir de votre code.
-
Les méthodes
onDraw()
etonMeasure()
, ainsi que la plupart des autres méthodeson
, ont un comportement approprié. Vous n'avez donc pas besoin de les remplacer. - Vous pouvez créer rapidement des vues composées arbitrairement complexes et les réutiliser comme s'il s'agissait d'un seul composant.
Modifier un type de vue existant
Si un composant est semblable à ce que vous souhaitez, vous pouvez l'étendre et remplacer le comportement que vous souhaitez modifier. Vous pouvez effectuer les mêmes opérations qu'avec un composant entièrement personnalisé, mais en commençant par une classe plus spécialisée dans la hiérarchie View
, vous pouvez obtenir sans frais un comportement qui répond à vos besoins.
Par exemple, l'application exemple NotePad illustre de nombreux aspects de l'utilisation de la plate-forme Android. Il s'agit notamment d'étendre une vue EditText
pour créer un bloc-notes à lignes. Ce n'est pas un exemple parfait et les API permettant de le faire peuvent changer, mais cela illustre les principes.
Si vous ne l'avez pas déjà fait, importez l'exemple NotePad dans Android Studio ou consultez la source à l'aide du lien fourni. Consultez en particulier la définition de LinedEditText
dans le fichier NoteEditor.java
.
Voici quelques points à noter dans ce fichier:
-
Définition
La classe est définie avec la ligne suivante:
public static class LinedEditText extends EditText
LinedEditText
est défini comme une classe interne dans l'activitéNoteEditor
, mais il est public afin qu'il soit accessible en tant queNoteEditor.LinedEditText
depuis l'extérieur de la classeNoteEditor
.De plus,
LinedEditText
eststatic
, ce qui signifie qu'il ne génère pas les "méthodes synthétiques" qui lui permettent d'accéder aux données de la classe parente. Cela signifie qu'elle se comporte comme une classe distincte plutôt que comme quelque chose de fortement lié àNoteEditor
. Il s'agit d'un moyen plus propre de créer des classes internes si elles n'ont pas besoin d'accéder à l'état depuis la classe externe. La classe générée reste petite et peut être utilisée facilement à partir d'autres classes.LinedEditText
étendEditText
, qui est la vue à personnaliser dans ce cas. Lorsque vous avez terminé, la nouvelle classe peut remplacer une vueEditText
normale. -
Initialisation de classe
Comme toujours, le super est appelé en premier. Il ne s'agit pas d'un constructeur par défaut, mais d'un constructeur paramétré. Le
EditText
est créé avec ces paramètres lorsqu'il est gonflé à partir d'un fichier de mise en page XML. Par conséquent, le constructeur doit les récupérer et les transmettre également au constructeur de super-classe. -
Méthodes remplacées
Cet exemple ne remplace que la méthode
onDraw()
, mais vous devrez peut-être en remplacer d'autres lorsque vous créez vos propres composants personnalisés.Pour cet exemple, le remplacement de la méthode
onDraw()
vous permet de peindre les lignes bleues sur le canevas de la vueEditText
. Le canevas est transmis à la méthodeonDraw()
remplacée. La méthodesuper.onDraw()
est appelée avant sa fin. La méthode de super-classe doit être appelée. Dans ce cas, appelez-le à la fin après avoir peint les lignes que vous souhaitez inclure. -
Composant personnalisé
Vous disposez maintenant de votre composant personnalisé, mais comment pouvez-vous l'utiliser ? Dans l'exemple NotePad, le composant personnalisé est utilisé directement à partir de la mise en page déclarative. Examinez donc
note_editor.xml
dans le dossierres/layout
:<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.example.android.notepad.NoteEditor$LinedEditText" android:id="@+id/note" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:padding="5dp" android:scrollbars="vertical" android:fadingEdge="vertical" android:gravity="top" android:textSize="22sp" android:capitalize="sentences" />
Le composant personnalisé est créé sous la forme d'une vue générique dans le fichier XML, et la classe est spécifiée à l'aide du package complet. La classe interne que vous définissez est référencée à l'aide de la notation
NoteEditor$LinedEditText
, qui est un moyen standard de faire référence aux classes internes dans le langage de programmation Java.Si votre composant de vue personnalisée n'est pas défini en tant que classe interne, vous pouvez le déclarer avec le nom de l'élément XML et exclure l'attribut
class
. Exemple:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
Notez que la classe
LinedEditText
est maintenant un fichier de classe distinct. Lorsque la classe est imbriquée dans la classeNoteEditor
, cette technique ne fonctionne pas.Les autres attributs et paramètres de la définition sont ceux qui sont transmis au constructeur de composant personnalisé, puis transmis au constructeur
EditText
. Ce sont donc les mêmes paramètres que vous utilisez pour une vueEditText
. Il est également possible d'ajouter vos propres paramètres.
Créer des composants personnalisés n'est pas aussi compliqué que nécessaire.
Un composant plus sophistiqué peut remplacer encore plus de méthodes on
et introduire ses propres méthodes d'assistance, ce qui personnalise considérablement ses propriétés et son comportement. La seule limite est votre imagination et ce que vous avez besoin que le composant fasse.