如何检测在Android Jetpack Compose中TextField光标位于哪一行。

huangapple go评论73阅读模式
英文:

How to detect TextField cursor lies on which line in Android Jetpack compose

问题

How to detect TextField cursor lies on which line in Android Jetpack Compose.

英文:

How to detect TextField cursor lies on which line in Android Jetpack compose.

如何检测在Android Jetpack Compose中TextField光标位于哪一行。

答案1

得分: 4

To have access to selected text position, you need TextFieldValue.

如果要访问所选文本的位置,您需要使用 TextFieldValue

If you have short lines and you are sure that they will not break in any way other than \n symbol, you can manually calculate the current line.

如果您的文本行较短,并且您确信它们除了 \n 符号之外不会以任何方式中断,您可以手动计算当前行。

For a more general solution, which does not depend on the format of your data, you can use the TextLayoutResult value to get this information.

对于一种更通用的解决方案,不依赖于数据格式,您可以使用 TextLayoutResult 值来获取这些信息。

onTextLayout is only available with BasicTextField, but using a decorationBox you can easily turn it into a TextField/OutlinedTextField.

onTextLayout 仅在 BasicTextField 中可用,但使用 decorationBox,您可以轻松将其转换为 TextFieldOutlinedTextField

var textFieldValue by remember {
    mutableStateOf(TextFieldValue(LoremIpsum().values.first()))
}
var textLayoutResult by remember {
    mutableStateOf<TextLayoutResult?>(null)
}
val cursorLine by remember {
    derivedStateOf {
        textLayoutResult?.getLineForOffset(textFieldValue.selection.start)
    }
}
val interactionSource = remember { MutableInteractionSource() }
Column {
    Text("Line: ${cursorLine.toString()}")
    BasicTextField(
        value = textFieldValue,
        onValueChange = { textFieldValue = it },
        interactionSource = interactionSource,
        onTextLayout = {
            textLayoutResult = it
        },
        decorationBox = { innerTextField ->
            TextFieldDefaults.TextFieldDecorationBox(
                value = textFieldValue.text,
                innerTextField = innerTextField,
                enabled = true,
                singleLine = false,
                visualTransformation = VisualTransformation.None,
                interactionSource = interactionSource,
            )
        }
    )
}

<img src="https://i.stack.imgur.com/xAtnP.gif" width="300"></img>

英文:

To have access to selected text position, you need TextFieldValue.

If you have short lines and you are sure that they will not break in any way other than \n symbol, you can manually calculate the current line.

For a more general solution, which does not depend on the format of your data, you can use the TextLayoutResult value to get this information.

onTextLayout is only available with BasicTextField, but using a decorationBox you can easily turn it into a TextField/OutlinedTextField.

var textFieldValue by remember {
    mutableStateOf(TextFieldValue(LoremIpsum().values.first()))
}
var textLayoutResult by remember {
    mutableStateOf&lt;TextLayoutResult?&gt;(null)
}
val cursorLine by remember {
    derivedStateOf {
        textLayoutResult?.getLineForOffset(textFieldValue.selection.start)

    }
}
val interactionSource = remember { MutableInteractionSource() }
Column {
    Text(&quot;Line: ${cursorLine.toString()}&quot;)
    BasicTextField(
        value = textFieldValue,
        onValueChange = { textFieldValue = it },
        interactionSource = interactionSource,
        onTextLayout = {
            textLayoutResult = it
        },
        decorationBox = { innerTextField -&gt;
            TextFieldDefaults.TextFieldDecorationBox(
                value = textFieldValue.text,
                innerTextField = innerTextField,
                enabled = true,
                singleLine = false,
                visualTransformation = VisualTransformation.None,
                interactionSource = interactionSource,
            )
        }
    )
}

<img src="https://i.stack.imgur.com/xAtnP.gif" width="300"></img>

答案2

得分: 2

以下是代码部分的翻译:

fun getLine(textFieldValue: TextFieldValue): Int {
    val text = textFieldValue.text
    val selection = textFieldValue.selection.start
    val lineList = mutableListOf<Int>()
    text.forEachIndexed { index: Int, c: Char ->
        if (c == '\n') {
            lineList.add(index)
        }
    }

    if (lineList.isEmpty()) return 1

    lineList.forEachIndexed { index, lineEndIndex ->
        if (selection <= lineEndIndex){
            return index + 1
        }
    }

    return  lineList.size + 1
}

fun getLineAndRowOfSelection(textFieldValue: TextFieldValue): Pair<Int, Int> {
    val text = textFieldValue.text

    val selection = textFieldValue.selection.start
    val lineMap = linkedMapOf<Int, Int>()
    var lineCount = 1

    text.forEachIndexed { index: Int, c: Char ->
        if (c == '\n') {
            lineMap[index] = lineCount
            lineCount++
        }
    }

    if (lineMap.isEmpty()) {
        return Pair(1, selection)
    } else if (lineMap.keys.last() < selection) {
        // Selection is in a row after last new line char
        val key = lineMap.keys.last()
        val lastLine = lineMap[key] ?: 0
        return Pair(lastLine + 1, selection - key - 1)
    } else {
        // Selection is before last new line char
        var previousLineIndex = -1
        lineMap.forEach { (lineEndIndex, line) ->
            if (selection <= lineEndIndex) {
                // First line
                return if (previousLineIndex == -1) {
                    Pair(line, selection)
                } else {
                    Pair(line, selection - previousLineIndex - 1)
                }
            }

            previousLineIndex = lineEndIndex
        }
    }

    return Pair(-1, -1)
}

请注意,这只是代码部分的翻译,不包括注释和示例部分。如果您需要任何其他信息,请告诉我。

英文:

You can get indexes of new line characters '\n' or others and then loop through each of them to get line count.

Result

如何检测在Android Jetpack Compose中TextField光标位于哪一行。

fun getLine(textFieldValue: TextFieldValue): Int {
    val text = textFieldValue.text
    val selection = textFieldValue.selection.start
    val lineList = mutableListOf&lt; Int&gt;()
    text.forEachIndexed { index: Int, c: Char -&gt;
        if (c == &#39;\n&#39;) {
            lineList.add(index)
        }
    }

    if (lineList.isEmpty()) return 1

    lineList.forEachIndexed { index, lineEndIndex -&gt;
        if (selection &lt;= lineEndIndex){
            return index + 1
        }
    }

    return  lineList.size + 1
}

If you wish to get not only line but row too you can do it by using a map

fun getLineAndRowOfSelection(textFieldValue: TextFieldValue): Pair&lt;Int, Int&gt; {
    val text = textFieldValue.text

    val selection = textFieldValue.selection.start
    val lineMap = linkedMapOf&lt;Int, Int&gt;()
    var lineCount = 1

    text.forEachIndexed { index: Int, c: Char -&gt;
        if (c == &#39;\n&#39;) {
            lineMap[index] = lineCount
            lineCount++
        }
    }

    if (lineMap.isEmpty()) {
        return Pair(1, selection)
    } else if (lineMap.keys.last() &lt; selection) {
        // Selection is in a row after last new line char
        val key = lineMap.keys.last()
        val lastLine = lineMap[key] ?: 0
        return Pair(lastLine + 1, selection - key - 1)
    } else {
        // Selection is before last new line char
        var previousLineIndex = -1
        lineMap.forEach { (lineEndIndex, line) -&gt;
            if (selection &lt;= lineEndIndex) {
                // First line
                return if (previousLineIndex == -1) {
                    Pair(line, selection)
                } else {
                    Pair(line, selection - previousLineIndex - 1)
                }
            }

            previousLineIndex = lineEndIndex
        }
    }

    return Pair(-1, -1)
}

Demo

@Preview
@Composable
private fun TextSelectionTest() {
    Column(
        modifier = Modifier.padding(top = 30.dp)
    ) {
        var textFieldValue by remember {
            mutableStateOf(TextFieldValue())
        }

        val lineAndRow = getLineAndRowOfSelection(textFieldValue)
        val line = getLine(textFieldValue)

        Text(
            &quot;Text ${textFieldValue.text}, &quot; +
                    &quot;selection: ${textFieldValue.selection}\n&quot; +
                    &quot;line and row: $lineAndRow\n&quot; +
                    &quot;line: $line&quot;
        )

        TextField(value = textFieldValue, onValueChange = { textFieldValue = it })
    }
}

huangapple
  • 本文由 发表于 2023年5月10日 15:23:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76215882.html
  • android
  • android-compose-textfield
  • android-jetpack
  • android-jetpack-compose
  • jetpack
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定