You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android Jetpack Compose中检测TextField光标所在行

检测Android Jetpack Compose中TextField的光标所在行

要检测Jetpack Compose里TextField的光标所在行,核心是结合文本内容、光标偏移量和文本布局信息计算,以下是具体实现方案:

核心思路

通过Compose文本布局核心类Paragraph分析文本的行分布,结合光标在文本中的偏移位置,匹配对应的行索引。需要保证文本布局参数(字体大小、行高、宽度)和TextField实际样式完全一致,确保计算准确性。

实现代码(Compose 1.6+)

@Composable
fun CursorRowDetector() {
    val textState = rememberTextFieldState()
    var cursorRow by remember { mutableStateOf(1) }
    val textStyle = TextStyle.Default.copy(
        fontSize = 16.sp,
        lineHeight = 24.sp
    )

    TextField(
        value = textState.text,
        onValueChange = { textState.edit { replace(0, text.length, it) } },
        textStyle = textStyle,
        modifier = Modifier
            .onGloballyPositioned { layoutCoordinates ->
                val text = textState.text.text
                val cursorOffset = textState.selection.start

                if (text.isNotEmpty() && cursorOffset <= text.length) {
                    // 计算TextField可用宽度(扣除内边距)
                    val padding = layoutCoordinates.paddingValues
                    val availableWidth = layoutCoordinates.size.width - 
                        padding.start.toPx() - padding.end.toPx()

                    // 创建Paragraph分析文本布局
                    val paragraph = Paragraph(
                        text = text,
                        style = textStyle,
                        width = availableWidth
                    )

                    // 获取光标所在行索引(+1是因为行号从1开始计数)
                    cursorRow = paragraph.getLineForOffset(cursorOffset) + 1
                } else {
                    cursorRow = 1
                }
            },
        label = { Text("输入文本") },
        maxLines = Int.MAX_VALUE
    )

    // 显示光标所在行
    Text(
        text = "光标所在行:$cursorRow",
        modifier = Modifier.padding(top = 16.dp)
    )
}

适配低版本Compose(<1.6)

如果使用低于1.6的Compose版本,用TextFieldValue替代TextFieldState即可:

@Composable
fun LegacyCursorRowDetector() {
    val textState = remember { mutableStateOf(TextFieldValue()) }
    var cursorRow by remember { mutableStateOf(1) }
    val textStyle = TextStyle.Default.copy(
        fontSize = 16.sp,
        lineHeight = 24.sp
    )

    TextField(
        value = textState.value,
        onValueChange = { textState.value = it },
        textStyle = textStyle,
        modifier = Modifier
            .onGloballyPositioned { layoutCoordinates ->
                val text = textState.value.text
                val cursorOffset = textState.value.selection.start

                if (text.isNotEmpty() && cursorOffset <= text.length) {
                    val padding = layoutCoordinates.paddingValues
                    val availableWidth = layoutCoordinates.size.width - 
                        padding.start.toPx() - padding.end.toPx()

                    val paragraph = Paragraph(
                        text = text,
                        style = textStyle,
                        width = availableWidth
                    )

                    cursorRow = paragraph.getLineForOffset(cursorOffset) + 1
                } else {
                    cursorRow = 1
                }
            },
        label = { Text("输入文本") },
        maxLines = Int.MAX_VALUE
    )

    Text(
        text = "光标所在行:$cursorRow",
        modifier = Modifier.padding(top = 16.dp)
    )
}

关键注意事项

  • 必须保证Paragraph的style参数和TextField的textStyle完全一致,包括字体、字号、行高,否则行计算会出现偏差。
  • 若TextField设置了自定义内边距,一定要在计算可用宽度时扣除,避免布局宽度不匹配导致行计算错误。
  • 空文本状态下,光标默认处于第1行。

内容的提问来源于stack exchange,提问作者Tippu Fisal Sheriff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:50:25