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

日常开发中可能会遇到给 TextView 的全部或部分文本增加高亮效果的需求,以前可能是通过 Spannable 或者 Html 标签实现。

升级 Android 14 后就不用这么迂回了,因其首次引入直接设置高亮的 API:HighLights。需要留意的是 HighLights API 和 Android 1.0 即加入的 textColorHighlight API 不同:

  1. 14 的新 API 就是文本在 normal 状态下的高亮,之前这个是为了设置选中时文本高亮,
  2. 14 的新 API 只提供了 get/set 方法,没有提供与之匹配的 attribute。而之前的 API还提供了 android:textColorHighlight attribute 配置

下面我们就来一探这个新 API 的玩法和 textColorHighlight API 的区别。

目录前瞻:

  1. 设置高亮
  2. 获取高亮
  3. 动态更新高亮
  4. 与选中时效果是否冲突
  5. 结语

1. 设置高亮

HighLights 采用的是熟知的建造者模式,即首先需要构建不同参数的 Builder 实例,针对参数也提供了两种设置方式:

  1. 一次指定单组高亮配置:addRange(Paint paint, int start, int end),如果多组需要设置同样高亮颜色的话,那要调用多次

  2. 一次指定多组高亮配置: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 设置黄色高亮,HighlightsBuilder 设置绿色高亮。

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,那么更新其属性,能否动态更新高亮效果呢?

  1. 首先在 TextView 下添加动态更新 HighLights 的 Button

  2. 然后点击该 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
  • TextView’s setHighLights()
  • Spot on: Android 14 adds highlights to TextViews

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/3524.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

香橙派5使用NPU加速yolov5的实时视频推理(二)

三、将best.onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20.04系统中了&#xff0c;我的Ubuntu系统中已经下载好了anaconda&#xff0c;使用anaconda的好处就是可以方便的安装一些库&#xff0c;而且还可以利用conda来配置虚拟环境&#xff0c;做到环境与环境之间相互独立。…

STM32开发基础知识入门

C语言基础 位操作 对基本类型变量可以在位级别进行操作。 1) 不改变其他位的值的状况下&#xff0c;对某几个位进行设值。 先对需要设置的位用&操作符进行清零操作&#xff0c;然后用|操作符设值。 2) 移位操作提高代码的可读性。 3) ~取反操作使用技巧 可用于对某…

【UML】软件需求说明书

目录&#x1f981; 故事的开端一. &#x1f981; 引言1.1编写目的1.2背景1.3定义1.4参考资料二. &#x1f981; 任务概述2.1目标2.2用户的特点2.3假定和约束三. &#x1f981; 需求规定3.1 功能性需求3.1.1系统用例图3.1.2用户登录用例3.1.3学员注册用例3.1.4 学员修改个人信息…

基于 PyTorch + LSTM 进行时间序列预测(附完整源码)

时间序列数据&#xff0c;顾名思义是一种随时间变化的数据类型。 例如&#xff0c;24小时内的温度、一个月内各种产品的价格、某家公司一年内的股票价格等。深度学习模型如长短期记忆网络&#xff08;LSTM&#xff09;能够捕捉时间序列数据中的模式&#xff0c;因此可以用于预…

【C/C++】程序的内存开辟

在C/C语言中&#xff0c;不同的类型开辟的空间区域都是不一样的. 这节我们就简单了解下开辟不同的类型内存所存放的区域在哪里. 文章目录栈区&#xff08;stack&#xff09;堆区&#xff08;heap&#xff09;数据段&#xff08;静态区&#xff09;常量存储区内存开辟布局图栈区…

批量保存网页为单个网页文件

有时候&#xff0c;总有会遇到一些奇怪的需求&#xff0c;各种搜索都找不到答案&#xff0c;本次记录批量保存网页到单个网页文件。使用背景&#xff1a;只想简单的解决问题&#xff0c;不涉及编程网页带格式,将网页存为PDF格式会变量太大&#xff0c;一个个的处理太累涉及技术…

《毫无意义的工作》读书思考——互联网中,技术管理岗的价值或作用是什么?

目录 一、背景 二、书中咋说的 三、价值或作用可以从哪些方面思考&#xff1f; 四、写在最后 一、背景 日常工作中&#xff0c;自己经历的、身边人的吐槽&#xff0c;常常会有对纯技术管理者意义的怀疑。工作若干年&#xff0c;遇到各种各样的管理者&#xff0c;但让人吐槽…

【2023年第十一届泰迪杯数据挖掘挑战赛】B题:产品订单的数据分析与需求预测 建模及python代码详解 问题一

相关链接 【2023年第十一届泰迪杯数据挖掘挑战赛】B题&#xff1a;产品订单的数据分析与需求预测 建模及python代码详解 问题一 【2023年第十一届泰迪杯数据挖掘挑战赛】B题&#xff1a;产品订单的数据分析与需求预测 建模及python代码详解 问题二 1 题目 一&#xff0e;问题…

【Linux】进程理解与学习Ⅲ-环境变量

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅&#x1f339;相关文章推荐&#xff1a;【Linux】冯.诺依曼体系结构与操作系统【Linux】进程理解与学习Ⅰ-进程概念浅谈Linux下的shell--BASH【Linux】进程理解与学习…

学习系统编程No.10【文件描述符】

引言&#xff1a; 北京时间&#xff1a;2023/3/25&#xff0c;昨天摆烂一天&#xff0c;今天再次坐牢7小时&#xff0c;难受尽在不言中&#xff0c;并且对于笔试题&#xff0c;还是非常的困难&#xff0c;可能是我做题不够多&#xff0c;也可能是没有好好的总结之前做过的一些…

UE4/5 C++网络服务器编程纪录【零】--准备篇

前言之前利用业余时间重新复习UE4/5的C开发&#xff0c;闲来无事做了个基于独立服务器的多人在线&#xff08;目前限定客户数量是20人以内&#xff09;DEMO&#xff0c;核心功能在我之前发的B站视频里面有&#xff0c;战斗、动作、交互以及场景演示都有了&#xff0c;有朋友看了…

Spring容器实现原理-Spring的结构组成与核心类

Spring容器基本用法 bean是Spring中最核心的东西&#xff0c;因为Spring就像是个大水桶&#xff0c;而bean就像是容器中的水&#xff0c;水桶脱离了水便没有什么用处了&#xff0c;让我们先看看bean的定义&#xff1a; /*** ClassName MyTestBean* Author jiaxinxiao* Date 2…

2021全球开放数据应用创新大赛-法律咨询问答亚军方案

赛题分析 任务&#xff1a;给定用户问题&#xff0c;根据多个候选答案生成回复&#xff0c;属于文本生成任务。 问题信用逾期了&#xff0c;银行打电话骚扰我父母&#xff0c;改如何处理候选答案1. 按照约定还款 2.报警标准回复你好&#xff0c;这种情况只能按照约定还款&…

Python 练习 六

1、(最大数的出现)编写程序读取整数,找出它们中的最大值&#xff0c;然后计算它的出现次数。假设输入以数字0结束。假设你输入的是“352555 0";程序找出的最大数是5&#xff0c;而5的出现次数是4。(提示:维护两个变量max和 count。变量max存储的是当前最大数&#xff0c;而…

CentOS 7安装redis6.2.6(包括服务开机自启和开放端口)

CentOS 7安装redis6.2.61. 官网下载redis文件2. 校验安装依赖2.1 安装系统默认版本gcc2.2 升级gcc版本3. 解压编译安装4. 修改配置redis.conf4.2 设置密码4.3 绑定ip&#xff08;可选&#xff09;5. 启动redis服务并测试5.2 测试安装是否成功5.3 redis开机自启配置6.开放防火墙…

QT表格控件实例(Table Widget 、Table View)

欢迎小伙伴的点评✨✨&#xff0c;相互学习&#x1f680;&#x1f680;&#x1f680; 博主&#x1f9d1;&#x1f9d1; 本着开源的精神交流Qt开发的经验、将持续更新续章&#xff0c;为社区贡献博主自身的开源精神&#x1f469;‍&#x1f680; 文章目录前言一、图示实例二、列…

art 虚拟机相关

​​​​​​​虚拟机中对象锁实现分析 Android 库加载命名空间 https://source.android.google.cn/docs/core/architecture/vndk/linker-namespace Android共享库命名空间-CSDN博客 编译 prebuilts/clang/host/linux-x86/clang-r450784d/bin/llvm-addr2line -f -C -e…

项目文章 | 缓解高胆固醇血症 ,浒苔多糖如何相助?

文章标题&#xff1a;Polysaccharides from Enteromorpha prolifera alleviate hypercholesterolemia via modulating the gut microbiota and bile acid metabolism 发表期刊&#xff1a;Food & Function 影响因子&#xff1a;6.317 作者单位&#xff1a;福建医科大…

关于Docker逃逸

关于Docker逃逸 文章目录关于Docker逃逸前言一、判断是否为docker容器&#xff1f;二、privileged特权模式启动容器逃逸三、 Docker Remote API未授权访问逃逸四、危险挂载导致Docker逃逸五、危险挂载Docker Socket逃逸六、 挂载宿主机procfs逃逸七、脏牛漏洞来进行docker逃逸八…

蓝桥杯C/C++VIP试题每日一练之矩形面积交

💛作者主页:静Yu 🧡简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者 💛社区地址:前端知识交流社区 🧡博主的个人博客:静Yu的个人博客 🧡博主的个人笔记本:前端面试题 个人笔记本只记录前端领域的面试题目,项目总结,面试技…