Estoy midiendo texto usando Paint.getTextBounds()
, ya que estoy interesado en obtener tanto el alto como el ancho del texto que se representará. Sin embargo, el texto real representado siempre es un poco más ancho que el .width()
de la Rect
información completada getTextBounds()
.
Para mi sorpresa, probé .measureText()
y descubrí que devuelve un valor diferente (más alto). Lo intenté y lo encontré correcto.
¿Por qué informan diferentes anchos? ¿Cómo puedo obtener correctamente la altura y el ancho? Quiero decir, puedo usarlo .measureText()
, pero entonces no sabría si debería confiar en lo .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 ser completamente dependiente 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
fuente
Respuestas:
Puedes hacer lo que hice para inspeccionar tal problema:
Estudie el código fuente de Android, la fuente Paint.java, vea los métodos measureText y getTextBounds. Aprenderá que measureText llama native_measureText, y getTextBounds llama nativeGetStringBounds, que son métodos nativos implementados en C ++.
Entonces continuaría estudiando Paint.cpp, que implementa ambos.
native_measureText -> SkPaintGlue :: measureText_CII
nativeGetStringBounds -> SkPaintGlue :: getStringBounds
Ahora su estudio verifica dónde difieren estos métodos. Después de algunas comprobaciones de parámetros, ambos llaman a la función SkPaint :: measureText en Skia Lib (parte de Android), pero ambos llaman a diferentes formas sobrecargadas.
Al profundizar más en Skia, veo que ambas llamadas resultan en el mismo cálculo en la misma función, solo devuelven el resultado de manera diferente.
Para responder a su pregunta: Ambas llamadas hacen el mismo cálculo. La posible diferencia de resultado radica en que getTextBounds devuelve los límites como un entero, mientras que measureText devuelve el valor flotante.
Entonces, lo que obtienes es un error de redondeo durante la conversión de float a int, y esto sucede en Paint.cpp en SkPaintGlue :: doTextBounds en la llamada a la función SkRect :: roundOut.
La diferencia entre el ancho calculado de esas dos llamadas puede ser máxima 1.
EDITAR 4 oct 2011
Qué puede ser mejor que la visualización. Tomé el esfuerzo, para explorar y para merecer la generosidad :)
Este es el tamaño de fuente 60, en rojo es rectángulo de límites , en púrpura es el resultado de measureText.
Se ve que la parte izquierda de los límites comienza algunos píxeles desde la izquierda, y el valor de measureText se incrementa en este valor tanto a la izquierda como a la derecha. Esto es algo llamado valor AdvanceX de Glyph. (He descubierto esto en las fuentes de Skia en SkPaint.cpp)
Por lo tanto, el resultado de la prueba es que measureText agrega un valor de avance al texto en ambos lados, mientras que getTextBounds calcula los límites mínimos donde se ajustará el texto dado.
Espero que este resultado te sea útil.
Código de prueba:
fuente
Mi experiencia con esto es que
getTextBounds
devolverá ese rect de límite mínimo absoluto que encapsula el texto, no necesariamente el ancho medido utilizado al renderizar. También quiero decir quemeasureText
supone una línea.Para obtener resultados de medición precisos, debe utilizar el
StaticLayout
para representar el texto y extraer las mediciones.Por ejemplo:
fuente
width
, nomaxWidth
), pero getLineWith () lo hará. También encontré el método estático getDesiredWidth (), aunque no hay una altura equivalente. Por favor corrija la respuesta! Además, me gustaría saber por qué tantos métodos diferentes producen resultados diferentes.measureText
debería funcionar bien. De lo contrario, si conoce las constantes de ancho (como enonMeasure
oonLayout
, puede usar la solución que publiqué anteriormente. ¿Está tratando de redimensionar automáticamente la posibilidad de texto? Además, la razón por la cual los límites del texto siempre son ligeramente más pequeños es porque excluye cualquier relleno de caracteres , el rectángulo acotado más pequeño necesario para dibujarLa respuesta de los ratones es excelente ... Y aquí está la descripción del problema real:
La respuesta breve y sencilla es que
Paint.getTextBounds(String text, int start, int end, Rect bounds)
vuelveRect
, que no comienza a(0,0)
. Es decir, para obtener el ancho real del texto que se establecerá llamandoCanvas.drawText(String text, float x, float y, Paint paint)
con el mismo objeto Paint de getTextBounds (), debe agregar la posición izquierda de Rect. Algo como eso:Observe esto
bounds.left
: esta es la clave del problema.De esta forma, recibirá el mismo ancho de texto que recibiría con
Canvas.drawText()
.Y la misma función debería ser para obtener
height
el texto:Ps: no probé este código exacto, pero probé la concepción.
Se da una explicación mucho más detallada en esta respuesta.
fuente
Perdón por responder de nuevo a esa pregunta ... necesitaba incrustar la imagen.
Creo que los resultados que @mice encontró 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 se dibuja MÁS ALLÁ de los límites.
Código fuente de la captura de pantalla:
fuente
DESCARGO DE RESPONSABILIDAD: Esta solución no es 100% precisa en términos de determinar el ancho mínimo.
También estaba descubriendo cómo medir texto en un lienzo. Después de leer la gran publicación de ratones, tuve algunos problemas sobre cómo medir texto multilínea. No hay una manera obvia de estas contribuciones, pero después de algunas investigaciones, he podido ver la clase StaticLayout. Le permite medir texto multilínea (texto con "\ n") y configurar muchas más propiedades de su texto a través de Paint asociado.
Aquí hay un fragmento que muestra cómo medir texto multilínea:
El wrapwitdh puede determinar si desea limitar su texto multilínea a un cierto ancho.
Dado que StaticLayout.getWidth () solo devuelve este boundedWidth, debe dar otro paso para obtener el ancho máximo requerido por su texto multilínea. Puede determinar el ancho de cada línea y el ancho máximo es el ancho de línea más alto, por supuesto:
fuente
i
al getLineWidth (solo estás pasando 0 cada vez)Hay otra forma de medir los límites del texto con precisión, primero debe obtener la ruta para la pintura y el texto actuales. En su caso, debería ser así:
Después de eso puedes llamar:
En mi código siempre devuelve los valores correctos y esperados. Pero, no estoy seguro si funciona más rápido que su enfoque.
fuente
La diferencia entre
getTextBounds
ymeasureText
se describe con la imagen a continuación.En breve,
getTextBounds
es obtener el RECT del texto exacto. ElmeasureText
es la longitud del texto, incluido el espacio adicional a la izquierda y a la derecha.Si hay espacios entre el texto, se mide
measureText
pero no se incluye en la longitud de los TextBounds, aunque las coordenadas se desplazan.El texto podría inclinarse (sesgarse) a la izquierda. En este caso, el lado izquierdo del cuadro delimitador excedería fuera de la medición del measureText, y la longitud total del texto encuadernado sería mayor que
measureText
El texto podría inclinarse (sesgarse) a la derecha. En este caso, el lado derecho del cuadro delimitador excedería fuera de la medición de measureText, y la longitud total del texto encuadernado sería mayor que
measureText
fuente
Así es como calculé las dimensiones reales para la primera letra (puede cambiar el encabezado del método para satisfacer sus necesidades, es decir, en lugar de
char[]
usarloString
):Tenga en cuenta que estoy usando TextPaint en lugar de la clase Paint original.
fuente