Tampilan teks Android


Menampilkan informasi tekstual mungkin merupakan bagian paling dasar dan penting dari banyak aplikasi Android. Artikel ini akan berbicara tentang TextView. Setiap pengembang, dimulai dengan "Hello World," terus-menerus dihadapkan dengan elemen antarmuka pengguna ini. Dari waktu ke waktu, ketika bekerja dengan teks, Anda harus berpikir tentang mengimplementasikan berbagai solusi desain atau meningkatkan kinerja saat merender layar.


Saya akan berbicara tentang perangkat TextView dan beberapa seluk-beluk bekerja dengannya. Kiat utama diambil dari laporan Google I / O sebelumnya.


TextView di bawah tenda


Untuk rendering teks di Android, seluruh tumpukan perpustakaan yang berbeda digunakan di bawah tenda. Mereka dapat dibagi menjadi dua bagian utama - kode java dan kode asli:



Kode Java pada dasarnya adalah bagian dari SDK Android yang tersedia untuk pengembang aplikasi, dan fitur-fitur baru darinya dapat porting ke pustaka dukungan.


Inti TextView sendiri ditulis dalam C ++, yang membatasi porting ke pustaka pendukung fitur baru yang diimplementasikan di sana dari versi baru sistem operasi. Inti adalah pustaka berikut:


  • Minikin digunakan untuk mengukur panjang teks, jeda baris dan kata-kata berdasarkan suku kata.
  • ICU menyediakan dukungan Unicode.
  • HarfBuzz menemukan untuk karakter Unicode elemen grafis yang sesuai (mesin terbang) dalam font.
  • FreeType membuat bitmap dari mesin terbang.
  • Skia adalah mesin untuk menggambar grafik 2D.

Mengukur panjang teks dan jeda baris


Jika Anda meneruskan baris ke perpustakaan Minikin, yang digunakan di dalam TextView, maka hal pertama yang menentukan adalah mesin terbang mana yang terdiri dari baris:



Seperti yang dapat Anda lihat dari contoh ini, mencocokkan karakter Unicode dengan mesin terbang tidak akan selalu satu ke satu: di sini 3 karakter sekaligus akan sesuai dengan satu mesin terbang ffi. Selain itu, perlu memperhatikan bahwa mesin terbang yang diperlukan dapat ditemukan di berbagai font sistem.


Menemukan mesin terbang hanya dalam font sistem dapat menyebabkan kesulitan, terutama jika ikon atau emoji ditampilkan melalui karakter, dan seharusnya menggabungkan karakter dari font yang berbeda dalam satu baris. Oleh karena itu, dimulai dengan Android Q (29) , menjadi mungkin untuk membuat daftar font Anda sendiri yang datang dengan aplikasi. Daftar ini akan digunakan untuk mencari mesin terbang:


 textView.typeface = TypeFace.CustomFallbackBuilder( FontFamily.Builder( Font.Builder(assets, “lato.ttf”).build() ).build() ).addCustomFallback( FontFamily.Builder( Font.Builder(assets, “kosugi.ttf”).build() ).build() ).build() 

Sekarang, menggunakan CustomFallbackBuilder ketika mencocokkan karakter dengan mesin terbang, SDK akan CustomFallbackBuilder ke kelompok font yang ditentukan secara berurutan, dan jika tidak dapat ditemukan, pencarian akan melanjutkan dalam font sistem (dan melalui metode setSystemFallback() Anda dapat menentukan keluarga font sistem yang disukai). CustomFallbackBuilder memiliki batasan jumlah keluarga font - Anda dapat menambahkan tidak lebih dari 64 font.


Perpustakaan Minikin membagi string menjadi kata-kata dan mengukur kata-kata individual. Untuk mempercepat pekerjaan, dimulai dengan Lollipop (21) , digunakan cache kata-kata LRU sistem. Cache semacam itu memberikan peningkatan kinerja yang sangat besar: panggilan ke Paint.measureText() untuk kata yang di-cache akan membutuhkan rata-rata 3% dari waktu pertama kali menghitung ukurannya.



Jika teks tidak sesuai dengan lebar yang ditentukan, Minikin mengatur jeda baris dan kata-kata dalam teks. Dimulai dengan Marshmallow (23), Anda dapat mengontrol perilakunya dengan menentukan atribut khusus breakStrategy dan breakStrategy untuk TextView.


Dengan nilai breakStrategy=simple perpustakaan hanya akan mengatur tanda hubung secara berurutan, melewati teks: segera setelah baris berhenti sesuai, tanda hubung diletakkan sebelum kata terakhir.



Dalam nilai balanced pustaka akan mencoba membuat garis putus sehingga garis selaras lebar.



high_quality memiliki perilaku yang hampir sama dengan balanced , dengan pengecualian beberapa perbedaan (salah satunya: pada garis kedua dari belakang, tanda hubung dapat tidak hanya kata-kata yang terpisah, tetapi juga kata-kata oleh suku kata).


Atribut hyphenationFrequency memungkinkan hyphenationFrequency untuk mengontrol strategi untuk pembungkus kata dengan suku kata. Nilai none akan melakukan hyphenation otomatis, normal akan membuat frekuensi hyphenation kecil, dan full , karenanya, akan menggunakan jumlah kata maksimum.



Kinerja rendering teks tergantung pada flag yang dipilih (diukur pada Android P (28) ):



Mengingat hit kinerja yang cukup kuat, pengembang Google, mulai dengan versi Q (29) dan AppCompat 1.1.0 , memutuskan untuk mematikan tanda hubung secara default. Jika pembungkus kata penting dalam aplikasi, sekarang Anda harus mengaktifkannya secara eksplisit.


Saat menggunakan bungkus kata, orang harus mempertimbangkan bahwa bahasa yang dipilih saat ini dalam sistem operasi akan mempengaruhi operasi perpustakaan. Bergantung pada bahasanya, sistem akan memilih kamus khusus dengan aturan transfer.


Gaya teks


Ada beberapa cara mendesain teks di Android:


  • Gaya tunggal yang berlaku untuk seluruh elemen TextView.
  • Multi-gaya (multi style) - beberapa gaya sekaligus, yang dapat diterapkan ke teks, di tingkat paragraf atau karakter individu. Ada beberapa cara untuk melakukan ini:
    • menggambar teks di atas kanvas
    • tag html
    • elemen markup khusus - rentang

Gaya tunggal menyiratkan penggunaan gaya XML atau atribut XML di markup TextView. Dalam hal ini, sistem akan menerapkan nilai dari sumber daya dalam urutan berikut: TextAppearance, theme (Theme), style default (style default), style dari aplikasi, dan prioritas tertinggi adalah nilai dari atribut View.


Menggunakan sumber daya adalah solusi yang cukup sederhana, tetapi, sayangnya, itu tidak memungkinkan Anda untuk menerapkan gaya ke bagian teks.


Tag HTML adalah solusi sederhana lain yang menyediakan fitur seperti membuat setiap kata menjadi tebal, miring, atau bahkan menyorot daftar dengan titik-titik dalam teks. Semua yang dibutuhkan pengembang adalah melakukan panggilan ke metode Html.fromHtml() , yang akan mengubah teks yang ditandai menjadi teks yang ditandai oleh rentang. Tetapi solusi ini memiliki kemampuan terbatas, karena hanya mengenali sebagian dari tag html dan tidak mendukung gaya CSS.


 val text = "My text <ul><li>bullet one</li><li>bullet two</li></ul>" myTextView.text = Html.fromHtml(text) 

Berbagai metode penataan TextView dapat dikombinasikan, tetapi perlu diingat prioritas metode tertentu, yang akan memengaruhi hasil akhir:



Cara lain - menggambar teks pada kanvas - memberi pengembang kendali penuh atas output teks: misalnya, Anda dapat menggambar teks di sepanjang garis lengkung. Tetapi solusi seperti itu, tergantung pada persyaratan, bisa sangat sulit untuk diterapkan dan berada di luar cakupan artikel ini.


Rentang


TextView menggunakan rentang untuk menyempurnakan gaya. Menggunakan bentang, Anda dapat mengubah warna rentang karakter, menjadikan bagian teks sebagai tautan, mengubah ukuran teks, menggambar titik di depan paragraf, dll.


Kategori rentang berikut dapat dibedakan:


  • Rentang karakter - diterapkan pada level karakter string.
    • Penampilan memengaruhi - jangan ubah ukuran teks.
    • Metrik yang mempengaruhi - mengubah ukuran teks.
  • Rentang paragraf - diterapkan di tingkat paragraf.


Kerangka kerja Android memiliki antarmuka dan kelas abstrak dengan metode yang disebut selama onMeasure() dan rendering dari TextView, metode ini memberikan akses bentang ke objek tingkat lebih rendah seperti TextPaint dan Canvas . Menggunakan span, kerangka kerja Android memeriksa antarmuka mana yang diimplementasikan objek ini untuk memanggil metode yang diperlukan.



Kerangka kerja android mendefinisikan sekitar 20+ rentang, jadi sebelum Anda membuatnya, lebih baik untuk memeriksa apakah SDK cocok.


Tampilan vs metrik mempengaruhi bentang


Kategori pertama rentang mempengaruhi bagaimana karakter dalam string akan terlihat: warna karakter, warna latar belakang, karakter yang digarisbawahi atau dicoret, dll. UpdateAppearance ini menerapkan antarmuka UpdateAppearance dan mewarisi dari kelas CharacterStyle , yang menyediakan akses ke objek TextPaint .



Metrik yang mempengaruhi rentang mempengaruhi ukuran teks dan tata letak, oleh karena itu, penggunaan rentang seperti itu tidak hanya memerlukan menggambar ulang TextView, tetapi juga panggilan onMeasure() / onLayout() . MetricAffectingSpan ini biasanya diwarisi dari kelas MetricAffectingSpan , yang diwarisi dari CharacterStyle disebutkan di atas.



Karakter vs paragraf yang mempengaruhi rentang


Rentang paragraf memengaruhi seluruh blok teks: ia dapat mengubah perataan, lekukan, atau bahkan menyisipkan titik di awal paragraf. Rentang seperti itu harus diwarisi dari kelas ParagraphStyle dan disisipkan ke dalam teks persis dari awal paragraf hingga akhirnya. Jika rentang tidak benar, maka rentang tidak akan berfungsi.



Di Android, paragraf dianggap sebagai bagian dari teks yang dipisahkan oleh baris baru ( \n ).



Menulis Rentang Anda


Saat menulis rentang Anda sendiri, Anda perlu memutuskan rentang apa yang akan memengaruhi untuk memilih kelas yang akan diwarisi:


  • Mempengaruhi teks pada level karakter -> CharacterStyle
  • Mempengaruhi teks di tingkat paragraf -> ParagraphStyle
  • Mempengaruhi tampilan teks → UpdateAppearance
  • Mempengaruhi ukuran teks - UpdateLayout

Berikut adalah contoh rentang untuk mengubah font:


 class CustomTypefaceSpan(private val font: Typeface?) : MetricAffectingSpan() { override fun updateMeasureState(textPaint: TextPaint) = update(textPaint) override fun updateDrawState(textPaint: TextPaint) = update(textPaint) fun update(textPaint: TextPaint) { textPaint.apply { val old = typeface val oldStyle = old?.style ?: 0 val font = Typeface.create(font, oldStyle) typeface = font //    } } } 

Bayangkan kita ingin membuat rentang kita sendiri untuk menyoroti blok kode, untuk ini kita akan mengedit rentang kita sebelumnya - setelah mengatur font, kita akan menambahkan perubahan dalam warna latar belakang teks:


 class CodeBlockSpan(private val font: Typeface?) : MetricAffectingSpan() { … fun update(textPaint: TextPaint) { textPaint.apply { //    … bgColor = lightGray //    } } } 

Terapkan rentang ke teks:


 //    span spannable.setSpan(CodeBlockSpan(typeface), ...) 

Tetapi Anda bisa mendapatkan hasil yang persis sama dengan menggabungkan dua rentang: ambil CustomTypefaceSpan dan BackgroundColorSpan sebelumnya dari kerangka Android:


 //    spannable.setSpan(BackgroundColorSpan(lightGray), ...) //   spannable.setSpan(CustomTypefaceSpan(typeface), ...) 

Dua solusi ini akan memiliki perbedaan. Faktanya adalah rentang yang ditulis sendiri tidak dapat mengimplementasikan antarmuka Parcelable , tidak seperti yang sistem.


Saat mentransmisikan saluran bergaya melalui Intent atau clipboard jika rentang markup yang ditulis sendiri tidak akan disimpan. Saat menggunakan bentang dari framework, markup akan tetap ada.



Menggunakan bentang dalam teks


Ada dua antarmuka untuk teks bergaya dalam kerangka: Spannable dan Spannable (masing-masing dengan markup tidak berubah dan bisa berubah, masing-masing) dan tiga implementasi: SpannedString (teks tidak berubah), SpannableString (teks tidak berubah) dan SpannableStringBuilder (teks dapat diubah).


Teks yang bisa diubahMarkup variabel
String yang terentangtidaktidak
String yang dapat dipentangtidakiya
Pembangun Spannablestringiyaiya

SpannableStringBuilder , misalnya, digunakan di dalam EditText yang perlu mengubah teks.


Anda dapat menambahkan rentang baru ke garis menggunakan metode:


setSpan(Object what, int start, int end, int flags)


Rentang dilewatkan melalui parameter pertama, kemudian rentang indeks dalam teks ditunjukkan. Dan parameter terakhir dapat dikontrol, apa yang akan menjadi perilaku rentang ketika memasukkan teks baru: apakah rentang akan menyebar ke teks yang dimasukkan pada titik awal atau akhir (jika Anda memasukkan teks baru di tengah, rentang akan secara otomatis berlaku untuk itu, terlepas dari nilai bendera) .


Kelas-kelas yang tercantum di atas berbeda tidak hanya secara semantik, tetapi juga dalam bagaimana mereka diatur secara internal: SpannedString dan SpannableString menggunakan array untuk menyimpan rentang, dan SpannableStringBuilder menggunakan pohon interval .


Jika Anda melakukan tes untuk kecepatan rendering teks tergantung pada jumlah bentang, Anda akan mendapatkan hasil berikut: ketika menggunakan hingga ~ 250 bentang berturut-turut, SpannableString dan SpannableStringBuilder bekerja pada kecepatan yang sama, tetapi jika elemen markup menjadi lebih dari 250, maka SpannableString dimulai kalah. Jadi, jika tugasnya adalah menerapkan gaya ke beberapa teks, maka ketika memilih kelas, seseorang harus dipandu oleh persyaratan semantik: apakah garis dan gaya akan bisa berubah. Tetapi jika markup membutuhkan lebih dari 250 rentang, maka Anda harus selalu memberikan SpannableStringBuilder untuk SpannableStringBuilder .


Periksa rentang dalam teks


Tugas muncul secara berkala untuk memeriksa apakah garis yang direntang memiliki rentang tertentu. Dan di Stackoverflow Anda dapat menemukan kode ini:


 fun <T> hasSpan(spanned: Spanned, clazz: Class<T>): Boolean { val spans: Array<out T> = spanned.getSpans(0, spanned.length, clazz) return spans.isNotEmpty() } 

Solusi seperti itu akan berhasil, tetapi tidak efisien: Anda harus memeriksa semua bentang, memeriksa apakah masing-masing milik tipe yang diteruskan, mengumpulkan hasilnya ke dalam array, dan pada akhirnya hanya memeriksa bahwa array tidak kosong.


Solusi yang lebih efektif adalah dengan menggunakan metode nextSpanTransition() :


 fun <T> hasSpan(spanned: Spanned, clazz: Class<T>): Boolean { val limit = spanned.length return spanned.nextSpanTransition(0, limit, clazz) < limit } 

Markup teks dalam berbagai sumber daya bahasa


Tugas semacam itu dapat muncul ketika Anda ingin menyorot kata tertentu dengan bantuan markup di berbagai sumber daya string. Misalnya, kita perlu menyorot kata "teks" dalam versi bahasa Inggris dan "texto" dalam bahasa Spanyol:


 <!-- values-en/strings.xml --> <string name="title">Best practices for text in Android</string> <!-- values-es/strings.xml --> <string name=”title”>Texto en Android: mejores prácticas</string> 

Jika Anda membutuhkan sesuatu yang sederhana, misalnya, untuk menyorot kata dalam huruf tebal, maka Anda dapat menggunakan tag html biasa ( <b> ). Di UI, Anda hanya perlu mengatur sumber string di TextView:


 textView.setText(R.string.title) 

Tetapi jika Anda membutuhkan sesuatu yang lebih kompleks, misalnya mengubah font, maka html tidak dapat lagi digunakan. Solusinya adalah dengan menggunakan <annotation> . Tag ini memungkinkan Anda untuk menentukan pasangan nilai kunci apa pun dalam file xml. Saat kami menarik string dari sumber, tag ini secara otomatis dikonversi ke rentang Annotation , disusun dalam teks dengan kunci dan nilai yang sesuai. Setelah itu, Anda dapat menguraikan daftar anotasi dalam teks dan menerapkan rentang yang diperlukan.


Misalkan kita perlu mengubah font menggunakan CustomTypefaceSpan .


Tambahkan tag dan tentukan kunci "font" untuknya dan nilai - jenis font yang ingin kita gunakan adalah "title_emphasis" :

 <!-- values-en/strings.xml --> <string name="title">Best practices for <annotation font=”title_emphasis”>text</annotation> in Android</string> <!-- values-es/strings.xml --> <string name=”title”><annotation font=”title_emphasis”>Texto</annotation> en Android: mejores prácticas</string> 

Tarik string dari sumber, temukan anotasi dengan tombol “font” dan atur rentang:


 //      SpannedString,     span' val titleText = getText(R.string.title) as SpannedString //    val annotations = titleText.getSpans(0, titleText.length, Annotation::class.java) //     SpannableString //      val spannableString = SpannableString(titleText) //     for (annotation in annotations) { //     "font" if (annotation.key == "font") { val fontName = annotation.value //   ,     if (fontName == "title_emphasis") { val typeface = getFontCompat(R.font.permanent_marker) //  span    ,    spannableString.setSpan( CustomTypefaceSpan(typeface), titleText.getSpanStart(annotation), titleText.getSpanEnd(annotation), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) } } } styledText.text = spannableString 


Disebutkan di atas bahwa bentang dari luar kerangka Android tidak dapat mengimplementasikan Parcelable dan dikirim melalui Intent. Tetapi ini tidak berlaku untuk anotasi yang mengimplementasikan Parcelable . Jadi Anda bisa melewatkan string beranotasi melalui Intent dan mengurai dengan cara yang persis sama dengan mengatur rentang Anda.


Bagaimana teks ditempatkan di TextView


TextView tidak hanya dapat menampilkan teks, tetapi juga gambar. Anda juga dapat mengatur berbagai indentasi di depan teks. Di bawah tenda, ini berfungsi agar TextView membuat kelas anak, Layout, yang bertanggung jawab langsung untuk menampilkan teks. Ini adalah kelas abstrak yang memiliki tiga implementasi, biasanya Anda tidak harus bekerja secara langsung dengannya kecuali Anda menulis kontrol sendiri:


  • BoringLayout digunakan untuk teks sederhana, tidak mendukung jeda baris, RTL, dan hal-hal lain, tetapi ini adalah yang paling ringan. TextView menggunakannya jika teks memenuhi semua batasan.
  • StaticLayout digunakan dalam TextView untuk kasus lain.
  • DynamicLayout digunakan untuk teks yang bisa diubah dalam EditText.

Layout memiliki banyak metode yang memungkinkan Anda mengetahui berbagai parameter teks yang ditampilkan: koordinat garis, garis dasar, koordinat awal dan akhir teks dalam garis, dll. (perincian lebih lanjut dapat ditemukan di dokumentasi )


Metode seperti itu bisa sangat berguna. Misalnya, beberapa pengembang dihadapkan dengan tugas mengekstraksi bagian teks ke dalam persegi panjang bulat, dan berusaha menemukan solusinya melalui bentang yang tidak berlaku dalam menyelesaikan masalah ini.



Tetapi metode kelas Layout bisa menyelamatkan. Berikut adalah contoh solusi:


Menggunakan anotasi, kami memilih kata-kata yang harus dilingkari dalam persegi panjang.


Kemudian buat 4 sumber daya yang dapat digambar untuk semua kasus pembungkus teks, yang harus dilampirkan dalam persegi panjang:



Selanjutnya, kami menemukan penjelasan yang kami butuhkan dalam teks, seperti dijelaskan di atas. Sekarang kita memiliki indeks awal dan akhir anotasi tersebut. Melalui metode Tata Letak, Anda dapat mengetahui jumlah baris tempat teks beranotasi dimulai, dan di mana ia berakhir:


 val startLine = layout.getLineForOffset(spanStartIndex) val endLine = layout.getLineForOffset(spanEndIndex) 

Selanjutnya, Anda harus menggambar satu atau lebih persegi panjang. Pertimbangkan kasus sederhana ketika bagian beranotasi dari teks muncul pada satu baris, maka kita hanya perlu satu persegi panjang dengan empat sudut bulat. Tentukan koordinatnya dan gambar:


 ... if (startLine == endLine) { val lineTop = layout.getLineTop(startLine) //    val lineBottom = layout.getLineBottom(startLine) //    val startCoor = layout.getPrimaryHorizontal(spanStartIndex).toInt() //    val endCoor = layout.getPrimaryHorizontal(spanEndIndex).toInt() //    //   drawable.setBounds(startCoor, lineTop, endCoor, lineBottom) drawable.draw(canvas) ... 

Seperti yang dapat Anda lihat dari contoh ini, Layout menyimpan banyak informasi berguna pada teks yang ditampilkan, yang dapat membantu dalam pelaksanaan berbagai tugas yang tidak standar.


Kinerja TextView


TextView, seperti halnya View, melewati tiga fase saat ditampilkan: onMeasure() , onLayout() dan onDraw() . Pada saat yang sama, onMeasure() membutuhkan waktu paling banyak, tidak seperti dua metode lainnya: saat ini, kelas Layout dibuat kembali dan ukuran teks dihitung. Jadi mengubah ukuran teks (misalnya, mengubah font) memerlukan banyak pekerjaan. Mengubah warna teks akan lebih ringan karena hanya membutuhkan memanggil onDraw() . Seperti disebutkan di atas, sistem memiliki kata cache global dengan ukuran yang dihitung. Jika kata tersebut sudah ada dalam cache, maka memanggil onMeasure() untuk itu akan membutuhkan 11-16% dari waktu yang diperlukan untuk perhitungan lengkap.


Akselerasi teks


Pada 2015, pengembang Instagram mempercepat tampilan komentar pada foto menggunakan cache global. Idenya adalah untuk menggambar teks sebelum ditampilkan di layar, sehingga "menghangatkan" cache sistem. Ketika tiba saatnya untuk menampilkan teks, pengguna melihatnya lebih cepat, karena teks sudah diukur dan berada di cache.


Dimulai dengan Android P (28) , pengembang Google telah menambahkan ke API kemampuan untuk melakukan fase mengukur ukuran teks terlebih dahulu di utas latar belakang - PrecomputedText (dan backport untuk API dimulai dengan Android I (14) - PrecomputedTextCompat ). Menggunakan API baru, 90% pekerjaan akan dilakukan di utas latar belakang.


Contoh:


 // UI thread val params: PrecomputedText.Params = textView.getTextMetricsParams() val ref = WeakReference(textView) executor.execute { // background thread val text = PrecomputedText.create("Hello", params) val textView = ref.get() textView?.post { // UI thread val textView = ref.get() textView?.text = text } } 

Tampilkan teks besar


Jika Anda perlu menampilkan teks besar, maka jangan segera mentransfernya ke TextView. Jika tidak, aplikasi mungkin berhenti bekerja dengan lancar atau benar-benar membeku, karena itu akan melakukan banyak pekerjaan pada utas utama untuk menunjukkan teks besar bahwa pengguna bahkan tidak dapat menggulir hingga akhir. Solusinya adalah dengan membagi teks menjadi beberapa bagian (mis. Paragraf) dan menampilkan masing-masing bagian dalam RecyclerView. Untuk speedup yang lebih besar, Anda dapat menghitung sebelum ukuran blok teks menggunakan PrecomputedText.


Untuk memfasilitasi pemasangan PrecomputedText di RecyclerView, pengembang Google membuat metode khusus untuk PrecomputedTextCompat.getTextFuture() dan AppCompatTextView.setTextFuture() :


 fun onBindViewHolder(vh: ViewHolder, position: Int) { val data = getData(position) vh.textView.setTextSize(...) vh.textView.setFontVariationSettings(...) //    val future = PrecomputedTextCompat.getTextFuture( data.text, vh.textView.getTextMetricsParamsCompat(), myExecutor ) //  future  TextView,      onMeasure() vh.textView.setTextFuture(future) } 

RecyclerView , , , .


, getTextFuture() (, ), , , getTextFuture() , , TextView.


, TextView


TextView.setText() :


 if (type == SPANNABLE || movementMethod != null) { text = spannableFactory.newSpannable(spannable) //  } else { text = new SpannedString(spannable) //  } 

span' TextView, setText() , .


, . TextView , -, . , . , , TextView spannableFactory :


 class MySpannableFactory : Spannable.Factory() { override fun newSpannable(source: CharSequence): Spannable { return source as? Spannable ?: super.newSpannable(source) } } textView.spannableFactory = MySpannableFactory() 

textView.setText(spannable, BufferType.SPANNABLE) , .


Google span' RecyclerView, .


TextView, span, setText() . TextView span. TextView spannable- span', :


 val spannable = textView.getText() as Spannable val span = CustomTypefaceSpan(span) spannable.setSpan(span, ...) 

span, TextView, TextView . , invalidate() , – requestLayout() :


 val spannable = textView.getText() as Spannable val span = CustomTypefaceSpan(span) spannable.setSpan(span, ...) span.setTypeface(anotherTypeface) textView.requestLayout() // re-measure and re-draw // or textView.invalidate() // re-draw 


TextView . autoLink . autoLink=”web” TextView URL URLSpan . , SDK setText() :


 spannable = new SpannableString(string); Matcher m = pattern.matcher(text); while (...) { //      String utl = … URLSpan span = new URLSpan(url); spannable.setSpan(span, ...); } 

UI , autoLink=”web” RecyclerView. . LinkifyCompat :


 //  ,       background thread val spannable = SpannableString(string) LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS) //   RecyclerView override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.textView.setText(spannable, BufferType.SPANNABLE) // ... } 

autoLink map – ( all ). . , WebView, ! SDK Linkify.gatherMapLinks() , :


 while ((address = WebView.findAddress(string)) != null) { ... } 

WebView TODO SDK:


 public static String findAddress(String addr) { // TODO: Rewrite this in Java so it is not needed to start up chromium // Could also be deprecated return getFactory().getStatics().findAddress(addr); } 

? Smart Linkify, Android P (28) , , . :


 // UI thread val text: Spannable = … val request = TextLinks.Request.Builder(text) val ref = WeakReference(textView) executor.execute { // background thread TextClassifier.generateLinks(request).apply(text) val textView = ref.get() textView?.post { // UI thread val textView = ref.get() textView?.text = text } } 

Linkify, . toolbar , Google .


Smart Linkify : , .



Magnifier


Android P (28) , – Magnifier, . .



TextView, EditText WebView, : API .


Kesimpulan


Android , , :


  • , TextView (, EditText)

- , Google I/O'19 “Best Practices for Using Text in Android” .



Tautan yang bermanfaat




Laporan


Source: https://habr.com/ru/post/id461787/


All Articles