Estou medindo o texto usando Paint.getTextBounds()
, pois estou interessado em obter a altura e a largura do texto a serem renderizadas. No entanto, o texto real renderizado é sempre um pouco maior do que o .width()
das Rect
informações preenchidas getTextBounds()
.
Para minha surpresa, testei .measureText()
e descobri que ele retorna um valor (mais alto) diferente. Eu tentei e achei correto.
Por que eles relatam larguras diferentes? Como posso obter corretamente a altura e a largura? Quero dizer, eu posso usar .measureText()
, mas não saberia se devo confiar nos .height()
retornados por getTextBounds()
.
Conforme solicitado, aqui está o código mínimo para reproduzir o 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())
);
}
A saída mostra que a diferença não apenas é maior que 1 (e não é um erro de arredondamento de última hora), mas também parece aumentar com o tamanho (eu estava prestes a tirar mais conclusões, mas pode ser totalmente dependente da fonte):
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
fonte
Respostas:
Você pode fazer o que fiz para inspecionar esse problema:
Estude o código-fonte Android, fonte Paint.java, consulte os métodos measureText e getTextBounds. Você aprenderia que o measureText chama native_measureText e getTextBounds chama nativeGetStringBounds, que são métodos nativos implementados em C ++.
Então você continuaria estudando o Paint.cpp, que implementa ambos.
native_measureText -> SkPaintGlue :: measureText_CII
nativeGetStringBounds -> SkPaintGlue :: getStringBounds
Agora, seu estudo verifica onde esses métodos diferem. Após algumas verificações de parâmetros, ambas chamam a função SkPaint :: measureText no Skia Lib (parte do Android), mas ambas chamam a forma sobrecarregada diferente.
Indo mais longe no Skia, vejo que as duas chamadas resultam no mesmo cálculo na mesma função, retornam apenas o resultado de maneira diferente.
Para responder à sua pergunta: As duas chamadas fazem o mesmo cálculo. A possível diferença de resultado reside no fato de que getTextBounds retorna limites como número inteiro, enquanto measureText retorna valor flutuante.
Então, o que você recebe é um erro de arredondamento durante a conversão de float para int, e isso acontece no Paint.cpp em SkPaintGlue :: doTextBounds na chamada para a função SkRect :: roundOut.
A diferença entre a largura calculada dessas duas chamadas pode ser no máximo 1.
EDIT 4 Out 2011
O que pode ser melhor que a visualização. Eu me esforcei, por explorar e por merecer recompensas :)
Este é o tamanho da fonte 60, em vermelho é o retângulo de limites , em roxo é o resultado de measureText.
Vimos que a parte esquerda dos limites inicia alguns pixels da esquerda e o valor de measureText é incrementado por esse valor à esquerda e à direita. Isso é chamado de valor AdvanceX da Glyph. (Descobri isso nas fontes Skia em SkPaint.cpp)
Portanto, o resultado do teste é que measureText adiciona algum valor avançado ao texto nos dois lados, enquanto getTextBounds calcula limites mínimos onde o texto fornecido se encaixa.
Espero que este resultado seja útil para você.
Código de teste:
fonte
Minha experiência com isso é que
getTextBounds
retornará o retângulo mínimo mínimo absoluto que encapsula o texto, não necessariamente a largura medida usada na renderização. Eu também quero dizer quemeasureText
assume uma linha.Para obter resultados de medição precisos, você deve usar o
StaticLayout
para renderizar o texto e extrair as medidas.Por exemplo:
fonte
width
não émaxWidth
), mas getLineWith () funcionará. Também encontrei o método estático getDesiredWidth (), embora não haja altura equivalente. Corrija a resposta! Além disso, eu realmente gostaria de saber por que tantos métodos diferentes produzem resultados diferentes.measureText
deve funcionar bem. Caso contrário, se você souber as dimensões da largura (como emonMeasure
ouonLayout
, você pode usar a solução que eu postei acima. Você está tentando redimensionar automaticamente a porcentagem de texto? Além disso, a razão pela qual os limites de texto são sempre um pouco menores é porque exclui qualquer preenchimento de caractere , apenas o menor rect limitada necessária para o desenho.A resposta dos ratos é ótima ... E aqui está a descrição do problema real:
A resposta curta e simples é que
Paint.getTextBounds(String text, int start, int end, Rect bounds)
retornosRect
que não começam às(0,0)
. Ou seja, para obter a largura real do texto que será definido chamandoCanvas.drawText(String text, float x, float y, Paint paint)
com o mesmo objeto Paint de getTextBounds (), você deve adicionar a posição esquerda de Rect. Algo parecido:Observe isso
bounds.left
- essa é a chave do problema.Dessa forma, você receberá a mesma largura de texto que receberia usando
Canvas.drawText()
.E a mesma função deve ser para obter
height
o texto:Ps: Eu não testei esse código exato, mas testei a concepção.
Uma explicação muito mais detalhada é dada nesta resposta.
fonte
Desculpe por responder novamente a essa pergunta ... Eu precisava incorporar a imagem.
Eu acho que os resultados encontrados pelos ratos são enganosos. As observações podem estar corretas para o tamanho da fonte 60, mas ficam muito mais diferentes quando o texto é menor. Por exemplo. 10 px. Nesse caso, o texto é realmente desenhado além dos limites.
Código fonte da captura de tela:
fonte
AVISO LEGAL: Esta solução não é 100% precisa em termos de determinação da largura mínima.
Eu também estava tentando descobrir como medir o texto em uma tela. Depois de ler o ótimo post dos ratos, tive alguns problemas sobre como medir texto em várias linhas. Não há uma maneira óbvia dessas contribuições, mas depois de algumas pesquisas, atravesso a classe StaticLayout. Ele permite medir o texto de várias linhas (texto com "\ n") e configurar muito mais propriedades do seu texto através do Paint associado.
Aqui está um trecho que mostra como medir o texto de várias linhas:
O wrapwitdh pode determinar se você deseja limitar o texto de múltiplas linhas a uma determinada largura.
Como o StaticLayout.getWidth () retorna apenas esse boundedWidth, você deve executar outra etapa para obter a largura máxima exigida pelo texto de várias linhas. Você pode determinar a largura de cada linha e a largura máxima é a largura da linha mais alta, é claro:
fonte
i
na getLineWidth (você está só de passagem 0 de cada vez)Há outra maneira de medir os limites do texto com precisão. Primeiro, você deve obter o caminho para o Paint e o texto atuais. No seu caso, deve ser assim:
Depois disso, você pode ligar para:
No meu código, ele sempre retorna valores corretos e esperados. Mas não tenho certeza se funciona mais rápido que sua abordagem.
fonte
A diferença entre
getTextBounds
emeasureText
é descrita na imagem abaixo.Em resumo,
getTextBounds
é obter o RECT do texto exato. OmeasureText
é o comprimento do texto, incluindo o espaço extra à esquerda e à direita.Se houver espaços entre o texto, ele será medido,
measureText
mas não incluindo, o comprimento dos TextBounds, embora a coordenada seja alterada.O texto pode estar inclinado (Inclinar) para a esquerda. Nesse caso, o lado esquerdo da caixa delimitadora excederia fora da medida do measureText, e o comprimento geral do texto delimitado seria maior que
measureText
O texto pode estar inclinado (Inclinar) para a direita. Nesse caso, o lado direito da caixa delimitadora excederia fora da medida do measureText, e o comprimento geral do texto delimitado seria maior que
measureText
fonte
Foi assim que calculei as dimensões reais da primeira letra (você pode alterar o cabeçalho do método para atender às suas necessidades, ou seja, em vez de
char[]
usarString
):Observe que estou usando TextPaint em vez da classe Paint original.
fonte