Android 14新功能HighLights快速实现文本高亮

正文

日常开发中可能会遇到给 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 构建一样的高亮效果,然后通过 TextView 的 setHighLights() 反映。

  1.      class MainActivity : AppCompatActivity() {
  2.          companion object {
  3.              const val TEXT = “val builder = Highlights.Builder()”
  4.          }
  5.          override fun onCreate(savedInstanceState: Bundle?) {
  6.              
  7.              val yellowpaint = Paint().apply {
  8.                  color = Color.YELLOW
  9.              }
  10.              val greenPaint = Paint().apply {
  11.                  color = Color.GREEN
  12.              }
  13.              with(binding.textview1) {
  14.                  text = TEXT
  15.                  val builder = Highlights.Builder()
  16.                      .addRange(yellowPaint, 0, 3)
  17.                      .addRange(greenPaint, 14, 24)
  18.                      .addRange(greenPaint, 25, 32)
  19.                  highlights = builder.build()
  20.              }
  21.              with(binding.textview2) {
  22.                  text = TEXT
  23.                  val builder = Highlights.Builder()
  24.                      .addRanges(yellowPaint, 0, 3)
  25.                      .addRanges(greenPaint, 14, 24, 25, 32)
  26.                  highlights = builder.build()
  27.              }
  28.          }
  29.      }

可以看到不同的 Builder 参数设置方式可以对 val 设置黄色高亮,Highlights 和 Builder 设置绿色高亮。

-1

2. 获取高亮

设置到 TextView 对象的 HighLights 实例还可以通过 getHighlights() 获取,并通过如下的 API 获取高亮的细节:

首先通过 getSize() 获取设置高亮的数量

其次从 0 开始遍历下标

  • 通过 getPaint(int index) 获取高亮的 Paint 对象
  • 以及通过 getRanges(int index) 获取对应的 Paint 范围 Ranges(也是一个数组,需要遍历打印具体的起始位置)
  1.      class MainActivity : AppCompatActivity() {
  2.          
  3.          override fun onCreate(savedInstanceState: Bundle?) {
  4.              
  5.              binding.textview1.highlights?.run {
  6.                  Log.d(“HighLights”, “textview1 usedHighLights’ size:$size”)
  7.                  for (in 0 until size) {
  8.                      Log.d(“HighLights”, “usedHighLights'” +
  9.                      ” paint:${getPaint(i).color.toColorString()}”)
  10.                      val range = getRanges(i)
  11.                      for (in range.indices) {
  12.                      Log.d(“HighLights”, “ranges:${range[j]}”)
  13.                      }
  14.                  }
  15.              }
  16.              binding.textview2.highlights?.run {
  17.                  Log.d(“HighLights”, “textview2 usedHighLights’ size:$size”)
  18.                  for (in 0 until size) {
  19.                      Log.d(“HighLights”, “usedHighLights'” +
  20.                      ” paint:${getPaint(i).color.toColorString()}”)
  21.                      val range = getRanges(i)
  22.                      for (in range.indices) {
  23.                      Log.d(“HighLights”, “ranges:${range[j]}”)
  24.                      }
  25.                  }
  26.              }
  27.          }
  28.      }

如下的 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

-2

  • 然后点击该 Button 之后将 textView1 的 Paint 颜色从 GREEN 改为 BLUE,并将其中 “Highlights” 的文本范围增大:头尾各扩展一个或多个下标
  1.      class MainActivity : AppCompatActivity() {
  2.          
  3.          override fun onCreate(savedInstanceState: Bundle?) {
  4.              
  5.              binding.changeHighlights.setOnClickListener {
  6.                  Log.d(“HighLights”, “changeHighlights tapped & change highlights”)
  7.                  textView1Highlights?.apply {
  8.                      // Change color
  9.                      getPaint(1).color = Color.BLUE
  10.                      // Change ranges
  11.                      getRanges(1)[0] -= 3
  12.                      getRanges(1)[1] += 1
  13.                      for (in 0 until size) {
  14.                      Log.d(“HighLights”, “textView1Highlights'” +
  15.                      ” paint:${getPaint(i).color.toColorString()}”)
  16.                      val range = getRanges(i)
  17.                      for (in range.indices) {
  18.                      Log.d(“HighLights”, “ranges:${range[j]}”)
  19.                      }
  20.                      }
  21.                  }
  22.                  binding.textview1.invalidate()
  23.              }
  24.          }
  25.      }

点击 Button 之后,颜色确实变成了蓝色,但是高亮范围却没有变化。

-3

我们打印的更新后 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 才可以被长按选中。

  1.      <androidx.constraintlayout.widget.ConstraintLayout … >
  2.          <TextView
  3.              android:id=“@+id/textview1”
  4.              …
  5.              android:textColorHighlight=“@color/purple_200”
  6.              android:textIsSelectable=“true”
  7.              … />
  8.          < … >
  9.      </androidx.constraintlayout.widget.ConstraintLayout>

我们在该 TextView 上长按看一下效果:

-4

可以看到水滴选中的范围内会变成我们设置的 textColorHighlight 紫色高亮,未选中的部分会按照 HighLights 配置的那样展示黄色和绿色以及没有设置 HighLights 的默认浅灰色。

5. 结语

可以看到新功能 HighLights 可以使得高亮的处理变得简单、易用,大家可以在 14 上采用该 API,当高版本普及后,低版本上的自定义高亮逻辑就可以舍弃了。

至于其原理,因为 Android 14 尚处于预览版阶段、源码没有公开,无法获悉实现。但估计是 TextView 在 draw 阶段会获取设置的 HighLights 包含的 size 以及对应的 Paint 和 Ranges,得以清晰地掌握各高亮的颜色和对应的范围,然后直接调用 Canvas 的 drawText(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
  • TextView’s setHighLights()
  • Spot on: Android 14 adds highlights to TextViews

以上就是Android 14新功能HighLights快速实现文本高亮的详细内容,更多关于Android HighLights文本高亮的资料请关注我们其它相关文章!

标签

发表评论