Je mesure du texte en utilisant Paint.getTextBounds()
, car je suis intéressé à obtenir à la fois la hauteur et la largeur du texte à rendre. Cependant, le texte réel rendu est toujours un peu plus large que .width()
les Rect
informations renseignées par getTextBounds()
.
À ma grande surprise, j'ai testé .measureText()
et constaté qu'il renvoie une valeur différente (supérieure). Je l'ai essayé et je l'ai trouvé correct.
Pourquoi signalent-ils des largeurs différentes? Comment puis-je obtenir correctement la hauteur et la largeur? Je veux dire, je peux utiliser .measureText()
, mais je ne saurais pas si je devrais faire confiance au .height()
retour getTextBounds()
.
Comme demandé, voici un code minimal pour reproduire le problème:
final String someText = "Hello. I believe I'm some text!";
Paint p = new Paint();
Rect bounds = new Rect();
for (float f = 10; f < 40; f += 1f) {
p.setTextSize(f);
p.getTextBounds(someText, 0, someText.length(), bounds);
Log.d("Test", String.format(
"Size %f, measureText %f, getTextBounds %d",
f,
p.measureText(someText),
bounds.width())
);
}
La sortie montre que la différence est non seulement supérieure à 1 (et qu'il ne s'agit pas d'une erreur d'arrondi de dernière minute), mais semble également augmenter avec la taille (j'étais sur le point de tirer plus de conclusions, mais cela peut être entièrement dépendant de la police):
D/Test ( 607): Size 10.000000, measureText 135.000000, getTextBounds 134
D/Test ( 607): Size 11.000000, measureText 149.000000, getTextBounds 148
D/Test ( 607): Size 12.000000, measureText 156.000000, getTextBounds 155
D/Test ( 607): Size 13.000000, measureText 171.000000, getTextBounds 169
D/Test ( 607): Size 14.000000, measureText 195.000000, getTextBounds 193
D/Test ( 607): Size 15.000000, measureText 201.000000, getTextBounds 199
D/Test ( 607): Size 16.000000, measureText 211.000000, getTextBounds 210
D/Test ( 607): Size 17.000000, measureText 225.000000, getTextBounds 223
D/Test ( 607): Size 18.000000, measureText 245.000000, getTextBounds 243
D/Test ( 607): Size 19.000000, measureText 251.000000, getTextBounds 249
D/Test ( 607): Size 20.000000, measureText 269.000000, getTextBounds 267
D/Test ( 607): Size 21.000000, measureText 275.000000, getTextBounds 272
D/Test ( 607): Size 22.000000, measureText 297.000000, getTextBounds 294
D/Test ( 607): Size 23.000000, measureText 305.000000, getTextBounds 302
D/Test ( 607): Size 24.000000, measureText 319.000000, getTextBounds 316
D/Test ( 607): Size 25.000000, measureText 330.000000, getTextBounds 326
D/Test ( 607): Size 26.000000, measureText 349.000000, getTextBounds 346
D/Test ( 607): Size 27.000000, measureText 357.000000, getTextBounds 354
D/Test ( 607): Size 28.000000, measureText 369.000000, getTextBounds 365
D/Test ( 607): Size 29.000000, measureText 396.000000, getTextBounds 392
D/Test ( 607): Size 30.000000, measureText 401.000000, getTextBounds 397
D/Test ( 607): Size 31.000000, measureText 418.000000, getTextBounds 414
D/Test ( 607): Size 32.000000, measureText 423.000000, getTextBounds 418
D/Test ( 607): Size 33.000000, measureText 446.000000, getTextBounds 441
D/Test ( 607): Size 34.000000, measureText 455.000000, getTextBounds 450
D/Test ( 607): Size 35.000000, measureText 468.000000, getTextBounds 463
D/Test ( 607): Size 36.000000, measureText 474.000000, getTextBounds 469
D/Test ( 607): Size 37.000000, measureText 500.000000, getTextBounds 495
D/Test ( 607): Size 38.000000, measureText 506.000000, getTextBounds 501
D/Test ( 607): Size 39.000000, measureText 521.000000, getTextBounds 515
la source
Réponses:
Vous pouvez faire ce que j'ai fait pour inspecter ce problème:
Étudiez le code source Android, Paint.java source, consultez les méthodes measureText et getTextBounds. Vous apprendrez que measureText appelle native_measureText et getTextBounds appelle nativeGetStringBounds, qui sont des méthodes natives implémentées en C ++.
Vous continuerez donc à étudier Paint.cpp, qui implémente les deux.
native_measureText -> SkPaintGlue :: measureText_CII
nativeGetStringBounds -> SkPaintGlue :: getStringBounds
Maintenant, votre étude vérifie où ces méthodes diffèrent. Après quelques vérifications de paramètres, les deux appellent la fonction SkPaint :: measureText dans Skia Lib (partie d'Android), mais ils appellent tous deux une forme surchargée différente.
En creusant plus loin dans Skia, je vois que les deux appels aboutissent au même calcul dans la même fonction, ne retournent que le résultat différemment.
Pour répondre à votre question: vos deux appels font le même calcul. La différence possible de résultat réside en fait que getTextBounds renvoie les limites sous forme d'entier, tandis que measureText renvoie une valeur flottante.
Donc, ce que vous obtenez est une erreur d'arrondi lors de la conversion de float en int, et cela se produit dans Paint.cpp dans SkPaintGlue :: doTextBounds lors de l'appel à la fonction SkRect :: roundOut.
La différence entre la largeur calculée de ces deux appels peut être au maximum de 1.
EDIT 4 octobre 2011
Quoi de mieux que la visualisation. J'ai pris l'effort, pour ma propre exploration et pour mériter une prime :)
Il s'agit de la taille de police 60, en rouge le rectangle des limites , en violet le résultat de measureText.
On voit que les limites de la partie gauche commencent quelques pixels à partir de la gauche, et la valeur de measureText est incrémentée de cette valeur à gauche et à droite. C'est quelque chose appelé la valeur AdvanceX de Glyph. (J'ai découvert cela dans les sources Skia dans SkPaint.cpp)
Ainsi, le résultat du test est que measureText ajoute une valeur avancée au texte des deux côtés, tandis que getTextBounds calcule les limites minimales où le texte donné s'adaptera.
J'espère que ce résultat vous sera utile.
Code de test:
la source
Mon expérience avec ceci est que
getTextBounds
retournera ce rectangle de délimitation minimal absolu qui encapsule le texte, pas nécessairement la largeur mesurée utilisée lors du rendu. Je veux également dire que celameasureText
suppose une ligne.Afin d'obtenir des résultats de mesure précis, vous devez utiliser le
StaticLayout
pour rendre le texte et extraire les mesures.Par exemple:
la source
width
pas le casmaxWidth
), mais getLineWith () le fera. J'ai également trouvé la méthode statique getDesiredWidth (), bien qu'il n'y ait pas d'équivalent en hauteur. Veuillez corriger la réponse! Aussi, j'aimerais vraiment savoir pourquoi tant de méthodes différentes donnent des résultats différents.measureText
devrait fonctionner correctement. Sinon, si vous connaissez les contraintes de largeur (comme dansonMeasure
ouonLayout
, vous pouvez utiliser la solution que j'ai publiée ci-dessus. Essayez-vous de redimensionner automatiquement le texte par hasard? De plus, la raison pour laquelle les limites du texte sont toujours légèrement plus petites est qu'elle exclut tout remplissage de caractères , juste le plus petit rect borné nécessaire pour dessiner.La réponse des souris est excellente ... Et voici la description du vrai problème:
La réponse courte et simple est que les
Paint.getTextBounds(String text, int start, int end, Rect bounds)
retoursRect
ne commencent pas à(0,0)
. Autrement dit, pour obtenir la largeur réelle du texte qui sera définie en appelantCanvas.drawText(String text, float x, float y, Paint paint)
avec le même objet Paint depuis getTextBounds (), vous devez ajouter la position gauche de Rect. Quelque chose comme ca:Remarquez ceci
bounds.left
- c'est la clé du problème.De cette façon, vous recevrez la même largeur de texte que celle que vous recevriez en utilisant
Canvas.drawText()
.Et la même fonction devrait être pour obtenir
height
du texte:Ps: Je n'ai pas testé ce code exact, mais testé la conception.
Une explication beaucoup plus détaillée est donnée dans cette réponse.
la source
Désolé d'avoir répondu à nouveau à cette question ... J'avais besoin d'intégrer l'image.
Je pense que les résultats trouvés par @mice sont trompeurs. Les observations peuvent être correctes pour la taille de police de 60, mais elles deviennent beaucoup plus différentes lorsque le texte est plus petit. Par exemple. 10px. Dans ce cas, le texte est en fait dessiné AU-DELÀ des limites.
Code source de la capture d'écran:
la source
AVERTISSEMENT: Cette solution n'est pas précise à 100% en termes de détermination de la largeur minimale.
Je cherchais également à mesurer du texte sur une toile. Après avoir lu le super article de souris, j'ai eu quelques problèmes sur la façon de mesurer du texte multiligne. Il n'y a pas de moyen évident de ces contributions, mais après quelques recherches, je suis tombé sur la classe StaticLayout. Il vous permet de mesurer du texte multiligne (texte avec "\ n") et de configurer beaucoup plus de propriétés de votre texte via le Paint associé.
Voici un extrait de code montrant comment mesurer du texte multiligne:
Le wrapwitdh est capable de déterminer si vous souhaitez limiter votre texte multiligne à une certaine largeur.
Puisque StaticLayout.getWidth () ne renvoie que ce boundedWidth, vous devez faire un autre pas pour obtenir la largeur maximale requise par votre texte multiligne. Vous pouvez déterminer la largeur de chaque ligne et la largeur maximale est bien sûr la largeur de ligne la plus élevée:
la source
i
dans le getLineWidth (vous ne faites que passer 0 à chaque fois)Il existe une autre façon de mesurer précisément les limites du texte, vous devez d'abord obtenir le chemin d'accès à la peinture et au texte actuels. Dans votre cas, ça devrait être comme ça:
Après cela, vous pouvez appeler:
Dans mon code, il renvoie toujours les valeurs correctes et attendues. Mais, je ne sais pas si cela fonctionne plus rapidement que votre approche.
la source
La différence entre
getTextBounds
etmeasureText
est décrite avec l'image ci-dessous.En bref,
getTextBounds
est d'obtenir le RECT du texte exact. LemeasureText
est la longueur du texte, y compris l'espace supplémentaire à gauche et à droite.S'il y a des espaces entre le texte, il est mesuré
measureText
dans la longueur des TextBounds, mais sans l'inclure, bien que les coordonnées soient décalées.Le texte peut être incliné (Inclinaison) vers la gauche. Dans ce cas, le côté gauche de la zone de délimitation dépasserait en dehors de la mesure du measureText, et la longueur totale du texte lié serait supérieure à
measureText
Le texte peut être incliné (Inclinaison) vers la droite. Dans ce cas, le côté droit de la zone de délimitation dépasserait en dehors de la mesure du measureText, et la longueur totale du texte lié serait supérieure à
measureText
la source
Voici comment j'ai calculé les dimensions réelles de la première lettre (vous pouvez modifier l'en-tête de la méthode en fonction de vos besoins, c'est-à-dire au lieu de l'
char[]
utiliserString
):Notez que j'utilise TextPaint au lieu de la classe Paint d'origine.
la source