Korzystam z tekstu Paint.getTextBounds()
, ponieważ jestem zainteresowany uzyskaniem zarówno wysokości, jak i szerokości tekstu do renderowania. Jednak rzeczywisty tekst renderowane jest zawsze nieco szerszy niż .width()
w Rect
informacji wypełnionej przez getTextBounds()
.
Ku mojemu zaskoczeniu przetestowałem .measureText()
i stwierdziłem, że zwraca inną (wyższą) wartość. Spróbowałem i uznałem, że jest poprawny.
Dlaczego zgłaszają różne szerokości? Jak prawidłowo uzyskać wysokość i szerokość? Mam na myśli, że mogę korzystać .measureText()
, ale wtedy nie wiedziałbym, czy powinienem zaufać .height()
oddanemu getTextBounds()
.
Zgodnie z prośbą, oto minimalny kod do odtworzenia problemu:
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())
);
}
Dane wyjściowe pokazują, że różnica nie tylko staje się większa niż 1 (i nie jest to błąd zaokrąglania w ostatniej chwili), ale wydaje się również, że rośnie wraz z rozmiarem (miałem już wyciągnąć więcej wniosków, ale może być całkowicie zależna od czcionki):
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
źródło
Odpowiedzi:
Możesz zrobić to, co zrobiłem, aby sprawdzić taki problem:
Studiuj kod źródłowy Androida, źródło Paint.java, zobacz zarówno metody MeasureText, jak i getTextBounds. Dowiesz się, że MeasureText wywołuje native_measureText, a getTextBounds wywołuje nativeGetStringBounds, które są metodami natywnymi zaimplementowanymi w C ++.
Więc kontynuowałbyś studiowanie Paint.cpp, który implementuje oba.
native_measureText -> SkPaintGlue :: MeasureText_CII
nativeGetStringBounds -> SkPaintGlue :: getStringBounds
Teraz twoje badanie sprawdza, gdzie różnią się te metody. Po kilku sprawdzeniach parametrów obie funkcje wywołują SkPaint :: MeasureText w Skia Lib (część Androida), ale obie wywołują różne przeciążone formularze.
Zagłębiając się w Skia, widzę, że oba wywołania dają to samo obliczenie w tej samej funkcji, tylko zwracają wynik inaczej.
Aby odpowiedzieć na twoje pytanie: oba połączenia wykonują te same obliczenia. Możliwa różnica wyniku polega na tym, że getTextBounds zwraca granice jako liczbę całkowitą, podczas gdy MeasureText zwraca wartość zmiennoprzecinkową.
Otrzymujesz więc błąd zaokrąglania podczas konwersji float na int, i dzieje się tak w Paint.cpp w SkPaintGlue :: doTextBounds w wywołaniu funkcji SkRect :: roundOut.
Różnica między obliczoną szerokością tych dwóch wywołań może wynosić maksymalnie 1.
EDYCJA 4 października 2011 r
Co może być lepszego niż wizualizacja. Podjąłem wysiłek, by odkrywać własne rzeczy i zasługiwać na nagrodę :)
Jest to wielkość czcionki 60, w kolorze czerwonym to granice prostokąta w fioletowy jest wynikiem measureText.
Widoczne jest, że ograniczona lewa część zaczyna niektóre piksele od lewej, a wartość MeasureText jest zwiększana o tę wartość zarówno po lewej, jak i po prawej stronie. Jest to coś, co nazywa się wartością AdvanceX Glypha. (Odkryłem to w źródłach Skia w SkPaint.cpp)
Wynik testu jest taki, że miara dodaje do tekstu po obu stronach pewną zaawansowaną wartość, podczas gdy getTextBounds oblicza minimalne granice, w których zmieści się dany tekst.
Mam nadzieję, że ten wynik Ci się przyda.
Kod testowy:
źródło
Moje doświadczenie z tym polega na tym, że
getTextBounds
zwróci ten absolutny minimalny prostokąt ograniczający, który otacza tekst, niekoniecznie zmierzoną szerokość używaną podczas renderowania. Chcę również powiedzieć, żemeasureText
zakłada jedną linię.Aby uzyskać dokładne wyniki pomiaru, użyj przycisku
StaticLayout
do renderowania tekstu i wyciągnij pomiary.Na przykład:
źródło
width
nie jestmaxWidth
), ale getLineWith () zadziała. Znalazłem również metodę statyczną getDesiredWidth (), chociaż nie ma równoważnej wysokości. Popraw odpowiedź! Chciałbym też naprawdę wiedzieć, dlaczego tak wiele różnych metod daje różne wyniki.measureText
powinna działać dobrze. W przeciwnym razie, jeśli znasz ograniczenia szerokości (takie jak wonMeasure
lubonLayout
, możesz użyć rozwiązania, które zamieściłem powyżej. Czy próbujesz automatycznie zmienić rozmiar tekstu? Ponadto, granica granic tekstu jest zawsze nieco mniejsza, ponieważ wyklucza to uzupełnianie znaków , tylko najmniejszy prostokąt z ogranicznikiem potrzebny do rysowaniaOdpowiedź myszy jest świetna ... A oto opis prawdziwego problemu:
Krótka prosta odpowiedź brzmi:
Paint.getTextBounds(String text, int start, int end, Rect bounds)
zwroty,Rect
które nie zaczynają się od(0,0)
. Oznacza to, że aby uzyskać rzeczywistą szerokość tekstu, który zostanie ustawiony przez wywołanieCanvas.drawText(String text, float x, float y, Paint paint)
tego samego obiektu Paint z getTextBounds (), należy dodać lewą pozycję Rect. Coś w tym stylu:Zauważ to
bounds.left
- to klucz do problemu.W ten sposób otrzymasz taką samą szerokość tekstu, jak przy użyciu
Canvas.drawText()
.I ta sama funkcja powinna być do uzyskania
height
tekstu:Ps: Nie testowałem dokładnie tego kodu, ale przetestowałem koncepcję.
W tej odpowiedzi podano o wiele bardziej szczegółowe wyjaśnienie .
źródło
Przepraszam, że odpowiedziałem ponownie na to pytanie ... Musiałem osadzić obraz.
Myślę, że wyniki znalezione przez myszy są mylące. Obserwacje mogą być poprawne dla rozmiaru czcionki 60, ale zmieniają się znacznie bardziej, gdy tekst jest mniejszy. Na przykład. 10px. W takim przypadku tekst jest rysowany POZA granicami.
Kod źródłowy zrzutu ekranu:
źródło
ZASTRZEŻENIE: To rozwiązanie nie jest w 100% dokładne pod względem określenia minimalnej szerokości.
Zastanawiałem się też, jak mierzyć tekst na płótnie. Po przeczytaniu świetnego postu od myszy miałem problemy z mierzeniem tekstu wielowierszowego. Nie ma oczywistej drogi od tych wkładów, ale po przeprowadzeniu pewnych badań przeprowadzam przeglądanie klasy StaticLayout. Pozwala mierzyć tekst wielowierszowy (tekst za pomocą „\ n”) i konfigurować znacznie więcej właściwości tekstu za pomocą skojarzonego z nim programu Paint.
Oto fragment pokazujący, jak mierzyć tekst wielowierszowy:
Wrapwitdh jest w stanie określić, czy chcesz ograniczyć tekst wielowierszowy do określonej szerokości.
Ponieważ StaticLayout.getWidth () zwraca tylko to boundedWidth, musisz zrobić kolejny krok, aby uzyskać maksymalną szerokość wymaganą przez tekst wielowierszowy. Jesteś w stanie określić szerokość każdej linii, a maksymalna szerokość jest oczywiście najwyższą szerokością linii:
źródło
i
do getLineWidth (za każdym razem po prostu podajesz 0)Istnieje inny sposób precyzyjnego pomiaru granic tekstu, najpierw powinieneś uzyskać ścieżkę dla bieżącej farby i tekstu. W twoim przypadku powinno być tak:
Następnie możesz zadzwonić:
W moim kodzie zawsze zwraca poprawne i oczekiwane wartości. Ale nie jestem pewien, czy działa szybciej niż twoje podejście.
źródło
Różnice między
getTextBounds
imeasureText
opisano na poniższym obrazku.W skrócie,
getTextBounds
jest uzyskanie RECT dokładnego tekstu. JestmeasureText
to długość tekstu, w tym dodatkowa przerwa po lewej i prawej stronie.Jeśli między tekstem są spacje, jest mierzone,
measureText
ale nie obejmuje długości TextBounds, chociaż współrzędna zostaje przesunięta.Tekst można przechylić (pochylenie) w lewo. W takim przypadku obwiednia po lewej stronie przekroczyłaby wartość pomiaru MeasureText, a całkowita długość oprawionego tekstu byłaby większa niż
measureText
Tekst może być przechylony (pochylenie) w prawo. W takim przypadku obwiednia po prawej stronie przekroczyłaby wartość pomiaru MeasureText, a całkowita długość oprawionego tekstu byłaby większa niż
measureText
źródło
W ten sposób obliczyłem rzeczywiste wymiary pierwszej litery (możesz zmienić nagłówek metody według własnych potrzeb, tj. Zamiast
char[]
używaćString
):Zauważ, że używam TextPaint zamiast oryginalnej klasy Paint.
źródło