Saya mengukur teks menggunakan Paint.getTextBounds()
, karena saya tertarik untuk mendapatkan tinggi dan lebar teks yang akan ditampilkan. Namun, teks yang sebenarnya diberikan selalu sedikit lebih lebar dari .width()
dari Rect
informasi diisi oleh getTextBounds()
.
Yang mengejutkan saya, saya menguji .measureText()
, dan menemukan bahwa itu mengembalikan nilai (lebih tinggi) yang berbeda. Saya mencobanya, dan ternyata benar.
Mengapa mereka melaporkan lebar yang berbeda? Bagaimana saya bisa mendapatkan tinggi dan lebar yang benar? Maksud saya, saya bisa menggunakan .measureText()
, tetapi kemudian saya tidak akan tahu apakah saya harus mempercayai yang .height()
dikembalikan oleh getTextBounds()
.
Seperti yang diminta, ini adalah kode minimal untuk mereproduksi masalah:
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())
);
}
Outputnya menunjukkan bahwa perbedaannya tidak hanya menjadi lebih besar dari 1 (dan tidak ada kesalahan pembulatan menit terakhir), tetapi juga tampaknya meningkat dengan ukuran (saya akan menarik lebih banyak kesimpulan, tetapi mungkin sepenuhnya tergantung pada font):
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
sumber
Jawaban:
Anda dapat melakukan apa yang saya lakukan untuk memeriksa masalah seperti itu:
Pelajari kode sumber Android, sumber Paint.java, lihat kedua metode pengukuranText dan getTextBounds. Anda akan belajar bahwa mengukur mengukur panggilan native_measureText, dan getTextBounds panggilan nativeGetStringBounds, yang merupakan metode asli diimplementasikan dalam C ++.
Jadi, Anda akan terus belajar Paint.cpp, yang mengimplementasikan keduanya.
native_measureText -> SkPaintGlue :: MeasText_CII
nativeGetStringBounds -> SkPaintGlue :: getStringBounds
Sekarang studi Anda memeriksa di mana metode ini berbeda. Setelah beberapa pemeriksaan param, keduanya memanggil fungsi SkPaint :: MeasText di Skia Lib (bagian dari Android), tetapi keduanya memanggil form kelebihan beban yang berbeda.
Menggali lebih jauh ke Skia, saya melihat bahwa kedua panggilan menghasilkan komputasi yang sama dalam fungsi yang sama, hanya mengembalikan hasil yang berbeda.
Untuk menjawab pertanyaan Anda: Kedua panggilan Anda melakukan perhitungan yang sama. Kemungkinan perbedaan hasil terletak pada kenyataan bahwa getTextBounds mengembalikan batas sebagai integer, sementara mengukurText mengembalikan nilai float.
Jadi yang Anda dapatkan adalah pembulatan kesalahan selama konversi float ke int, dan ini terjadi di Paint.cpp di SkPaintGlue :: doTextBounds yang dipanggil untuk berfungsi SkRect :: roundOut.
Perbedaan antara lebar yang dihitung dari kedua panggilan tersebut mungkin secara maksimal 1.
EDIT 4 Okt 2011
Apa yang mungkin lebih baik daripada visualisasi. Saya mengambil upaya, untuk menjelajah sendiri, dan untuk mendapatkan hadiah yang layak :)
Ini adalah ukuran font 60, merah adalah batas persegi panjang, ungu adalah hasil dari sizeText.
Terlihat bahwa batas bagian kiri mulai beberapa piksel dari kiri, dan nilai dari sizeText bertambah dengan nilai ini di kiri dan kanan. Ini adalah sesuatu yang disebut nilai AdvanceX Glyph. (Saya menemukan ini di sumber Skia di SkPaint.cpp)
Jadi hasil dari tes ini adalah mengukurText menambahkan beberapa nilai muka pada teks di kedua sisi, sementara getTextBounds menghitung batas minimal di mana teks yang diberikan akan cocok.
Semoga hasil ini bermanfaat bagi Anda.
Kode pengujian:
sumber
Pengalaman saya dengan ini adalah bahwa
getTextBounds
akan mengembalikan persegi batas minimum absolut yang merangkum teks, belum tentu lebar terukur yang digunakan saat rendering. Saya juga ingin mengatakan bahwameasureText
mengasumsikan satu baris.Untuk mendapatkan hasil pengukuran yang akurat, Anda harus menggunakan
StaticLayout
untuk merender teks dan mengeluarkan pengukuran.Sebagai contoh:
sumber
width
, tidakmaxWidth
), tetapi getLineWith () akan. Saya juga menemukan metode statis getDesiredWidth (), meskipun tidak ada yang setara tinggi. Harap perbaiki jawabannya! Juga, saya benar-benar ingin tahu mengapa begitu banyak metode berbeda menghasilkan hasil yang berbeda.measureText
harus berfungsi dengan baik. Jika tidak, jika Anda mengetahui batasan lebar (seperti dalamonMeasure
atauonLayout
, Anda dapat menggunakan solusi yang saya posting di atas. Apakah Anda mencoba mengubah ukuran teks secara otomatis? Juga, alasan batas teks selalu sedikit lebih kecil adalah karena tidak termasuk padding karakter. , hanya kotak batas terkecil yang diperlukan untuk menggambarJawaban tikus itu hebat ... Dan di sini adalah deskripsi masalah sebenarnya:
Jawaban singkatnya adalah
Paint.getTextBounds(String text, int start, int end, Rect bounds)
pengembalianRect
yang tidak dimulai(0,0)
. Artinya, untuk mendapatkan lebar aktual teks yang akan ditetapkan dengan memanggilCanvas.drawText(String text, float x, float y, Paint paint)
dengan objek Paint yang sama dari getTextBounds (), Anda harus menambahkan posisi kiri Rect. Sesuatu seperti itu:Perhatikan ini
bounds.left
- ini kunci masalahnya.Dengan cara ini Anda akan menerima lebar teks yang sama, yang akan Anda terima menggunakan
Canvas.drawText()
.Dan fungsi yang sama seharusnya untuk mendapatkan
height
teks:PS: Saya tidak menguji kode persis ini, tetapi menguji konsepsi.
Penjelasan lebih rinci diberikan dalam jawaban ini .
sumber
Maaf telah menjawab lagi pertanyaan itu ... Saya harus menyematkan gambar.
Saya pikir hasil @ tikus ditemukan menyesatkan. Pengamatan mungkin benar untuk ukuran font 60 tetapi mereka berubah jauh lebih berbeda ketika teks lebih kecil. Misalnya. 10px. Dalam hal ini teks sebenarnya digambar di luar batas.
Sumber kode tangkapan layar:
sumber
PENOLAKAN: Solusi ini tidak 100% akurat dalam hal menentukan lebar minimum.
Saya juga mencari tahu bagaimana mengukur teks pada kanvas. Setelah membaca posting hebat dari tikus saya punya beberapa masalah tentang bagaimana mengukur teks multiline. Tidak ada cara yang jelas dari kontribusi ini tetapi setelah beberapa penelitian saya melintas di kelas StaticLayout. Ini memungkinkan Anda untuk mengukur teks multiline (teks dengan "\ n") dan mengonfigurasi lebih banyak properti teks Anda melalui Paint terkait.
Berikut ini cuplikan yang menunjukkan cara mengukur teks multiline:
Wrapwitdh dapat menentukan apakah Anda ingin membatasi teks multiline hingga lebar tertentu.
Karena StaticLayout.getWidth () hanya mengembalikan ini boundedWidth Anda harus mengambil langkah lain untuk mendapatkan lebar maksimum yang diperlukan oleh teks multiline Anda. Anda dapat menentukan lebar setiap garis dan lebar maks adalah lebar garis tertinggi tentu saja:
sumber
i
getLineWidth (Anda hanya melewati 0 setiap kali)Ada cara lain untuk mengukur batas teks dengan tepat, pertama Anda harus mendapatkan path untuk Paint dan teks saat ini. Dalam kasus Anda itu harus seperti ini:
Setelah itu Anda dapat menelepon:
Dalam kode saya itu selalu mengembalikan nilai yang benar dan yang diharapkan. Tapi, tidak yakin apakah itu bekerja lebih cepat daripada pendekatan Anda.
sumber
Perbedaan antara
getTextBounds
danmeasureText
dijelaskan dengan gambar di bawah ini.Pendeknya,
getTextBounds
adalah untuk mendapatkan RECT dari teks yang tepat. InimeasureText
adalah panjang teks, termasuk celah ekstra di kiri dan kanan.Jika ada spasi di antara teks, itu diukur
measureText
tetapi tidak termasuk dalam panjang TextBounds, meskipun koordinat digeser.Teks dapat dimiringkan (miring). Dalam hal ini, sisi kiri kotak pembatas akan melebihi di luar pengukuran MeasText, dan panjang keseluruhan dari teks terikat akan lebih besar dari
measureText
Teks bisa dimiringkan (Miring) dengan benar. Dalam hal ini, sisi kanan kotak pembatas akan melebihi di luar pengukuran MeasText, dan panjang keseluruhan dari teks terikat akan lebih besar dari
measureText
sumber
Inilah cara saya menghitung dimensi sebenarnya untuk huruf pertama (Anda dapat mengubah header metode yang sesuai dengan kebutuhan Anda, yaitu alih-alih
char[]
menggunakanString
):Perhatikan bahwa saya menggunakan TextPaint, bukan kelas Paint asli.
sumber