Android Paint: .measureText () frente a .getTextBounds ()



Answers

Mi experiencia con esto es que getTextBounds devolverá ese rect límite mínimo absoluto que encapsula el texto, no necesariamente el ancho medido usado al renderizar. También quiero decir que measureText asume una línea.

Para obtener resultados de medición precisos, debe usar StaticLayout para representar el texto y extraer las medidas.

Por ejemplo:

String text = "text";
TextPaint textPaint = textView.getPaint();
int boundedWidth = 1000;

StaticLayout layout = new StaticLayout(text, textPaint, boundedWidth , Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
int height = layout.getHeight();
Question

Estoy midiendo texto usando Paint.getTextBounds() , ya que estoy interesado en obtener el alto y el ancho del texto para representar. Sin embargo, el texto real procesado siempre es un poco más ancho que el .width() de la información de Rect llena por getTextBounds() .

Para mi sorpresa, probé .measureText() , y encontré que devuelve un valor diferente (más alto). Lo intenté y lo encontré correcto.

¿Por qué informan diferentes anchuras? ¿Cómo puedo obtener correctamente el alto y el ancho? Quiero decir, puedo usar .measureText() , pero entonces no sabría si debería confiar en .height() devuelto por getTextBounds() .

Según lo solicitado, aquí hay un código mínimo para reproducir el problema:

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())
    );
}

El resultado muestra que la diferencia no solo es mayor que 1 (y no es un error de redondeo de último minuto), sino que también parece aumentar con el tamaño (estaba a punto de sacar más conclusiones, pero puede depender enteramente de la fuente):

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



Hay otra manera de medir los límites de texto con precisión, primero debe obtener la ruta para la pintura y el texto actuales. En tu caso, debería ser así:

p.getTextPath(someText, 0, someText.length(), 0.0f, 0.0f, mPath);

Después de eso puedes llamar:

mPath.computeBounds(mBoundsPath, true);

En mi código, siempre devuelve los valores correctos y esperados. Pero no estoy seguro de si funciona más rápido que tu enfoque.




Perdón por responder nuevamente sobre esa pregunta ... Necesitaba incrustar la imagen.

Creo que los resultados @mice encontrados son engañosos. Las observaciones pueden ser correctas para el tamaño de fuente de 60, pero se vuelven mucho más diferentes cuando el texto es más pequeño. P.ej. 10px. En ese caso, el texto en realidad se dibuja MÁS ALLÁ de los límites.

Código fuente de la captura de pantalla:

  @Override
  protected void onDraw( Canvas canvas ) {
    for( int i = 0; i < 20; i++ ) {
      int startSize = 10;
      int curSize = i + startSize;
      paint.setTextSize( curSize );
      String text = i + startSize + " - " + TEXT_SNIPPET;
      Rect bounds = new Rect();
      paint.getTextBounds( text, 0, text.length(), bounds );
      float top = STEP_DISTANCE * i + curSize;
      bounds.top += top;
      bounds.bottom += top;
      canvas.drawRect( bounds, bgPaint );
      canvas.drawText( text, 0, STEP_DISTANCE * i + curSize, paint );
    }
  }



Links