? ? ? ? ?這一節主要了解一下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緩存,為點擊索引計算提供數據支持