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()
反映。
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 = TEXTval builder = Highlights.Builder().addRange(yellowPaint, 0, 3).addRange(greenPaint, 14, 24).addRange(greenPaint, 25, 32)highlights = builder.build()}with(binding.textview2) {text = TEXTval 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:3203-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 colorgetPaint(1).color = Color.BLUE// Change rangesgetRanges(1)[0] -= 3getRanges(1)[1] += 1for (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 ... ><TextViewandroid: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
相关文章:

Android 14 新功能之 HighLights:快速实现文本高亮~
日常开发中可能会遇到给 TextView 的全部或部分文本增加高亮效果的需求,以前可能是通过 Spannable 或者 Html 标签实现。 升级 Android 14 后就不用这么迂回了,因其首次引入直接设置高亮的 API:HighLights。需要留意的是 HighLights API 和 …...

[渗透教程]-004-嗅探工具-Nmap
文章目录 Nmap介绍基本操作进阶操作Nmap介绍 nmap是一个网络扫描和主机检测工具,它可以帮助用户识别网络上的设备和服务。获取主机正在运行哪些服务,nmap支持多种扫描,UDP,TCP connect(),TCP SYN(半开扫描) ftp代理,反向标志,ICMP,FIN,ACK扫描,ftp代理,反向标志,ICMP. 可以用于…...

大数据技术之Hive SQL题库-初级
第一章环境准备1.1 建表语句hive>-- 创建学生表 DROP TABLE IF EXISTS student; create table if not exists student_info(stu_id string COMMENT 学生id,stu_name string COMMENT 学生姓名,birthday string COMMENT 出生日期,sex string COMMENT 性别 ) row format delim…...
常见HTTP状态码汇总
文章目录1xx: 信息2xx: 成功3xx: 重定向4xx: 客户端错误5xx: 服务器错误1xx: 信息 状态码描述100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。101 Switching Protocols服务器转换协议:服…...

蓝桥杯刷题冲刺 | 倒计时15天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.年号字串2.裁纸刀3.猜生日1.年号字串 题目 链接: 年号字串 - 蓝桥云课 (lanqiao.c…...

【差分数组】
差分数组一维差分差分数组的作用差分矩阵结语一维差分 输入一个长度为 n 的整数序列。接下来输入 m个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c ,请你输出进行完所有操作后的序列。 输入格式 第一行包含两个…...

2022年NOC软件创意编程(学而思)决赛小学高年级组scratch
2022NOC决赛图形化小高组 一、选择题 1.运行下面的程序,最终“我的变量”的值是多少? 2.希望定义一个函数如下,可以让角色旋转指定的圈数。里面空缺的地方填上什么数字比较合适? 3.运行程序,在舞台上可以看见几个角色 ? 4.运行程序,角色会依次说什么 ? 5.我们都知…...

[JAVA]一步接一步的一起开发-图书管理系统(非常仔细,你一定能看懂)[1W字+]
目录 1.想法 2.框架的搭构 2.1图书 2.1.1Book类 2.1.2BookList类 2.2用户 2.2.1User抽象类 2.2.2AdminUser类(管理者) 2.2.3NormalUser 2.3操作 操作接口 借阅操作 删除操作 查询操作 归还图书 展示图书 退出系统 2.4小结 3.主函数的编…...

大数据周会-本周学习内容总结07
目录 01【hadoop】 1.1【编写集群分发脚本xsync】 1.2【集群部署规划】 1.3【Hadoop集群启停脚本】 02【HDFS】 2.1【HDFS的API操作】 03【MapReduce】 3.1【P077- WordCount案例】 3.2【P097-自定义分区案例】 历史总结 01【hadoop】 1.1【编写集群分发脚本xsync】…...

搭建一个双系统个人服务器
搭建一个双系统个人服务器0.前言一、双系统安装1.磁盘划分2.windows安装3.ubuntu安装二、系统启动项美化:1. refind引导2. 美化 grub 界面三、系统代理0.前言 年后找了份工作,忙于适应新环境所以更新也减缓了,最近闲暇时间给个人电脑进行了整…...

电脑长按电源键强行关机,对SSD有伤害吗?SSD 掉盘之殇
说到“按住电源键强制关机”的操作,想必大家都不会陌生,毕竟在电脑蓝屏或者电脑死机的时候,我们总是束手无策。而且,身边的人在遇到同样的情况时,往往都是选择长按电源键强制关机,所以当我们遇到同样的情况…...
Linux:centos内核优化详解
一、系统内核部分设置在以下文件 vim /etc/sysctl.conf 1.禁用IPV6 net.ipv6.conf.all.disable_ipv6 1 # 禁用整个系统所有接口的IPv6 net.ipv6.conf.default.disable_ipv6 1 net.ipv6.conf.lo.disable_ipv6 1 # 禁用某一个指定接口的IPv6(此处为:lo) 理想情况下,…...

链表经典OJ题合集(包含带环问题,相交问题,随机指针复制等,附动画讲解)
目录 一:前言 二:简单题目 (1)移除链表元素 (2)反转链表 (3)找链表的中间结点 (4)输入一个链表,输出该链表中倒数第k个结点 (5)合并两个有序链表 (6)相交链表 (7)判断链表是否带环 三:较难题目 (1)链表分割 (2)判断链表是否为回…...
CSS新增
系列文章目录 前端系列文章——传送门 CSS系列文章——传送门 文章目录系列文章目录什么是 CSS3渐进增强和优雅降级CSS3 中的选择器CSS3 中的背景CSS3 中的边框CSS3 中的文本效果CSS3 中的字体 font-face什么是 CSS3 CSS3是CSS(层叠样式表)技术的升级版…...

奇安信_防火墙部署_透明桥模式
奇安信_防火墙部署_透明桥模式一、预备知识二、项目场景三、拓扑图四、基本部署配置1. 登录web控制台2.连通性配置3.可信主机配置4.授权导入5.特征库升级6.安全配置文件五、透明桥配置1. 创建桥2. 端口绑定桥3. 创建桥端口六、结语一、预备知识 安全设备接入网络部署方式 二、…...

C语言——字符串函数(2)和内存函数
(一)strtok函数dilimiters参数是个字符串,定义了用作分隔符的字符集合第一个参数指定一个字符串,它包含了0个或者多个由dilimiters字符串中一个或者多个分隔符分割的标记。strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回…...
第1节 线性回归模型
1. 模型概述 对于收集到的数据(xi,yi)(x_i,y_i)(xi,yi),建立线性回归模型yiθTxiεi(1)y_i\theta^{^T} x_i \varepsilon_i (1)yiθTxiεi(1) 需要估计的参数为θT\theta^{^T}θT,我们的目的是让估计的参数θT\theta^{^T}θT和xix_ixi…...

CodeGeeX 130亿参数大模型的调优笔记:比FasterTransformer更快的解决方案
0x0 背景 相信大家都使用或者听说过github copilot这个高效的代码生成工具。CodeGeeX类似于github copilot,是由清华大学,北京智源研究院,智谱AI等机构共同开发的一个拥有130亿参数的多编程语言代码生成预训练模型。它在vscode上也提供了插件…...
Linux驱动之并发与竞争
文章目录并发与竞争的概念原子操作原子整形操作 API 函数原子位操作 API 函数自旋锁自旋锁简介自旋锁结构体自旋锁 API 函数自旋锁的注意事项读写自旋锁读写自旋锁的API顺序锁顺序锁的APIRCU(Read-Copy-Update)RCU的API信号量信号量API互斥体互斥体的API完成量(Completion)完成…...

【密码学复习】第四讲分组密码(三)
AES算法的整体结构 AES算法的轮函数 1)字节代换(SubByte) 2)行移位(ShiftRow) 3)列混合(MixColumn) 4)密钥加(AddRoundKey)1-字节代换…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...

倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...