195 votes

Peinture Android : .measureText() vs .getTextBounds()

Je suis en mesure de texte à l'aide d' Paint.getTextBounds(), depuis que je suis intéressé à obtenir à la fois la hauteur et la largeur du texte à être rendus. Cependant, le texte rendu est toujours un peu plus large que l' .width() de la Rect informations remplies par getTextBounds().

À ma grande surprise, j'ai testé .measureText(), et constaté qu'il renvoie à un autre (supérieur) de la valeur. Je lui ai donné un essai, et trouvé correct.

Pourquoi n'ont-ils rapport des largeurs différentes? Comment puis-je correctement obtenir la hauteur et la largeur? Je veux dire, je peux utiliser .measureText(), mais ensuite je ne sais pas si je dois faire confiance à la .height() retournée par getTextBounds().

Comme demandé, voici le 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 non seulement devient plus grand que 1 (et pas la dernière minute de l'erreur d'arrondi), mais semble également augmenter avec la taille (j'ai été sur le point de tirer plus de conclusions, mais il peut être tout à fait en fonction 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

387voto

Pointer Null Points 14229

Vous pouvez faire ce que j'ai fait pour examiner ce type de problème:

Étude du code source Android, Paint.java source, voir les deux à savoir measuretext et getTextBounds méthodes. Vous apprendriez que savoir measuretext appels native_measureText, et getTextBounds appels nativeGetStringBounds, qui sont indigènes méthodes implémentées en C++.

Donc, si vous voulez continuer à étudier Paint.cpp qui implémente à la fois.

native_measureText -> SkPaintGlue::measureText_CII

nativeGetStringBounds -> SkPaintGlue::getStringBounds

Maintenant, votre étude vérifie où ces méthodes diffèrent. Après quelques param contrôles, à la fois la fonction d'appel SkPaint::à savoir measuretext dans Skia Lib (partie d'Android), mais ils ont tous deux appel à des surchargé forme.

Creuser plus loin dans Skia, je vois que les deux appels en même calcul en fonction même, mais seulement de résultat de retour différemment.

Pour répondre à votre question: Les deux à vos appels faire le même calcul. Possible différence de résultat réside dans le fait que getTextBounds retourne limites entier, tout savoir measuretext renvoie la valeur flottante.

Donc, ce que vous obtenez est une erreur d'arrondi lors de la conversion de float en int, et ce qui se passe dans Paint.cpp dans SkPaintGlue::doTextBounds dans 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 Oct 2011

Ce qui est peut-être mieux que la visualisation. J'ai pris l'effort, afin de les explorer, et pour méritants bounty :) enter image description here

C'est la taille de la police 60, en rouge les limites d'un rectangle, en violet est le résultat de savoir measuretext.

On voit bien que les limites de gauche lance un certain nombre de pixels à partir de la gauche, et la valeur du savoir measuretext est incrémenté de cette valeur à la fois à gauche et à droite. C'est quelque chose qui s'appelle Glyphe AdvanceX valeur. (J'ai découvert cela dans Skia sources SkPaint.cpp)

Si le résultat du test est que savoir measuretext ajoute un peu d'avance valeur pour le texte sur les deux côtés, tandis que getTextBounds calcule minimale des limites où le texte donné.

Espérons que ce résultat est utile pour vous.

Test de code:

  protected void onDraw(Canvas canvas){
     final String s = "Hello. I'm some text!";

     Paint p = new Paint();
     Rect bounds = new Rect();
     p.setTextSize(60);

     p.getTextBounds(s, 0, s.length(), bounds);
     float mt = p.measureText(s);
     int bw = bounds.width();

     Log.i("LCG", String.format(
          "measureText %f, getTextBounds %d (%s)",
          mt,
          bw, bounds.toShortString())
      );
     bounds.offset(0, -bounds.top);
     p.setStyle(Style.STROKE);
     canvas.drawColor(0xff000080);
     p.setColor(0xffff0000);
     canvas.drawRect(bounds, p);
     p.setColor(0xff00ff00);
     canvas.drawText(s, 0, bounds.bottom, p);
  }

22voto

Chase Points 7520

Mon expérience avec cela est que sera de retour que rect englobant minimal absolu qui encapsule le texte, pas nécessairement la largeur mesurée utilisée lors du rendu. Je tiens aussi à dire que suppose une seule ligne.

Afin d’obtenir des résultats de mesure précis, vous devez utiliser le `` pour restituer le texte et tirez sur les mesures.

Par exemple :

19voto

Prizoff Points 1162

La souris réponse est génial... Et en voici la description du problème réel:

La courte réponse est simple, Paint.getTextBounds(String text, int start, int end, Rect bounds) retours Rect qui ne commence à (0,0). Qui est, pour obtenir la largeur réelle de texte qui sera mis en appelant Canvas.drawText(String text, float x, float y, Paint paint) avec la même Peinture objet de getTextBounds (), vous devez ajouter la position la plus à gauche de Rect. Quelque chose comme ça:

public int getTextWidth(String text, Paint paint) {
    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, end, bounds);
    int width = bounds.left + bounds.width();
    return width;
}

Avis cette bounds.left - ce la clé du problème.

De cette façon, vous recevrez la même largeur de texte, que vous recevrez l'aide d' Canvas.drawText().

Et de la même fonction, doit être pour l'obtention height du texte:

public int getTextHeight(String text, Paint paint) {
    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, end, bounds);
    int height = bounds.bottom + bounds.height();
    return height;
}

P. s.: Je n'ai pas testé ce code exact, mais testé la conception.


Beaucoup plus explication détaillée est donnée dans cette réponse.

11voto

Moritz Points 3760

Désolé d’avoir répondu à nouveau sur cette question... J’avais besoin d’intégrer l’image.

Je pense que les résultats @mice trouvés sont missleading. Les observations pourraient être correctes pour la taille de la police de 60, mais ils tournent beaucoup plus différentes lorsque le texte est plus petit. Par exemple. 10px. dans ce cas le texte est en fait tiré au-delà des limites.

enter image description here

Code source de la capture d’écran :

10voto

Moritz Points 3760

AVERTISSEMENT: Cette solution n'est pas précis à 100% en termes de détermination de la largeur minimale.

J'ai également essayer de comprendre comment mesurer texte sur une toile. Après la lecture de la grande poste de la souris, j'ai eu quelques problèmes sur la façon de mesurer texte multiligne. Il n'y a pas de façon évidente à partir de ces contributions, mais après quelques recherches, j'ai cam à travers le StaticLayout classe. Il vous permet de mesurer texte multiligne (texte présentant de "\n") et de configurer beaucoup plus de propriétés de votre texte par les associés de la Peinture.

Voici un extrait de code montrant comment mesurer texte multiligne:

private StaticLayout measure( TextPaint textPaint, String text, Integer wrapWidth ) {
int boundedWidth = Integer.MAX_VALUE;
if( wrapWidth != null && wrapWidth > 0 ) {
  boundedWidth = wrapWidth;
}
StaticLayout layout = new StaticLayout( text, textPaint, boundedWidth, Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false );
return layout;

}

Le wrapwitdh est en mesure de déterminer si vous souhaitez limiter votre texte multiligne à une certaine largeur.

Depuis le StaticLayout.getWidth() ne renvoie que ce boundedWidth vous avez à prendre une autre étape pour obtenir la largeur maximale requise par votre texte multiligne. Vous êtes en mesure de déterminer chacune des lignes, la largeur et la largeur maximale est le plus élevé de la largeur de ligne de cours:

private float getMaxLineWidth( StaticLayout layout ) {
float maxLine = 0.0f;
int lineCount = layout.getLineCount();
for( int i = 0; i < lineCount; i++ ) {
  if( layout.getLineWidth( 0 ) > maxLine ) {
    maxLine = layout.getLineWidth( 0 );
  }
}
return maxLine;

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X