Ich messe Text mit Paint.getTextBounds()
, da ich daran interessiert bin, sowohl die Höhe als auch die Breite des zu rendernden Textes zu ermitteln. Der tatsächlich gerenderte Text ist jedoch immer etwas breiter als die .width()
von Rect
ausgefüllten Informationen getTextBounds()
.
Zu meiner Überraschung habe ich getestet .measureText()
und festgestellt, dass es einen anderen (höheren) Wert zurückgibt. Ich habe es versucht und fand es richtig.
Warum melden sie unterschiedliche Breiten? Wie kann ich die Höhe und Breite richtig ermitteln? Ich meine, ich kann verwenden .measureText()
, aber dann würde ich nicht wissen, ob ich dem .height()
zurückgegebenen von vertrauen sollte getTextBounds()
.
Wie angefordert, ist hier minimaler Code, um das Problem zu reproduzieren:
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())
);
}
Die Ausgabe zeigt, dass der Unterschied nicht nur größer als 1 wird (und kein Rundungsfehler in letzter Minute ist), sondern auch mit der Größe zuzunehmen scheint (ich wollte mehr Schlussfolgerungen ziehen, aber er kann vollständig von der Schriftart abhängig sein):
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
quelle
Antworten:
Sie können das tun, was ich getan habe, um ein solches Problem zu untersuchen:
Studieren Sie den Android-Quellcode, Paint.java-Quelle, und sehen Sie sich sowohl die MeasureText- als auch die GetTextBounds-Methode an. Sie werden erfahren, dass MeasureText native_measureText aufruft und getTextBounds nativeGetStringBounds aufruft, native Methoden, die in C ++ implementiert sind.
Sie würden also weiterhin Paint.cpp studieren, das beide implementiert.
native_measureText -> SkPaintGlue :: MeasureText_CII
nativeGetStringBounds -> SkPaintGlue :: getStringBounds
Jetzt prüft Ihre Studie, wo sich diese Methoden unterscheiden. Nach einigen Parameterprüfungen rufen beide die Funktion SkPaint :: MeasureText in Skia Lib (Teil von Android) auf, aber beide rufen unterschiedliche überladene Form auf.
Wenn ich mich weiter mit Skia befasse, sehe ich, dass beide Aufrufe zu derselben Berechnung in derselben Funktion führen und nur unterschiedliche Ergebnisse zurückgeben.
So beantworten Sie Ihre Frage: Beide Anrufe führen dieselbe Berechnung durch. Ein möglicher Unterschied des Ergebnisses besteht darin, dass getTextBounds Grenzen als Ganzzahl zurückgibt , während MeasureText den Gleitkommawert zurückgibt.
Sie erhalten also einen Rundungsfehler während der Konvertierung von float in int. Dies geschieht in Paint.cpp in SkPaintGlue :: doTextBounds beim Aufruf der Funktion SkRect :: roundOut.
Der Unterschied zwischen der berechneten Breite dieser beiden Anrufe kann maximal 1 betragen.
BEARBEITEN 4. Oktober 2011
Was kann besser sein als Visualisierung. Ich habe mir die Mühe gemacht, selbst zu erkunden und Kopfgeld zu verdienen :)
Dies ist die Schriftgröße 60, in Rot ist das Rechteck begrenzt , in Lila ist das Ergebnis von MeasureText.
Es ist zu sehen, dass der linke linke Teil einige Pixel von links beginnt und der Wert von MeasureText sowohl links als auch rechts um diesen Wert erhöht wird. Dies wird als AdvanceX-Wert von Glyph bezeichnet. (Ich habe dies in Skia-Quellen in SkPaint.cpp entdeckt.)
Das Ergebnis des Tests ist also, dass MeasureText dem Text auf beiden Seiten einen Vorabwert hinzufügt, während getTextBounds minimale Grenzen berechnet, an die der angegebene Text passt.
Hoffe, dieses Ergebnis ist nützlich für Sie.
Testcode:
quelle
Meine Erfahrung damit ist,
getTextBounds
dass das absolut minimale Begrenzungsrecht zurückgegeben wird, das den Text einkapselt, nicht unbedingt die gemessene Breite, die beim Rendern verwendet wird. Ich möchte auch sagen, dass diesmeasureText
eine Zeile voraussetzt.Um genaue Messergebnisse zu erhalten, sollten Sie die verwenden
StaticLayout
den Text mit rendern und die Messungen herausziehen.Beispielsweise:
quelle
width
nicht der Fall istmaxWidth
), aber getLineWith () wird es tun. Ich habe auch die statische Methode getDesiredWidth () gefunden, obwohl es kein Höhenäquivalent gibt. Bitte korrigieren Sie die Antwort! Außerdem würde ich gerne wissen, warum so viele verschiedene Methoden zu unterschiedlichen Ergebnissen führen.measureText
funktionieren. Andernfalls, wenn Sie die Breitenkonstanten kennen (z. B. inonMeasure
oderonLayout
, können Sie die oben beschriebene Lösung verwenden. Versuchen Sie, die Textgröße automatisch zu ändern? Der Grund dafür, dass die Textgrenzen immer etwas kleiner sind, liegt darin, dass keine Zeichenauffüllung vorhanden ist , nur das kleinste begrenzte Rechteck, das zum Zeichnen benötigt wird.Die Antwort der Mäuse ist großartig ... Und hier ist die Beschreibung des eigentlichen Problems:
Die kurze einfache Antwort lautet:
Paint.getTextBounds(String text, int start, int end, Rect bounds)
Rückkehr,Rect
die nicht bei beginnt(0,0)
. Das heißt, um die tatsächliche Breite des Texts zu erhalten, der durch AufrufenCanvas.drawText(String text, float x, float y, Paint paint)
mit demselben Paint-Objekt von getTextBounds () festgelegt wird, sollten Sie die linke Position von Rect hinzufügen. Sowas in der Art:Beachten Sie dies
bounds.left
- dies ist der Schlüssel des Problems.Auf diese Weise erhalten Sie die gleiche Textbreite, die Sie mit verwenden würden
Canvas.drawText()
.Und die gleiche Funktion sollte für das Abrufen
height
des Textes sein:Ps: Ich habe diesen genauen Code nicht getestet, aber die Konzeption getestet.
Diese Antwort enthält eine viel detailliertere Erklärung .
quelle
Entschuldigung für die erneute Beantwortung dieser Frage ... Ich musste das Bild einbetten.
Ich denke, die Ergebnisse, die @mice gefunden hat, sind irreführend. Die Beobachtungen mögen für die Schriftgröße von 60 korrekt sein, aber sie werden viel anders, wenn der Text kleiner ist. Z.B. 10px. In diesem Fall wird der Text tatsächlich ÜBER die Grenzen hinaus gezeichnet.
Quellcode des Screenshots:
quelle
HAFTUNGSAUSSCHLUSS: Diese Lösung ist hinsichtlich der Bestimmung der minimalen Breite nicht 100% genau.
Ich habe auch herausgefunden, wie man Text auf einer Leinwand misst. Nachdem ich den großartigen Beitrag von Mäusen gelesen hatte, hatte ich einige Probleme beim Messen von mehrzeiligem Text. Es gibt keinen offensichtlichen Weg von diesen Beiträgen, aber nach einigen Recherchen komme ich durch die StaticLayout-Klasse. Sie können mehrzeiligen Text (Text mit "\ n") messen und über die zugehörige Farbe viel mehr Eigenschaften Ihres Textes konfigurieren.
Hier ist ein Ausschnitt, der zeigt, wie mehrzeiliger Text gemessen wird:
Der Wrapwitdh kann bestimmen, ob Sie Ihren mehrzeiligen Text auf eine bestimmte Breite beschränken möchten.
Da StaticLayout.getWidth () nur diese begrenzte Breite zurückgibt, müssen Sie einen weiteren Schritt ausführen, um die maximale Breite zu erhalten, die für Ihren mehrzeiligen Text erforderlich ist. Sie können jede Linienbreite bestimmen und die maximale Breite ist natürlich die höchste Linienbreite:
quelle
i
in die getLineWidth gehen (Sie gehen jedes Mal nur 0)Es gibt eine andere Möglichkeit, die Textgrenzen genau zu messen. Zuerst sollten Sie den Pfad für die aktuelle Farbe und den aktuellen Text ermitteln. In Ihrem Fall sollte es so sein:
Danach können Sie anrufen:
In meinem Code werden immer korrekte und erwartete Werte zurückgegeben. Aber nicht sicher, ob es schneller funktioniert als Ihr Ansatz.
quelle
Der Unterschied zwischen
getTextBounds
undmeasureText
wird mit dem Bild unten beschrieben.Zusamenfassend,
getTextBounds
ist es, die RECT des genauen Textes zu erhalten. DasmeasureText
ist die Länge des Textes, einschließlich der zusätzlichen Lücke links und rechts.Wenn zwischen dem Text Leerzeichen vorhanden sind, wird dies
measureText
in der Länge der TextBounds gemessen, jedoch nicht berücksichtigt, obwohl die Koordinaten verschoben werden.Der Text kann nach links geneigt werden. In diesem Fall würde der Begrenzungsrahmen auf der linken Seite außerhalb des Maßes für den MeasureText liegen, und die Gesamtlänge des gebundenen Textes wäre größer als
measureText
Der Text kann nach rechts geneigt werden. In diesem Fall würde der Begrenzungsrahmen auf der rechten Seite außerhalb der Messung des MeasureText liegen, und die Gesamtlänge des gebundenen Textes wäre größer als
measureText
quelle
So habe ich die tatsächlichen Abmessungen für den ersten Buchstaben berechnet (Sie können den Methodenkopf an Ihre Bedürfnisse anpassen, dh anstatt ihn zu
char[]
verwendenString
):Beachten Sie, dass ich TextPaint anstelle der ursprünglichen Paint-Klasse verwende.
quelle