”本文正在参加「金石计划」”
日常开发中可能会遇到给 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()
反映。
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 尚处于预览版阶段、源码没有公开,无法获悉完成。但估量是 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