Cet atelier de programmation fait partie du cours Développement Android avancé en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre, mais ce n'est pas obligatoire. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Android avancé en Kotlin.
Introduction
Android propose un grand nombre de sous-classes View, telles que Button, TextView, EditText, ImageView, CheckBox ou RadioButton. Vous pouvez utiliser ces sous-classes pour créer une UI qui permet l'interaction de l'utilisateur et affiche des informations dans votre application. Si aucune des sous-classes View ne répond à vos besoins, vous pouvez créer une sous-classe View appelée vue personnalisée .
Pour créer une vue personnalisée, vous pouvez étendre une sous-classe View existante (telle que Button ou EditText) ou créer votre propre sous-classe de View. En étendant directement View, vous pouvez créer un élément d'UI interactif de n'importe quelle taille et forme en remplaçant la méthode onDraw() pour que View le dessine.
Une fois que vous avez créé une vue personnalisée, vous pouvez l'ajouter à vos mises en page d'activité de la même manière que vous ajouteriez un TextView ou un Button.
Cette leçon vous explique comment créer une vue personnalisée à partir de zéro en étendant View.
Ce que vous devez déjà savoir
- Savoir créer une application avec une activité et l'exécuter à l'aide d'Android Studio
Points abordés
- Comment étendre
Viewpour créer une vue personnalisée. - Comment dessiner une vue personnalisée de forme circulaire.
- Comment utiliser des écouteurs pour gérer l'interaction de l'utilisateur avec la vue personnalisée.
- Vous savez utiliser une vue personnalisée dans une mise en page.
Objectifs de l'atelier
L'application CustomFanController montre comment créer une sous-classe de vue personnalisée en étendant la classe View. La nouvelle sous-classe est appelée DialView.
L'application affiche un élément d'interface utilisateur circulaire qui ressemble à une commande de ventilateur physique, avec des paramètres pour éteint (0), faible (1), moyen (2) et élevé (3). Lorsque l'utilisateur appuie sur la vue, l'indicateur de sélection passe à la position suivante : 0-1-2-3, puis revient à 0. De plus, si la sélection est égale ou supérieure à 1, la couleur d'arrière-plan de la partie circulaire de la vue passe du gris au vert (indiquant que la puissance du ventilateur est activée).


Les vues sont les composants de base de l'UI d'une application. La classe View fournit de nombreuses sous-classes, appelées widgets d'UI, qui couvrent la plupart des besoins de l'interface utilisateur d'une application Android standard.
Les blocs de construction de l'UI, tels que Button et TextView, sont des sous-classes qui étendent la classe View. Pour gagner du temps et réduire les efforts de développement, vous pouvez étendre l'une de ces sous-classes View. La vue personnalisée hérite de l'apparence et du comportement de son parent. Vous pouvez modifier le comportement ou l'aspect de l'apparence selon vos besoins. Par exemple, si vous étendez EditText pour créer une vue personnalisée, la vue se comporte comme une vue EditText, mais peut également être personnalisée pour afficher, par exemple, un bouton X qui efface le texte du champ de saisie de texte.
Vous pouvez étendre n'importe quelle sous-classe View, telle que EditText, pour obtenir une vue personnalisée. Choisissez celle qui se rapproche le plus de ce que vous souhaitez accomplir. Vous pouvez ensuite utiliser la vue personnalisée comme n'importe quelle autre sous-classe View dans une ou plusieurs mises en page en tant qu'élément XML avec des attributs.
Pour créer votre propre vue personnalisée de A à Z, étendez la classe View elle-même. Votre code remplace les méthodes View pour définir l'apparence et la fonctionnalité de la vue. Pour créer votre propre vue personnalisée, vous devez dessiner l'intégralité de l'élément d'UI de n'importe quelle taille et forme à l'écran. Si vous créez une sous-classe d'une vue existante telle que Button, cette classe gère le dessin pour vous. (Vous en apprendrez davantage sur le dessin dans la suite de cet atelier de programmation.)
Pour créer une vue personnalisée, suivez ces étapes générales :
- Créez une classe de vue personnalisée qui étend
Viewou une sous-classeView(telle queButtonouEditText). - Si vous étendez une sous-classe
Viewexistante, remplacez uniquement le comportement ou les aspects de l'apparence que vous souhaitez modifier. - Si vous étendez la classe
View, dessinez la forme de la vue personnalisée et contrôlez son apparence en remplaçant les méthodesViewtelles queonDraw()etonMeasure()dans la nouvelle classe. - Ajoutez du code pour répondre à l'interaction de l'utilisateur et, si nécessaire, redessinez la vue personnalisée.
- Utilisez la classe de vue personnalisée comme widget d'UI dans la mise en page XML de votre activité. Vous pouvez également définir des attributs personnalisés pour la vue afin de la personnaliser dans différentes mises en page.
Dans cette tâche, vous allez :
- Créez une application avec un
ImageViewcomme espace réservé temporaire pour la vue personnalisée. - Étendez
Viewpour créer la vue personnalisée. - Initialisez la vue personnalisée avec les valeurs de dessin et de peinture.
Étape 1 : Créez une application avec un espace réservé ImageView
- Créez une application Kotlin intitulée
CustomFanControllerà l'aide du modèle Activité vide. Assurez-vous que le nom du package estcom.example.android.customfancontroller. - Ouvrez
activity_main.xmldans l'onglet Texte pour modifier le code XML. - Remplacez le
TextViewexistant par ce code. Ce texte sert de libellé dans l'activité de la vue personnalisée.
<TextView
android:id="@+id/customViewLabel"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@android:color/black"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="24dp"
android:text="Fan Control"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>- Ajoutez cet élément
ImageViewà la mise en page. Il s'agit d'un espace réservé pour la vue personnalisée que vous allez créer dans cet atelier de programmation.
<ImageView
android:id="@+id/dialView"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@android:color/darker_gray"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"/>- Extrayez les ressources de chaîne et de dimension dans les deux éléments d'interface utilisateur.
- Cliquez sur l'onglet Conception. La mise en page doit se présenter comme suit :

Étape 2 : Créer votre classe de vue personnalisée
- Créez une classe Kotlin appelée
DialView. - Modifiez la définition de la classe pour étendre
View. Importezandroid.view.Viewlorsque vous y êtes invité. - Cliquez sur
View, puis sur l'ampoule rouge. Choisissez Add Android View constructors using '@JvmOverloads' (Ajouter des constructeurs Android View à l'aide de "@JvmOverloads"). Android Studio ajoute le constructeur à partir de la classeView. L'annotation@JvmOverloadsindique au compilateur Kotlin de générer des surcharges pour cette fonction qui remplacent les valeurs de paramètre par défaut.
class DialView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {- Au-dessus de la définition de la classe
DialView, juste en dessous des importations, ajoutez unenumde premier niveau pour représenter les vitesses de ventilateur disponibles. Notez que ceenumest de typeInt, car les valeurs sont des ressources de chaîne plutôt que des chaînes réelles. Android Studio affichera des erreurs pour les ressources de chaîne manquantes dans chacune de ces valeurs. Vous les corrigerez lors d'une étape ultérieure.
private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
}- Sous
enum, ajoutez ces constantes. Vous les utiliserez pour dessiner les indicateurs et les libellés.
private const val RADIUS_OFFSET_LABEL = 30
private const val RADIUS_OFFSET_INDICATOR = -35- Dans la classe
DialView, définissez plusieurs variables dont vous avez besoin pour dessiner la vue personnalisée. Si vous y êtes invité, importezandroid.graphics.PointF.
private var radius = 0.0f // Radius of the circle.
private var fanSpeed = FanSpeed.OFF // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)radiuscorrespond au rayon actuel du cercle. Cette valeur est définie lorsque la vue est dessinée à l'écran.fanSpeedcorrespond à la vitesse actuelle du ventilateur, qui est l'une des valeurs de l'énumérationFanSpeed. La valeur par défaut estOFF.- Enfin
postPositionest un point X,Y qui sera utilisé pour dessiner plusieurs éléments de la vue à l'écran.
Ces valeurs sont créées et initialisées ici au lieu de l'être lorsque la vue est réellement dessinée, afin de garantir que l'étape de dessin proprement dite s'exécute le plus rapidement possible.
- Toujours dans la définition de la classe
DialView, initialisez un objetPaintavec quelques styles de base. Importezandroid.graphics.Paintetandroid.graphics.Typefacelorsque vous y êtes invité. Comme pour les variables, ces styles sont initialisés ici pour accélérer l'étape de dessin.
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
textAlign = Paint.Align.CENTER
textSize = 55.0f
typeface = Typeface.create( "", Typeface.BOLD)
}- Ouvrez
res/values/strings.xmlet ajoutez les ressources de chaîne pour les vitesses du ventilateur :
<string name="fan_off">off</string>
<string name="fan_low">1</string>
<string name="fan_medium">2</string>
<string name="fan_high">3</string>Une fois que vous avez créé une vue personnalisée, vous devez pouvoir la dessiner. Lorsque vous étendez une sous-classe View telle que EditText, cette sous-classe définit l'apparence et les attributs de la vue, et se dessine à l'écran. Par conséquent, vous n'avez pas besoin d'écrire de code pour dessiner la vue. Vous pouvez remplacer les méthodes du parent pour personnaliser votre vue.
Si vous créez votre propre vue à partir de zéro (en étendant View), vous êtes responsable du dessin de la vue entière chaque fois que l'écran est actualisé, et du remplacement des méthodes View qui gèrent le dessin. Pour dessiner correctement une vue personnalisée qui étend View, vous devez :
- Calculez la taille de la vue lorsqu'elle apparaît pour la première fois, et chaque fois que sa taille change, en remplaçant la méthode
onSizeChanged(). - Remplacez la méthode
onDraw()pour dessiner la vue personnalisée, en utilisant un objetCanvasstylisé par un objetPaint. - Appelez la méthode
invalidate()lorsque vous répondez à un clic de l'utilisateur qui modifie la façon dont la vue est dessinée pour invalider l'intégralité de la vue, ce qui force un appel àonDraw()pour redessiner la vue.
La méthode onDraw() est appelée chaque fois que l'écran est actualisé, ce qui peut se produire plusieurs fois par seconde. Pour des raisons de performances et pour éviter les problèmes visuels, vous devez travailler le moins possible dans onDraw(). En particulier, ne placez pas d'allocations dans onDraw(), car elles peuvent entraîner un garbage collection qui peut provoquer un bégaiement visuel.
Les classes Canvas et Paint offrent un certain nombre de raccourcis de dessin utiles :
- Dessinez du texte à l'aide de
drawText(). Spécifiez la typographie en appelantsetTypeface()et la couleur du texte en appelantsetColor(). - Dessinez des formes primitives à l'aide de
drawRect(),drawOval()etdrawArc(). Déterminez si les formes sont remplies, décrites ou les deux en appelantsetStyle(). - Dessinez des bitmaps à l'aide de
drawBitmap().
Vous en apprendrez davantage sur Canvas et Paint dans un prochain atelier de programmation. Pour en savoir plus sur la façon dont Android dessine les vues, consultez Dessiner des vues via Android.
Dans cette tâche, vous allez dessiner la vue personnalisée du contrôleur de ventilateur sur l'écran (le cadran lui-même, l'indicateur de position actuelle et les libellés des indicateurs) avec les méthodes onSizeChanged() et onDraw(). Vous allez également créer une méthode d'assistance, computeXYForSpeed(),, pour calculer la position X,Y actuelle du libellé de l'indicateur sur le cadran.
Étape 1 : Calculer les positions et dessiner la vue
- Dans la classe
DialView, sous les initialisations, remplacez la méthodeonSizeChanged()de la classeViewpour calculer la taille du cadran de la vue personnalisée. Importezkotlin.math.minlorsque vous y êtes invité.
La méthodeonSizeChanged()est appelée chaque fois que la taille de la vue change, y compris la première fois qu'elle est dessinée lorsque la mise en page est gonflée. RemplacezonSizeChanged()pour calculer les positions, les dimensions et toute autre valeur liée à la taille de votre vue personnalisée, au lieu de les recalculer à chaque fois que vous dessinez. Dans ce cas, vous utilisezonSizeChanged()pour calculer le rayon actuel de l'élément circulaire du sélecteur.
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
radius = (min(width, height) / 2.0 * 0.8).toFloat()
}- Sous
onSizeChanged(), ajoutez ce code pour définir une fonction d'extensioncomputeXYForSpeed()pour la classePointF. Importezkotlin.math.cosetkotlin.math.sinlorsque vous y êtes invité. Cette fonction d'extension de la classePointFcalcule les coordonnées X et Y à l'écran pour le libellé de texte et l'indicateur actuel (0, 1, 2 ou 3), en fonction de la positionFanSpeedactuelle et du rayon du sélecteur. Vous l'utiliserez dansonDraw().
private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
// Angles are in radians.
val startAngle = Math.PI * (9 / 8.0)
val angle = startAngle + pos.ordinal * (Math.PI / 4)
x = (radius * cos(angle)).toFloat() + width / 2
y = (radius * sin(angle)).toFloat() + height / 2
}- Remplacez la méthode
onDraw()pour afficher la vue à l'écran avec les classesCanvasetPaint. Lorsque vous y êtes invité, importezandroid.graphics.Canvas. Voici le remplacement du squelette :
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}- Dans
onDraw(), ajoutez cette ligne pour définir la couleur de peinture sur gris (Color.GRAY) ou vert (Color.GREEN) selon que la vitesse du ventilateur estOFFou toute autre valeur. Lorsque vous y êtes invité, importezandroid.graphics.Color.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN- Ajoutez ce code pour dessiner un cercle pour le cadran, avec la méthode
drawCircle(). Cette méthode utilise la largeur et la hauteur de la vue actuelle pour trouver le centre du cercle, son rayon et la couleur de peinture actuelle. Les propriétéswidthetheightsont des membres de la superclasseViewet indiquent les dimensions actuelles de la vue.
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)- Ajoutez le code suivant pour dessiner un cercle plus petit pour le repère de l'indicateur de vitesse du ventilateur, également avec la méthode
drawCircle(). Cette partie utilisePointF.Méthode d'extensioncomputeXYforSpeed()pour calculer les coordonnées X et Y du centre de l'indicateur en fonction de la vitesse du ventilateur actuelle.
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)- Enfin, dessinez les libellés de vitesse du ventilateur (0, 1, 2, 3) aux emplacements appropriés autour du cadran. Cette partie de la méthode appelle à nouveau
PointF.computeXYForSpeed()pour obtenir la position de chaque libellé et réutilise l'objetpointPositionà chaque fois pour éviter les allocations. UtilisezdrawText()pour dessiner les libellés.
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}Une fois terminée, la méthode onDraw() se présente comme suit :
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}
}Étape 2 : Ajouter la vue à la mise en page
Pour ajouter une vue personnalisée à l'UI d'une application, spécifiez-la comme élément dans la mise en page XML de l'activité. Contrôlez son apparence et son comportement avec les attributs des éléments XML, comme vous le feriez pour n'importe quel autre élément d'UI.
- Dans
activity_main.xml, remplacez la baliseImageViewdedialViewparcom.example.android.customfancontroller.DialViewet supprimez l'attributandroid:background. Les classesDialViewetImageViewd'origine héritent des attributs standards de la classeView. Il n'est donc pas nécessaire de modifier les autres attributs. Le nouvel élémentDialViewse présente comme suit :
<com.example.android.customfancontroller.DialView
android:id="@+id/dialView"
android:layout_width="@dimen/fan_dimen"
android:layout_height="@dimen/fan_dimen"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" />- Exécutez l'application. La vue de contrôle du ventilateur s'affiche dans l'activité.

La dernière tâche consiste à permettre à votre vue personnalisée d'effectuer une action lorsque l'utilisateur appuie dessus. Chaque pression doit déplacer l'indicateur de sélection vers la position suivante : désactivé-1-2-3 et retour à désactivé. De plus, si la sélection est définie sur 1 ou plus, changez l'arrière-plan de gris à vert pour indiquer que le ventilateur est en marche.
Pour rendre votre vue personnalisée cliquable, vous devez :
- Définissez la propriété
isClickablede la vue surtrue. Cela permet à votre vue personnalisée de répondre aux clics. - Implémentez
performClick()de la classeViewpour effectuer des opérations lorsque l'utilisateur clique sur la vue. - Appelez la méthode
invalidate(). Cela indique au système Android d'appeler la méthodeonDraw()pour redessiner la vue.
Normalement, avec une vue Android standard, vous implémentez OnClickListener() pour effectuer une action lorsque l'utilisateur clique sur cette vue. Pour une vue personnalisée, vous implémentez plutôt la méthode performClick() de la classe View et appelez super.performClick(). La méthode performClick() par défaut appelle également onClickListener(). Vous pouvez donc ajouter vos actions à performClick() et laisser onClickListener() disponible pour une personnalisation ultérieure par vous ou d'autres développeurs susceptibles d'utiliser votre vue personnalisée.
- Dans
DialView.kt, à l'intérieur de l'énumérationFanSpeed, ajoutez une fonction d'extensionnext()qui modifie la vitesse actuelle du ventilateur pour passer à la vitesse suivante de la liste (deOFFàLOW,MEDIUMetHIGH, puis de nouveau àOFF). L'énumération complète ressemble maintenant à ceci :
private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
fun next() = when (this) {
OFF -> LOW
LOW -> MEDIUM
MEDIUM -> HIGH
HIGH -> OFF
}
}- Dans la classe
DialView, juste avant la méthodeonSizeChanged(), ajoutez un blocinit(). Si vous définissez la propriétéisClickablede la vue sur "true", cette vue peut accepter les entrées utilisateur.
init {
isClickable = true
}- Sous
init(),, remplacez la méthodeperformClick()par le code ci-dessous.
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
contentDescription = resources.getString(fanSpeed.label)
invalidate()
return true
}Appel à super.performClick() doit avoir lieu en premier, ce qui permet les événements d'accessibilité ainsi que les appels onClickListener().
Les deux lignes suivantes incrémentent la vitesse du ventilateur avec la méthode next() et définissent la description du contenu de la vue sur la ressource de chaîne représentant la vitesse actuelle (arrêt, 1, 2 ou 3).
Enfin, la méthode invalidate() invalide l'intégralité de la vue, ce qui force un appel à onDraw() pour redessiner la vue. Si quelque chose change dans votre vue personnalisée pour une raison quelconque, y compris une interaction utilisateur, et que la modification doit être affichée, appelez invalidate()..
- Exécutez l'application. Appuyez sur l'élément
DialViewpour faire passer l'indicateur de "off" à 1. Le cadran doit devenir vert. À chaque pression, l'indicateur doit passer à la position suivante. Lorsque l'indicateur est à nouveau désactivé, le cadran doit redevenir gris.


Cet exemple montre les mécanismes de base de l'utilisation d'attributs personnalisés avec votre vue personnalisée. Vous définissez des attributs personnalisés pour la classe DialView avec une couleur différente pour chaque position du sélecteur.
- Créez et ouvrez
res/values/attrs.xml. - Dans
<resources>, ajoutez un élément de ressource<declare-styleable>. - Dans l'élément de ressource
<declare-styleable>, ajoutez trois élémentsattr, un pour chaque attribut, avec unnameet unformat.formatest comme un type, et dans ce cas, il s'agit decolor.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DialView">
<attr name="fanColor1" format="color" />
<attr name="fanColor2" format="color" />
<attr name="fanColor3" format="color" />
</declare-styleable>
</resources>- Ouvrez le fichier de mise en page
activity_main.xml. - Dans
DialView, ajoutez des attributs pourfanColor1,fanColor2etfanColor3, puis définissez leurs valeurs sur les couleurs indiquées ci-dessous. Utilisezapp:comme préface pour l'attribut personnalisé (comme dansapp:fanColor1) plutôt queandroid:, car vos attributs personnalisés appartiennent à l'espace de nomsschemas.android.com/apk/res/your_app_package_nameplutôt qu'à l'espace de nomsandroid.
app:fanColor1="#FFEB3B"
app:fanColor2="#CDDC39"
app:fanColor3="#009688"Pour utiliser les attributs de votre classe DialView, vous devez les récupérer. Ils sont stockés dans un AttributeSet, qui est remis à votre cours lors de sa création, s'il existe. Vous récupérez les attributs dans init et attribuez les valeurs des attributs à des variables locales pour la mise en cache.
- Ouvrez le fichier de classe
DialView.kt. - Dans
DialView, déclarez des variables pour mettre en cache les valeurs des attributs.
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0- Dans le bloc
init, ajoutez le code suivant à l'aide de la fonction d'extensionwithStyledAttributes. Vous fournissez les attributs et la vue, et définissez vos variables locales. L'importation dewithStyledAttributesimportera également la fonctiongetColor()appropriée.
context.withStyledAttributes(attrs, R.styleable.DialView) {
fanSpeedLowColor = getColor(R.styleable.DialView_fanColor1, 0)
fanSpeedMediumColor = getColor(R.styleable.DialView_fanColor2, 0)
fanSeedMaxColor = getColor(R.styleable.DialView_fanColor3, 0)
}- Utilisez les variables locales dans
onDraw()pour définir la couleur du sélecteur en fonction de la vitesse actuelle du ventilateur. Remplacez la ligne où la couleur de peinture est définie (paint.color=if(fanSpeed== FanSpeed.OFF) Color.GRAYelseColor.GREEN) par le code ci-dessous.
paint.color = when (fanSpeed) {
FanSpeed.OFF -> Color.GRAY
FanSpeed.LOW -> fanSpeedLowColor
FanSpeed.MEDIUM -> fanSpeedMediumColor
FanSpeed.HIGH -> fanSeedMaxColor
} as Int- Exécutez votre application, cliquez sur le sélecteur et le paramètre de couleur devrait être différent pour chaque position, comme indiqué ci-dessous.
|
|
|
|
Pour en savoir plus sur les attributs de vue personnalisés, consultez Créer une classe de vue.
L'accessibilité est un ensemble de techniques de conception, d'implémentation et de test qui permettent à votre application d'être utilisable par tous, y compris par les personnes ayant un handicap.
Les handicaps courants susceptibles d'affecter l'utilisation d'un appareil Android sont, par exemple, la cécité, les déficiences visuelles, le daltonisme, la surdité ou la perte d'audition, ainsi que la réduction de l'habileté motrice. Lorsque vous développez vos applications en gardant l'accessibilité à l'esprit, vous améliorez l'expérience utilisateur non seulement pour les personnes souffrant de ces handicaps, mais aussi pour tous vos autres utilisateurs.
Android fournit plusieurs fonctionnalités d'accessibilité par défaut dans les vues d'UI standards telles que TextView et Button. Toutefois, lorsque vous créez une vue personnalisée, vous devez réfléchir à la manière dont elle fournira des fonctionnalités accessibles, telles que des descriptions vocales du contenu à l'écran.
Dans cette tâche, vous allez découvrir TalkBack, le lecteur d'écran d'Android, et modifier votre application pour inclure des conseils et des descriptions vocaux pour la vue personnalisée DialView.
Étape 1 : Explorer TalkBack
TalkBack est le lecteur d'écran intégré à Android. Lorsque TalkBack est activé, l'utilisateur peut interagir avec son appareil Android sans voir l'écran, car Android décrit les éléments de l'écran à voix haute. Les utilisateurs ayant une déficience visuelle peuvent utiliser votre application grâce à TalkBack.
Dans cette tâche, vous allez activer TalkBack pour comprendre comment fonctionnent les lecteurs d'écran et comment naviguer dans les applications.
- Sur un appareil ou un émulateur Android, accédez à Paramètres > Accessibilité > TalkBack.
- Appuyez sur le bouton bascule Activé/Désactivé pour activer TalkBack.
- Appuyez sur OK pour confirmer les autorisations.
- Si vous y êtes invité, confirmez le mot de passe de votre appareil. Si c'est la première fois que vous exécutez TalkBack, un tutoriel se lance. (Il est possible que le tutoriel ne soit pas disponible sur les appareils plus anciens.)
- Il peut être utile de parcourir le tutoriel les yeux fermés. Pour rouvrir le tutoriel ultérieurement, accédez à Paramètres > Accessibilité > TalkBack > Paramètres > Lancer le tutoriel TalkBack.
- Compilez et exécutez l'application
CustomFanController, ou ouvrez-la avec le bouton Aperçu ou Récents sur votre appareil. Lorsque TalkBack est activé, vous remarquerez que le nom de l'application est annoncé, ainsi que le texte du libelléTextView("Contrôle du ventilateur"). Toutefois, si vous appuyez sur la vueDialViewelle-même, aucune information n'est énoncée sur l'état de la vue (le paramètre actuel du sélecteur) ni sur l'action qui aura lieu lorsque vous appuierez sur la vue pour l'activer.
Étape 2 : Ajouter des descriptions de contenu pour les libellés des sélecteurs
Les descriptions de contenu décrivent la signification et l'objectif des vues dans votre application. Ces libellés permettent aux lecteurs d'écran tels que la fonctionnalité TalkBack d'Android d'expliquer précisément la fonction de chaque élément. Pour les vues statiques telles que ImageView, vous pouvez ajouter la description du contenu à la vue dans le fichier de mise en page avec l'attribut contentDescription. Les vues de texte (TextView et EditText) utilisent automatiquement le texte de la vue comme description du contenu.
Pour la vue de contrôle personnalisé du ventilateur, vous devez mettre à jour dynamiquement la description du contenu chaque fois que l'utilisateur clique sur la vue, afin d'indiquer le réglage actuel du ventilateur.
- En bas de la classe
DialView, déclarez une fonctionupdateContentDescription()sans arguments ni type de retour.
fun updateContentDescription() {
}- Dans
updateContentDescription(), remplacez la propriétécontentDescriptionde la vue personnalisée par la ressource de chaîne associée à la vitesse du ventilateur actuelle (arrêt, 1, 2 ou 3). Il s'agit des mêmes libellés que ceux utilisés dansonDraw()lorsque le sélecteur est affiché à l'écran.
fun updateContentDescription() {
contentDescription = resources.getString(fanSpeed.label)
}- Faites défiler la page jusqu'au bloc
init(), puis ajoutez un appel àupdateContentDescription()à la fin de ce bloc. Cela permet d'initialiser la description du contenu lorsque la vue est initialisée.
init {
isClickable = true
// ...
updateContentDescription()
}- Ajoutez un autre appel à
updateContentDescription()dans la méthodeperformClick(), juste avantinvalidate().
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
updateContentDescription()
invalidate()
return true
}- Compilez et exécutez l'application, et assurez-vous que TalkBack est activé. Appuyez pour modifier le paramètre de la vue du sélecteur et remarquez que TalkBack annonce désormais le libellé actuel (désactivé, 1, 2, 3) ainsi que la phrase "Appuyez deux fois pour activer".
Étape 3 : Ajouter des informations pour l'action de clic
Vous pourriez vous arrêter là et votre vue serait utilisable dans TalkBack. Toutefois, il serait utile que votre vue indique non seulement qu'elle peut être activée ("Appuyez deux fois pour activer"), mais aussi ce qui se passera lorsqu'elle le sera ("Appuyez deux fois pour modifier" ou "Appuyez deux fois pour réinitialiser").
Pour ce faire, vous ajoutez des informations sur l'action de la vue (ici, une action de clic ou d'appui) à un objet d'informations de nœud d'accessibilité, par le biais d'un délégué d'accessibilité. Un délégué d'accessibilité vous permet de personnaliser les fonctionnalités d'accessibilité de votre application par composition (plutôt que par héritage).
Pour cette tâche, vous utiliserez les classes d'accessibilité des bibliothèques Android Jetpack (androidx.*) afin d'assurer la rétrocompatibilité.
- Dans
DialView.kt, dans le blocinit, définissez un délégué d'accessibilité sur la vue en tant que nouvel objetAccessibilityDelegateCompat. Importezandroidx.core.view.ViewCompatetandroidx.core.view.AccessibilityDelegateCompatlorsque vous y êtes invité. Cette stratégie permet la plus grande rétrocompatibilité dans votre application.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
})- Dans l'objet
AccessibilityDelegateCompat, remplacez la fonctiononInitializeAccessibilityNodeInfo()par un objetAccessibilityNodeInfoCompatet appelez la méthode du super. Importezandroidx.core.view.accessibility.AccessibilityNodeInfoCompatlorsque vous y êtes invité.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
}
})Chaque vue comporte un arbre de nœuds d'accessibilité, qui peut ou non correspondre aux composants de mise en page réels de la vue. Les services d'accessibilité d'Android parcourent ces nœuds pour obtenir des informations sur la vue (par exemple, des descriptions de contenu vocales ou des actions possibles sur cette vue). Lorsque vous créez une vue personnalisée, vous devrez peut-être également remplacer les informations sur le nœud afin de fournir des informations personnalisées pour l'accessibilité. Dans ce cas, vous allez remplacer les informations du nœud pour indiquer qu'il existe des informations personnalisées pour l'action de la vue.
- Dans
onInitializeAccessibilityNodeInfo(), créez un objetAccessibilityNodeInfoCompat.AccessibilityActionCompatet attribuez-le à la variablecustomClick. Transmettez la constanteAccessibilityNodeInfo.ACTION_CLICKet une chaîne d'espace réservé au constructeur. Lorsque vous y êtes invité, importezAccessibilityNodeInfo.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
"placeholder"
)
}
})La classe AccessibilityActionCompat représente une action sur une vue à des fins d'accessibilité. Une action typique est un clic ou un appui, comme vous l'utilisez ici, mais d'autres actions peuvent inclure la prise ou la perte du focus, une opération du presse-papiers (couper/copier/coller) ou le défilement dans la vue. Le constructeur de cette classe nécessite une constante d'action (ici, AccessibilityNodeInfo.ACTION_CLICK) et une chaîne utilisée par TalkBack pour indiquer l'action.
- Remplacez la chaîne
"placeholder"par un appel àcontext.getString()pour récupérer une ressource de chaîne. Pour la ressource spécifique, testez la vitesse actuelle du ventilateur. Si la vitesse est actuellementFanSpeed.HIGH, la chaîne est"Reset". Si la vitesse du ventilateur est différente, la chaîne est"Change.". Vous créerez ces ressources de chaîne à une étape ultérieure.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH) R.string.change else R.string.reset)
)
}
})- Après la parenthèse fermante de la définition
customClick, utilisez la méthodeaddAction()pour ajouter la nouvelle action d'accessibilité à l'objet d'informations sur le nœud.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH)
R.string.change else R.string.reset)
)
info.addAction(customClick)
}
})- Dans
res/values/strings.xml, ajoutez les ressources de chaîne pour "Change" (Modifier) et "Reset" (Réinitialiser).
<string name="change">Change</string>
<string name="reset">Reset</string>- Compilez et exécutez l'application, puis assurez-vous que TalkBack est activé. Notez que la phrase "Appuyer deux fois pour activer" est désormais remplacée par "Appuyer deux fois pour modifier" (si la vitesse du ventilateur est inférieure à élevée ou 3) ou "Appuyer deux fois pour réinitialiser" (si la vitesse du ventilateur est déjà élevée ou 3). Notez que l'invite "Appuyez deux fois pour…" est fournie par le service TalkBack lui-même.
Téléchargez le code de l'atelier de programmation terminé.
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-custom-views
Vous pouvez également télécharger le dépôt sous forme de fichier ZIP, le décompresser et l'ouvrir dans Android Studio.
- Pour créer une vue personnalisée qui hérite de l'apparence et du comportement d'une sous-classe
Viewtelle queEditText, ajoutez une classe qui étend cette sous-classe et apportez des modifications en remplaçant certaines méthodes de la sous-classe. - Pour créer une vue personnalisée de n'importe quelle taille et forme, ajoutez une classe qui étend
View. - Remplacez les méthodes
Viewtelles queonDraw()pour définir la forme et l'apparence de base de la vue. - Utilisez
invalidate()pour forcer le dessin ou le redessin de la vue. - Pour optimiser les performances, allouez des variables et attribuez les valeurs requises pour le dessin et la peinture avant de les utiliser dans
onDraw(), par exemple lors de l'initialisation des variables membres. - Remplacez
performClick()parOnClickListener() dans la vue personnalisée pour fournir le comportement interactif de la vue. Cela permet à vous ou à d'autres développeurs Android qui peuvent utiliser votre classe de vue personnalisée d'utiliseronClickListener()pour fournir un comportement supplémentaire. - Ajoutez la vue personnalisée à un fichier de mise en page XML avec des attributs pour définir son apparence, comme vous le feriez avec d'autres éléments d'interface utilisateur.
- Créez le fichier
attrs.xmldans le dossiervaluespour définir des attributs personnalisés. Vous pouvez ensuite utiliser les attributs personnalisés pour la vue personnalisée dans le fichier de mise en page XML.
Cours Udacity :
Documentation pour les développeurs Android :
- Créer des vues personnalisées
@JvmOverloads- Composants personnalisés
- Dessiner des vues via Android
onMeasure()onSizeChanged()onDraw()CanvasPaintdrawText()setTypeface()setColor()drawRect()drawOval()drawArc()drawBitmap()setStyle()invalidate()- Afficher
- Événements d'entrée
- Peinture
- Bibliothèque d'extensions Kotlin android-ktx
withStyledAttributes- Documentation Android KTX
- Blog d'annonce initiale d'Android KTX
- Rendre les vues personnalisées plus accessibles
AccessibilityDelegateCompatAccessibilityNodeInfoCompatAccessibilityNodeInfoCompat.AccessibilityActionCompat
Vidéos :
Cette section répertorie les devoirs possibles pour les élèves qui suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :
- Attribuer des devoirs si nécessaire
- Indiquer aux élèves comment rendre leurs devoirs
- Noter les devoirs
Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.
Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.
Question 1
Quelle méthode remplacez-vous pour calculer les positions, les dimensions et toute autre valeur lorsque la taille est attribuée à la vue personnalisée pour la première fois ?
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ onDraw()
Question 2
Pour indiquer que vous souhaitez que votre vue soit redessinée avec onDraw(), quelle méthode appelez-vous à partir du thread d'UI, après qu'une valeur d'attribut a changé ?
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ getVisibility()
Question 3
Quelle méthode View devez-vous remplacer pour ajouter de l'interactivité à votre vue personnalisée ?
▢ setOnClickListener()
▢ onSizeChanged()
▢ isClickable()
▢ performClick()
Pour accéder aux autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation "Développement Android avancé en Kotlin".



