这一节主要了解一下Compose中的ClickableText,在Jetpack Compose中,ClickableText是用于创建可点击文本的组件,其核心功能是通过声明式语法将文本设置为交互式元素,用户点击时可触发特定操作。简单总结如下:

API含义
text:要显示的文本内容(AnnotatedString类型,支持富文本样式)。
onClick:点击事件的回调函数,接收点击位置的偏移量作为参数,用于定位具体点击的字符。
style:文本样式(如颜色、字体大小),可通过TextStyle自定义。
modifier:可选修饰符,用于添加额外交互或布局属性。
富文本支持:通过AnnotatedString可为文本添加元数据(如链接、样式标签),结合ClickableText实现复杂交互。

场景
1 基础交互
创建可点击的按钮或链接(如“登录”“注册”)。
实现文本展开/折叠功能(点击“显示更多”展开隐藏内容)。
2 富文本交互
在文章中嵌入可点击的关键词或参考文献链接。
开发聊天应用时,将用户名或话题标签设置为可点击(如@用户或#话题)。
3 导航与跳转
结合路由库(如Compose Navigation)实现页面跳转。

栗子:

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp@Composable
fun TestLinkTextExample(modifier: Modifier = Modifier) {val uriHandler = LocalUriHandler.currentval annotatedText = buildAnnotatedString {append("请阅读我们的 ")val termsStart = lengthappend("用户协议")val termsEnd = lengthaddStringAnnotation(tag = "URL", annotation = "https://example.com/terms", // 换成具体业务链接start = termsStart,end = termsEnd)addStyle(style = SpanStyle(color = Color(0xFF2196F3),textDecoration = TextDecoration.Underline),start = termsStart,end = termsEnd)append(" 和 ")val privacyStart = lengthappend("隐私政策")val privacyEnd = lengthaddStringAnnotation(tag = "URL",annotation = "https://examplexxx.com/privacy",//换成具体业务链接start = privacyStart,end = privacyEnd)addStyle(style = SpanStyle(color = Color(0xFF2196F3),textDecoration = TextDecoration.Underline),start = privacyStart,end = privacyEnd)}ClickableText(text = annotatedText,style = TextStyle(fontSize = 16.sp,color = Color.Black),modifier = modifier.padding(16.dp),onClick = { offset ->annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset).firstOrNull()?.let { annotation ->uriHandler.openUri(annotation.item)}})
}
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp@Composable
fun TestTextExample(modifier: Modifier = Modifier) {var showDialog by remember { mutableStateOf(false) }var dialogMessage by remember { mutableStateOf("") }// 构建带@提及和#话题的文本val annotatedText = buildAnnotatedString {val userStart = lengthappend("@JetpackCompose")val userEnd = lengthaddStringAnnotation(tag = "MENTION",annotation = "JetpackCompose", start = userStart,end = userEnd)addStyle(style = SpanStyle(color = Color(0xFF9C27B0)), start = userStart,end = userEnd)append(" 发布了关于 ")val topicStart = lengthappend("#Compose开发")val topicEnd = lengthaddStringAnnotation(tag = "TOPIC", annotation = "Compose开发", start = topicStart,end = topicEnd)addStyle(style = SpanStyle(color = Color(0xFFF44336)), start = topicStart,end = topicEnd)append(" 的新内容,快去看看吧!")}fun handleClick(offset: Int) {annotatedText.getStringAnnotations(tag = "MENTION", start = offset, end = offset).firstOrNull()?.let {dialogMessage = "查看用户: ${it.item}"showDialog = truereturn}annotatedText.getStringAnnotations(tag = "TOPIC", start = offset, end = offset).firstOrNull()?.let {dialogMessage = "查看话题: ${it.item}"showDialog = truereturn}}if (showDialog) {AlertDialog(onDismissRequest = { showDialog = false },title = { Text("操作提示") },text = { Text(dialogMessage) },confirmButton = {Text(text = "确定",modifier = Modifier.clickable { showDialog = false })})}ClickableText(text = annotatedText,style = TextStyle(fontSize = 16.sp,color = Color.Black),modifier = modifier.padding(16.dp),onClick = ::handleClick)
}

注意
1 点击区域定位:onClick回调返回的是字符偏移量,需通过AnnotatedString的元数据定位具体点击的文本片段。若文本包含多语言或复杂排版,需额外处理偏移量计算。
2 性能优化:避免在onClick中执行耗时操作,否则可能导致界面卡顿。
3 无障碍支持:为可点击文本添加semantic描述,提升无障碍体验。

简单看一下其源码:

@Composable
@Deprecated("Use Text or BasicText and pass an AnnotatedString that contains a LinkAnnotation")
fun ClickableText(text: AnnotatedString,modifier: Modifier = Modifier,style: TextStyle = TextStyle.Default,softWrap: Boolean = true,overflow: TextOverflow = TextOverflow.Clip,maxLines: Int = Int.MAX_VALUE,onTextLayout: (TextLayoutResult) -> Unit = {},onClick: (Int) -> Unit
) {// 1val layoutResult = remember { mutableStateOf<TextLayoutResult?>(null) }// 2 val pressIndicator = Modifier.pointerInput(onClick) {detectTapGestures { pos ->layoutResult.value?.let { layoutResult ->onClick(layoutResult.getOffsetForPosition(pos))}}}// 3BasicText(text = text,modifier = modifier.then(pressIndicator),style = style,softWrap = softWrap,overflow = overflow,maxLines = maxLines,onTextLayout = {layoutResult.value = itonTextLayout(it)})
}

分析:
1.状态定义:缓存文本布局结果
layoutResult用于mutableStateOf存储TextLayoutResult对象,用于记录文本的布局信息(如字符位置、行高、宽高等)。
remember 确保重组时不会重复初始化,仅在依赖变化时更新。
2.点击事件处理:pointerInput检测点击并计算索引
pointerInput:Compose中处理低级触摸事件的入口,传入onClick作为键(当onClick变化时,重新创建触摸事件处理器)。
detectTapGestures:手势检测器,监听点击事件,pos参数是点击位置相对于组件左上角的坐标(Offset类型,包含x和y偏移)。
索引计算:当点击发生时,通过layoutResult.getOffsetForPosition(pos)将点击坐标转换为字符索引(核心逻辑与新版一致),再通过onClick回调暴露该索引。
3.文本渲染:BasicText与布局结果同步
BasicText:Compose中最基础的文本渲染组件,负责将AnnotatedString渲染到屏幕上。
修饰符组合:通过modifier.then(pressIndicator)将点击事件修饰符(pressIndicator)与外部传入的modifier组合,确保点击事件能被正确监听。
布局结果同步:onTextLayout回调在文本布局完成后触发,将最新的TextLayoutResult存入layoutResult缓存,为点击索引计算提供数据支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94388.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94388.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/94388.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试必刷的数组三连:原地删除与合并

坚持用 清晰易懂的图解 多语言代码&#xff0c;让每道题变得简单&#xff01; 呆头个人主页详情 呆头个人Gitee代码仓库 呆头详细专栏系列 座右铭&#xff1a; “不患无位&#xff0c;患所以立。” 面试必刷的数组三连&#xff1a;原地删除与合并前言目录1.移除元素2.删除有序…

力扣经典算法篇-41-旋转图像(辅助数组法,原地旋转法)

1、题干 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a;输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]]…

译|用户增长策略如何使用因果机器学习的案例

来自上传文件中的文章《[Causal Machine Learning for Growth: Loyalty Programs, LTV, and What to Do When You Can’t Experiment | by Torty Sivill | Towards AI]》 本文探讨了当 A/B 测试不可行时&#xff0c;如何利用因果推断从历史数据中获取洞察。技术亮点在于通过构建…

java~final关键字

final关键字final基本介绍final的使用细节final基本介绍 final是最终的意思&#xff0c;可以修饰类&#xff0c;属性&#xff0c;方法&#xff0c;局部变量什么时候会要使用到final呢&#xff1f; 1.想要类不被继承时 2.不希望类的某个属性的值被改变时 3.不想父类的某个方法被…

Node.js(四)之数据库与身份认证

数据库与身份认证 目录 数据库与身份认证 十三、数据库的基本概念 13.1 什么是数据库 13.2 常见的数据库及分类 13.3 传统型数据库的数据组织结构 1. Excel 的数据组织结构 2. 传统型数据库的数据组织结构 3. 实际开发中库、表、行、字段的关系 十四、安装并配置MySQ…

SpringBoot+SpringMVC常用注解

文章目录发展历程项目创建项目结构入门案例配置文件的两种方式&#xff1a;只能使用一种创建项目二入门案例常用知识及注解Controller:类上面加&#xff0c;SpringMVC的注解GetMapping:方法上面加Spring框架的两项核心功能Component:组件。控制反转&#xff0c;加在业务类上面&…

标准GS相位恢复算法

标准GS相位恢复算法详解与MATLAB实现 Gerchberg-Saxton (GS) 算法是一种经典的相位恢复方法&#xff0c;广泛应用于光学成像、衍射成像和全息技术等领域。该算法通过迭代过程从未知相位的强度测量中恢复相位信息。 算法原理 GS算法的核心思想是利用傅里叶变换关系在空间域和频率…

【Linux网络编程基础--socket地址API】

一、主机字节序和网络字节序主机字节序&#xff08;Host Byte Order&#xff09;&#xff1a;你当前电脑的内存字节顺序&#xff08;比如 x86 是小端&#xff09;网络字节序&#xff08;Network Byte Order&#xff09;&#xff1a;统一规定为大端序&#xff08;高位字节在高位…

Linux路径MTU发现(Path MTU Discovery, PMTU)

Linux路径MTU发现&#xff08;Path MTU Discovery, PMTU&#xff09;机制是TCP/IP协议栈中确保数据包高效传输的核心技术。其核心目标是动态探测源主机到目的主机路径上的最小MTU&#xff08;Maximum Transmission Unit&#xff09;&#xff0c;从而避免IP分片&#xff0c;提升…

【MySQL进阶】------MySQL程序

MySQL程序简介 MySQL安装完成通常会包含如下程序&#xff1a; Linux系统程序⼀般在 /usr/bin⽬录下&#xff0c;可以通过命令查看&#xff1a; windows系统⽬录&#xff1a;你的安装路径\MySQL Server 8.0\bin&#xff0c;可以通过命令查看&#xff1a; 每个 MySQL 程序都有许…

Linux大页内存导致服务内存不足

Linux大页内存导致服务内存不足的解决方法 大页内存&#xff08;Huge Pages&#xff09;是Linux内核提供的一种机制&#xff0c;用于减少TLB&#xff08;转换后备缓冲区&#xff09;的压力&#xff0c;提高内存访问性能。然而&#xff0c;如果配置不当&#xff0c;大页内存可能…

超宽带测距+测角+无线通信一体化模组:智能门锁、智能遥控器、AR头戴、智能穿戴

超宽带测距测角无线通信一体化模组&#xff1a;智能门锁、智能遥控器、AR头戴、智能穿戴UWB测距测角技术&#xff0c;因其高精度、低延迟、抗干扰能力&#xff0c;正广泛应用于“人-物-设备”的空间感知场景&#xff0c;成为构建智能空间和精准互动的重要底层技术。代表厂商与产…

基于单片机空气质量检测/气体检测系统

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 随着环境污染问题日益严重&#xff0c;空气质量监测成为社会关注的焦点。基于单片机的空气质量检…

网络安全 | 从 0 到 1 了解 WAF:Web 应用防火墙到底是什么?

&#x1f914; 写在前面 2020年 我参加公司的安全技能大赛&#xff0c;队友在实操环节启用了 WAF 防火墙&#xff0c;这是我第一次接触到 Web 应用防火墙。作为一个 Web 开发老鸟&#xff0c;真是羞愧呀&#x1f602;。 &#x1f510; Web应用防火墙 WAF 全称是 Web Applica…

服务器突然之间特别卡,什么原因?

原因总结&#xff1a;1.一般是本地网速的问题&#xff0c;服务器网速的问题&#xff0c;服务器CPU被占满的问题今天发现另一个会导致特别卡的问题&#xff0c;是主存占满也会导致卡顿。解释如下&#xff1a;当服务器的主存&#xff08;物理内存&#xff09;被完全占满时&#x…

AI应用标准详解:A2A MCP AG-UI

"OpenAI接入MCP&#xff0c;Google推出A2A&#xff0c;微软与OpenAI紧密绑定"标志着云计算竞争焦点已从"算力"和"模型参数"转向‌Agent标准协议控制权‌。在AI快速演进的今天&#xff0c;我们不再仅关注单个AI的智能水平&#xff0c;而是探索多个…

Web安全学习步骤

以下是Web安全专项学习步骤&#xff0c;聚焦实战能力培养&#xff0c;分为4个阶段资源清单**&#xff0c;适合从入门到进阶。重点培养漏洞挖掘能力与防御方案设计双重视角&#xff1a;---阶段1&#xff1a;Web技术筑基&#xff08;1-2个月&#xff09; | 领域 | 关键…

Android工程命令行打包并自动生成签名Apk

1.进入工程目录查看所有gradle任务 2.打包debug与release 打包前先生成jks签名文件test.jks 在工程的build.gradle中添加签名配置 signingConfigs {release {storeFile file("/home/dev/test.jks")storePassword "111111"keyAlias "key0"keyPas…

分布式微服务--Nacos作为配置中心(一)

1.Nacos配置远程配置中心注意总结&#xff1a;本地配置文件必须使用 bootstrap.yml 或 bootstrap.properties远程配置的加载优先于 application.yml&#xff0c;因此必须写在 bootstrap 配置文件中。本地配置文件中 file-extension 的取值仅支持两种&#xff1a;properties 或 …

Linux安装MySQL及链接第三方工具详细教程,带图带错误分析

本教程所有代码均为root用户权限下操作&#xff0c;如果不是root用户&#xff0c;在代码前加上&#xff08;sudo &#xff09;即可 一、安装MySQL服务 准备工作&#xff1a; 有时&#xff0c;系统无法解析 部分域名&#xff0c;导致无法获取镜像列表&#xff0c;从而无法安装…