onMeasure() : définition, modes MeasureSpec et redéfinition de la méthode

Auteur : IT Sectr Publié le : 2026-07-22 Temps de lecture : 9 min

onMeasure() est une méthode protégée de la classe android.view.View que le système Android appelle pour déterminer la taille d'une View. Le système transmet deux objets MeasureSpec à la méthode, chacun contenant un mode de mesure (EXACTLY, AT_MOST ou UNSPECIFIED) et une taille suggérée par le conteneur parent. Selon la Documentation Développeurs Android (2026), redéfinir onMeasure avec un traitement correct de MeasureSpec est une étape obligatoire pour toutes les vues et ViewGroups personnalisées nécessitant un contrôle précis de la taille.

Points clés

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — la méthode View pour mesurer les tailles avec MeasureSpec transmis par le parent
  • MeasureSpec — une valeur 32 bits qui encode le mode de mesure (UNSPECIFIED, EXACTLY, AT_MOST) et la taille
  • setMeasuredDimension(int w, int h) — un appel obligatoire dans onMeasure qui fixe les dimensions finales de la View
  • Algorithme à deux passages de mesure : le parent mesure les enfants, puis les enfants signalent leurs tailles, et le parent prend la décision finale
  • measureChildWithMargins — une méthode auxiliaire pour mesurer les vues enfants dans les ViewGroups personnalisées

Qu'est-ce que onMeasure() ?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) est une méthode de la classe View que le système Android appelle pour déterminer la largeur et la hauteur d'une vue. Le développeur redéfinit cette méthode pour spécifier la taille que la View doit avoir en fonction des contraintes transmises dans MeasureSpec. Sans une redéfinition correcte de onMeasure, une vue personnalisée peut s'afficher incorrectement ou ne pas apparaître du tout.

Le système appelle onMeasure pendant la phase measure du cycle de vie de la View, qui précède les phases de layout (onLayout) et de draw (onDraw). Si une View ne redéfinit pas onMeasure, l'implémentation de la superclasse est utilisée, qui définit des tailles par défaut basées sur le background drawable ou les layout_params. L'appel à super.onMeasure(widthMeasureSpec, heightMeasureSpec) ne fonctionne que pour les sous-classes standard de View, comme TextView ou ImageView.

Une exigence clé de onMeasure est que l'appel à setMeasuredDimension(int, int) doit être présent à la fin de la méthode. Si cet appel manque, le système lève une IllegalStateException indiquant que la View n'a pas défini les dimensions mesurées. Les dimensions finales deviennent disponibles via les accesseurs getMeasuredWidth() et getMeasuredHeight() après la fin de la phase measure.

Modes MeasureSpec : trois valeurs clés

MeasureSpec est un entier 32 bits dont les 2 bits supérieurs encodent le mode de mesure et les 30 bits inférieurs encodent la taille. Le mode détermine à quel point la View est libre de choisir sa propre taille. Android fournit trois modes : EXACTLY, AT_MOST et UNSPECIFIED. Chaque mode dicte une logique de traitement différente dans onMeasure.

Mode MeasureSpecValeurComportement
EXACTLYLe parent a spécifié une taille exacteLa View doit s'ajuster exactement à la taille donnée si elle ne veut pas dépasser les limites
AT_MOSTLe parent a défini une taille maximaleLa View peut choisir n'importe quelle taille de 0 au maximum donné
UNSPECIFIEDLe parent n'impose aucune contrainteLa View peut choisir n'importe quelle taille souhaitée sans limite supérieure

Pour extraire le mode et la taille de MeasureSpec, on utilise les méthodes statiques de la classe MeasureSpec : MeasureSpec.getMode(int) renvoie l'un des trois modes (EXACTLY, AT_MOST, UNSPECIFIED), et MeasureSpec.getSize(int) renvoie la taille numérique en pixels. Pour créer un MeasureSpec personnalisé, on utilise MeasureSpec.makeMeasureSpec(int size, int mode). Ces trois méthodes couvrent tous les scénarios de travail avec les tailles dans onMeasure.

Logique typique de traitement de MeasureSpec

Le modèle standard pour traiter MeasureSpec : si le mode est EXACTLY, utilisez la taille transmise comme finale ; si AT_MOST, choisissez le minimum entre la taille souhaitée (contenu de la View) et le maximum transmis ; si UNSPECIFIED, utilisez la taille souhaitée de la View sans contrainte. Ce modèle garantit un comportement correct sous toutes les contraintes du parent.

kotlin
override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
    val desiredWidth = 200
    val desiredHeight = 100

    val widthMode = MeasureSpec.getMode(widthMeasureSpec)
    val widthSize = MeasureSpec.getSize(widthMeasureSpec)
    val heightMode = MeasureSpec.getMode(heightMeasureSpec)
    val heightSize = MeasureSpec.getSize(heightMeasureSpec)

    val width = when (widthMode) {
        MeasureSpec.EXACTLY -> widthSize
        MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
        else -> desiredWidth
    }
    val height = when (heightMode) {
        MeasureSpec.EXACTLY -> heightSize
        MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
        else -> desiredHeight
    }
    setMeasuredDimension(width, height)
}

Utilisation de resolveSize

Pour simplifier la logique standard, Android fournit la méthode resolveSizeAndState, qui prend la taille souhaitée, MeasureSpec et renvoie la taille finale avec le mode correct. Cette méthode implémente le modèle décrit ci-dessus en une seule ligne de code. La fonction resolveSize(int size, int measureSpec) est également disponible, renvoyant une taille propre sans bits d'état.

Algorithme de mesure à deux passages

Android utilise un algorithme de mesure à deux passages qui garantit que chaque View dans la hiérarchie reçoit des dimensions correctes en tenant compte des contraintes du parent et des préférences des enfants. Au premier passage, le parent transmet MeasureSpec avec des contraintes aux vues enfants, et les vues enfants calculent leurs tailles souhaitées. Au deuxième passage, le parent prend la décision finale sur les tailles.

Pour ViewGroup, le processus de mesure est plus complexe : le parent doit d'abord mesurer tous ses enfants, puis déterminer sa propre taille en fonction de leurs tailles. L'appel à measureChildren(int widthMeasureSpec, int heightMeasureSpec) parcourt toutes les vues enfants et appelle measure(child, childWidthSpec, childHeightSpec) pour chacune. Après avoir mesuré tous les enfants, la ViewGroup appelle setMeasuredDimension avec ses propres dimensions.

Une nuance importante : la méthode measure (publique, finale) ne peut pas être redéfinie — c'est onMeasure qui est redéfini. Cela garantit que le système peut effectuer des tâches de maintenance avant et après onMeasure, telles que la vérification des changements de taille et le calcul de la zone sale pour le dessin ultérieur. Si une View a des dimensions fixes, la redéfinition de onMeasure peut ne pas être nécessaire.

Drapeau MEASURED_SIZE_STATE

MeasureSpec inclut non seulement la taille et le mode, mais aussi des bits d'état, accessibles via MeasureSpec.getMode(). Après avoir appelé setMeasuredDimension, l'état fait partie des dimensions mesurées de la View et peut être vérifié via getMeasuredState(). Ceci est utilisé dans ScrollView et d'autres conteneurs défilables pour transmettre correctement les contraintes aux enfants.

Exemple de redéfinition de onMeasure en Kotlin

Examinons un exemple pratique de création d'une vue personnalisée avec onMeasure redéfini pour un affichage carré. La classe SquareView étend View et garantit que la largeur et la hauteur sont toujours égales, quel que soit le MeasureSpec transmis. Dans onMeasure, le côté minimal est déterminé et la taille carrée est définie.

kotlin
class SquareView(context: Context)
    : View(context) {

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        val widthSize =
            MeasureSpec.getSize(widthMeasureSpec)
        val heightSize =
            MeasureSpec.getSize(heightMeasureSpec)
        val size = minOf(widthSize, heightSize)
        setMeasuredDimension(size, size)
    }
}

ViewGroup personnalisée avec mesure des enfants

ViewGroup nécessite une logique onMeasure plus complexe car les enfants doivent d'abord être mesurés, puis la taille de la ViewGroup elle-même est déterminée. L'exemple CascadeLayout distribue les enfants en cascade avec un décalage. Après avoir mesuré tous les enfants via measureChildWithMargins, la largeur et la hauteur totales sont calculées.

kotlin
class CascadeLayout(context: Context)
    : ViewGroup(context) {

    private val cascadeOffset = 40

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        var maxWidth = 0
        var totalHeight = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChildWithMargins(child,
                widthMeasureSpec,
                cascadeOffset * i,
                heightMeasureSpec, 0)
            maxWidth = maxOf(maxWidth,
                child.measuredWidth +
                cascadeOffset * i)
            totalHeight += child.measuredHeight
        }
        setMeasuredDimension(
            resolveSize(maxWidth, widthMeasureSpec),
            resolveSize(totalHeight, heightMeasureSpec))
    }

    override fun generateLayoutParams(attrs: AttributeSet?)
        : LayoutParams = MarginLayoutParams(context, attrs)

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var top = t
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val left = l + cascadeOffset * i
            child.layout(left, top,
                left + child.measuredWidth,
                top + child.measuredHeight)
            top += child.measuredHeight
        }
    }
}

Erreurs fréquentes lors de la redéfinition de onMeasure

Appel manquant à setMeasuredDimension est l'erreur la plus courante. Si un développeur redéfinit onMeasure mais n'appelle pas setMeasuredDimension, l'application plante avec IllegalStateException. Cela arrive surtout lorsque la méthode a des branches conditionnelles et que l'une d'elles manque l'appel. Chaque branche de code dans onMeasure doit se terminer par un appel à setMeasuredDimension.

Ignorer le mode AT_MOST est la deuxième erreur la plus fréquente. Si une View en mode AT_MOST utilise toujours la taille transmise au lieu de calculer en fonction du contenu, le conteneur parent ne peut pas distribuer correctement l'espace. Par exemple, un TextView en AT_MOST doit calculer la largeur du texte et utiliser le minimum entre la largeur souhaitée et la largeur transmise. Ignorer AT_MOST fait que la View occupe tout l'espace disponible même avec un petit contenu.

Créer des objets dans onMeasure est une erreur de performance classique. Comme onMeasure peut être appelé plusieurs fois (à chaque demande de layout), créer des objets (Paint, Rect, String) dans cette méthode encombre la mémoire et déclenche le ramasse-miettes. Tous les objets doivent être créés une fois dans le constructeur de la View, et seule la logique de calcul des tailles doit s'exécuter dans onMeasure. La même règle s'applique à onDraw et onLayout.

Mesure des vues enfants dans ViewGroup

measureChildWithMargins est une méthode protégée de ViewGroup qui mesure une seule vue enfant en tenant compte de ses MarginLayoutParams. La méthode accepte le MeasureSpec du parent et les décalages accumulés de largeur et de hauteur. Elle ajuste automatiquement le MeasureSpec pour la vue enfant en soustrayant les paddings du parent et les margins de l'enfant, puis transmet le MeasureSpec ajusté à child.measure().

Pour une logique de mesure avancée, une ViewGroup peut redéfinir measureChild(View child, int parentWidthSpec, int parentHeightSpec) ou travailler directement avec MeasureSpec pour chaque enfant. Par exemple, LinearLayout dans onMeasure parcourt toutes les vues enfants, mesure chacune en tenant compte de son layout_weight et distribue l'espace restant proportionnellement. Cette approche permet d'implémenter des algorithmes de mise en page arbitraires.

La mise en cache des résultats de mesure via le mécanisme de cache de mesure est disponible via le drapeau setMeasureWithLargestChildEnabled dans certaines ViewGroups. Cependant, dans la plupart des cas, onMeasure est rappelé à tout changement de layout et le cache ne s'applique pas. Dans les ViewGroups personnalisées, il est recommandé de minimiser les calculs dans onMeasure plutôt que de se fier au cache.

Questions fréquentes

Est-il nécessaire de redéfinir onMeasure pour une vue personnalisée ?

Oui, si la vue personnalisée hérite directement de la classe View. Si elle hérite de TextView, ImageView ou Button avec leurs tailles standard, onMeasure peut rester inchangé. Pour ViewGroup, la redéfinition de onMeasure est toujours nécessaire — sinon les enfants ne seront pas mesurés correctement.

Que se passe-t-il si setMeasuredDimension n'est pas appelé ?

Le système Android lève IllegalStateException avec le message « The View did not call setMeasuredDimension ». Cette exception se produit dans la méthode measure() après la fin de onMeasure, si les dimensions finales sont restées nulles. L'exception plante l'application si elle n'est pas traitée via try-catch.

Quelle est la différence entre getWidth et getMeasuredWidth ?

getMeasuredWidth() renvoie la taille définie dans onMeasure (phase de mesure). getWidth() renvoie la taille réelle que la View a reçue dans onLayout après tous les ajustements de positionnement. Pour la plupart des vues, ces valeurs coïncident, mais dans les ViewGroups personnalisées, elles peuvent différer.

Peut-on modifier des animations ou l'état de la View dans onMeasure ?

Non, onMeasure est destiné exclusivement au calcul des tailles. Modifier l'état, lancer des animations, faire du réseau ou mettre à jour des données dans cette méthode viole l'architecture Android et peut entraîner des appels récursifs à measure, car les changements d'état peuvent déclencher requestLayout.

Comment onMeasure interagit-il avec ConstraintLayout ?

ConstraintLayout gère la mesure des enfants de manière indépendante en fonction des contraintes définies. Si une vue personnalisée dans ConstraintLayout redéfinit onMeasure, elle doit traiter correctement le MeasureSpec transmis par ConstraintLayout, sinon les contraintes peuvent ne pas fonctionner. ConstraintLayout utilise un algorithme à deux passages avec son propre WidgetContainer pour le calcul.

Résumé

  • onMeasure() — la méthode View pour déterminer les dimensions, appelée par le système Android pendant la phase measure
  • MeasureSpec encode le mode de mesure (EXACTLY, AT_MOST, UNSPECIFIED) et la taille transmise par le parent
  • setMeasuredDimension — un appel obligatoire à la fin de onMeasure qui définit les dimensions finales
  • resolveSize — une méthode auxiliaire qui implémente la logique standard de traitement de MeasureSpec en une ligne
  • Algorithme à deux passages garantit une mesure correcte dans la hiérarchie parent-enfant
  • measureChildWithMargins est utilisé pour mesurer les vues enfants dans les ViewGroups personnalisées avec marges
  • Créer des objets dans onMeasure est fortement déconseillé en raison du risque de GC et de chutes d'images

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi