首頁 > 軟體

Android 14新功能HighLights快速實現文字高亮

2023-03-29 06:02:06

正文

日常開發中可能會遇到給 TextView 的全部或部分文字增加高亮效果的需求,以前可能是通過 Spannable 或者 Html 標籤實現。

升級 Android 14 後就不用這麼迂迴了,因其首次引入直接設定高亮的 API:HighLights。需要留意的是 HighLights API 和 Android 1.0 即加入的 textColorHighlight API 不同:

  • 14 的新 API 就是文字在 normal 狀態下的高亮,之前這個是為了設定選中時文字高亮,
  • 14 的新 API 只提供了 get/set 方法,沒有提供與之匹配的 attribute。而之前的 API還提供了 android:textColorHighlight attribute 設定

下面我們就來一探這個新 API 的玩法和 textColorHighlight API 的區別。

目錄前瞻:

  • 設定高亮
  • 獲取高亮
  • 動態更新高亮
  • 與選中時效果是否衝突
  • 結語

1. 設定高亮

HighLights 採用的是熟知的建造者模式,即首先需要構建不同引數的 Builder 範例,針對引數也提供了兩種設定方式:

一次指定單組高亮設定:addRange(Paint paint, int start, int end),如果多組需要設定同樣高亮顏色的話,那要呼叫多次

一次指定多組高亮設定:addRange(Paint paint, int... ranges),如果多組需要設定同樣高亮顏色的話,只要呼叫一次即可

既然是多組範圍那麼 int 引數必須是偶數數目的,即成對出現,反之會發生如下的 Exception:

java.lang.IllegalArgumentException: Flatten ranges must have even numbered elements

可以說上述兩個 API 的引數都是成對出現,對於高亮的響應範圍的話:前者是包含 inclusive 在內的,後者是不包含 exclusive 在內的,需要注意。

我們通過程式碼範例演示通過上述兩個 Builder API 構建一樣的高亮效果,然後通過 TextViewsetHighLights() 反映。

 class MainActivity : AppCompatActivity() {
     companion object {
         const val TEXT = "val builder = Highlights.Builder()"
     }
     override fun onCreate(savedInstanceState: Bundle?) {
         ...
         val yellowPaint = Paint().apply {
             color = Color.YELLOW
         }
         val greenPaint = Paint().apply {
             color = Color.GREEN
         }
         with(binding.textview1) {
             text = TEXT
             val builder = Highlights.Builder()
                 .addRange(yellowPaint, 0, 3)
                 .addRange(greenPaint, 14, 24)
                 .addRange(greenPaint, 25, 32)
             highlights = builder.build()
         }
         with(binding.textview2) {
             text = TEXT
             val builder = Highlights.Builder()
                 .addRanges(yellowPaint, 0, 3)
                 .addRanges(greenPaint, 14, 24, 25, 32)
             highlights = builder.build()
         }
     }
 }

可以看到不同的 Builder 引數設定方式可以對 val 設定黃色高亮,Highlights 和 Builder 設定綠色高亮。

2. 獲取高亮

設定到 TextView 物件的 HighLights 範例還可以通過 getHighlights() 獲取,並通過如下的 API 獲取高亮的細節:

首先通過 getSize() 獲取設定高亮的數量

其次從 0 開始遍歷下標

  • 通過 getPaint(int index) 獲取高亮的 Paint 物件
  • 以及通過 getRanges(int index) 獲取對應的 Paint 範圍 Ranges(也是一個陣列,需要遍歷列印具體的起始位置)
 class MainActivity : AppCompatActivity() {
     ...
     override fun onCreate(savedInstanceState: Bundle?) {
         ...
         binding.textview1.highlights?.run {
             Log.d("HighLights", "textview1 usedHighLights' size:$size")
             for (i in 0 until size) {
                 Log.d("HighLights", "usedHighLights'" +
                         " paint:${getPaint(i).color.toColorString()}")
                 val range = getRanges(i)
                 for (j in range.indices) {
                     Log.d("HighLights", "ranges:${range[j]}")
                 }
             }
         }
         binding.textview2.highlights?.run {
             Log.d("HighLights", "textview2 usedHighLights' size:$size")
             for (i in 0 until size) {
                 Log.d("HighLights", "usedHighLights'" +
                         " paint:${getPaint(i).color.toColorString()}")
                 val range = getRanges(i)
                 for (j in range.indices) {
                     Log.d("HighLights", "ranges:${range[j]}")
                 }
             }
         }
     }
 }

如下的 log 可以看到列印出來的 Paint 顏色、範圍 Ranges 和設定的引數是一一對應的。

 03-23 23:08:27.196  7182  7182 D HighLights: textview1 usedHighLights' size:3
 03-23 23:08:27.196  7182  7182 D HighLights: usedHighLights' paint:YELLOW
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:0
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:3
 03-23 23:08:27.196  7182  7182 D HighLights: usedHighLights' paint:GREEN
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:14
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:24
 03-23 23:08:27.196  7182  7182 D HighLights: usedHighLights' paint:GREEN
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:25
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:32
 
 03-23 23:08:27.196  7182  7182 D HighLights: textview2 usedHighLights' size:2
 03-23 23:08:27.196  7182  7182 D HighLights: usedHighLights' paint:YELLOW
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:0
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:3
 03-23 23:08:27.196  7182  7182 D HighLights: usedHighLights' paint:GREEN
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:14
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:24
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:25
 03-23 23:08:27.196  7182  7182 D HighLights: ranges:32

3. 動態更新高亮

既然我們可以獲取已經設定的 HighLights,那麼更新其屬性,能否動態更新高亮效果呢?

首先在 TextView 下新增動態更新 HighLights 的 Button

  • 然後點選該 Button 之後將 textView1 的 Paint 顏色從 GREEN 改為 BLUE,並將其中 “Highlights” 的文字範圍增大:頭尾各擴充套件一個或多個下標
 class MainActivity : AppCompatActivity() {
     ...
     override fun onCreate(savedInstanceState: Bundle?) {
         ...
         binding.changeHighlights.setOnClickListener {
             Log.d("HighLights", "changeHighlights tapped & change highlights")
             textView1Highlights?.apply {
                 // Change color
                 getPaint(1).color = Color.BLUE
                 // Change ranges
                 getRanges(1)[0] -= 3
                 getRanges(1)[1] += 1
                 for (i in 0 until size) {
                     Log.d("HighLights", "textView1Highlights'" +
                             " paint:${getPaint(i).color.toColorString()}")
                     val range = getRanges(i)
                     for (j in range.indices) {
                         Log.d("HighLights", "ranges:${range[j]}")
                     }
                 }
             }
             binding.textview1.invalidate()
         }
     }
 }

點選 Button 之後,顏色確實變成了藍色,但是高亮範圍卻沒有變化。

我們列印的更新後 HighLights 的引數 log:可以看到,無論是顏色(GREEN -> BLUE)還是範圍(14 -> 11,24 -> 25)確實都已經更改了。

可為什麼唯獨 Ranges 沒有重新整理?有可能是 14 預覽版階段的 Bug。

 03-25 10:47:29.276  5344  5344 D HighLights: changeHighlights tapped & change highlights
 03-25 10:47:29.276  5344  5344 D HighLights: textview1 textView1Highlights' size:3
 03-25 10:47:29.276  5344  5344 D HighLights: textView1Highlights' paint:YELLOW
 03-25 10:47:29.276  5344  5344 D HighLights: ranges:0
 03-25 10:47:29.276  5344  5344 D HighLights: ranges:3
 03-25 10:47:29.277  5344  5344 D HighLights: textView1Highlights' paint:BLUE
 03-25 10:47:29.277  5344  5344 D HighLights: ranges:11
 03-25 10:47:29.277  5344  5344 D HighLights: ranges:25
 03-25 10:47:29.277  5344  5344 D HighLights: textView1Highlights' paint:BLUE
 03-25 10:47:29.277  5344  5344 D HighLights: ranges:25
 03-25 10:47:29.277  5344  5344 D HighLights: ranges:32

4. 與選中時效果是否衝突

我們給上述其中一個 TextView 新增選中高亮顏色的設定即 textColorHighlight,該顏色與上述 HighLights 顏色不同,以清晰地判斷兩種高亮是否會發生衝突。

注意需要將 textIsSelectable 設定為 true,這樣 TextView 才可以被長按選中。

 <androidx.constraintlayout.widget.ConstraintLayout ... >
     <TextView
         android:id="@+id/textview1"
         ...
         android:textColorHighlight="@color/purple_200"
         android:textIsSelectable="true"
         ... />
     < ... >
 </androidx.constraintlayout.widget.ConstraintLayout>

我們在該 TextView 上長按看一下效果:

可以看到水滴選中的範圍內會變成我們設定的 textColorHighlight 紫色高亮,未選中的部分會按照 HighLights 設定的那樣展示黃色和綠色以及沒有設定 HighLights 的預設淺灰色。

5. 結語

可以看到新功能 HighLights 可以使得高亮的處理變得簡單、易用,大家可以在 14 上採用該 API,當高版本普及後,低版本上的自定義高亮邏輯就可以捨棄了。

至於其原理,因為 Android 14 尚處於預覽版階段、原始碼沒有公開,無法獲悉實現。但估計是 TextViewdraw 階段會獲取設定的 HighLights 包含的 size 以及對應的 PaintRanges,得以清晰地掌握各高亮的顏色和對應的範圍,然後直接呼叫 CanvasdrawText(text, start, end, x, y, paint) 去完成繪製。

可以說 HighLights 這種 API 既方便了開發者的使用:從設定高亮到獲取高亮到動態更新高亮,其清晰的邏輯一定程度上也可以簡化 SDK 的實現。

事實上 Android 14 還針對 TextView 做了其他新功能的支援,比如設定文內搜尋結果的文字高亮、索引,後續一併進行解讀:

  • setSearchResultHighlightColor(int color):設定所有匹配到搜尋鍵碼的文字顏色
  • setSearchResultHighlights(int... ranges):設定所有匹配到搜尋鍵碼的文字高亮 HighLights 的範圍
  • setFocusedSearchResultHighlightColor(int color):設定當前聚焦到的匹配關鍵字的文字顏色
  • setFocusedSearchResultIndex(int index):設定當前聚焦到的匹配關鍵字的索引

參考

以上就是Android 14新功能HighLights快速實現文字高亮的詳細內容,更多關於Android HighLights文字高亮的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com