如何在 Jetpack Compose 中向 BasicTextField 添加行号?

问题描述 投票:0回答:1

我正在 Jetpack Compose 中创建一个代码编辑器应用程序。我使用的是充满整个屏幕的

BasicTextField
。现在,我想在文本字段旁边显示行号。但是,文本字段的行与行号不垂直对齐。

我看到了一些示例,例如 Compose Multi-platform 中的 Code Viewer 示例。但是,由于该示例使用静态文本而不是文本字段,因此当我使用

BasicTextField
时,线条不会对齐。

我也尝试使用此代码:

var lineTops by remember { mutableStateOf(emptyArray<Float>()) }
val density = LocalDensity.current

BasicTextField(
    modifier = Modifier.fillMaxSize(),
    value = text,
    onValueChange = { text = it },
    onTextLayout = { result ->
        lineTops = Array(result.lineCount) { result.getLineTop(it) }
    },
    decorationBox = { innerTextField ->
        Row {
            if (lineTops.isNotEmpty()) {
                Box(modifier = Modifier.padding(horizontal = 4.dp)) {
                    lineTops.forEachIndexed { index, top ->
                        Text(
                            modifier = Modifier.offset(y = with(density) { top.toDp() }),
                            text = index.toString()
                        )
                    }
                }
            }
            innerTextField()
        }
    }
)

但是,对齐问题仍然存在。

android android-jetpack-compose editor textfield
1个回答
0
投票

我设法通过使用 2 个 TextField 并同步两者之间的滚动状态来做到这一点。

@Composable
fun ScriptField(viewModel: ScriptCardViewModel, readOnly: Boolean, modifier: Modifier) {
  // using a mutable state to store the number of lines
  var linesText by remember { mutableIntStateOf(1) }

  // the scrolling state of both text fields
  val linesTextScroll = rememberScrollState()
  val scriptTextScroll = rememberScrollState()

  // synchronize scrolling
  LaunchedEffect(linesTextScroll.value) {
    scriptTextScroll.scrollTo(linesTextScroll.value)
  }
  LaunchedEffect(scriptTextScroll.value) {
    linesTextScroll.scrollTo(scriptTextScroll.value)
  }

  Row(modifier = modifier) {
    // line number text field (which is not editable)
    BasicTextField(
      modifier = Modifier
        .fillMaxHeight()
        .width(12.dp * linesText.toString().length)
        .verticalScroll(linesTextScroll),
      value = IntRange(1, linesText).joinToString(separator = "\n"),
      readOnly = true,
      onValueChange = {})

    VerticalDivider(
      modifier = Modifier.fillMaxHeight().padding(horizontal = 8.dp),
      color = Color.White
    )
    // actual textField
    BasicTextField(
      modifier = Modifier
        .fillMaxHeight()
        .weight(1f)
        // this is a hack to prevent this https://stackoverflow.com/questions/76287857/when-parent-of-textfield-is-clickable-hardware-enter-return-button-triggers-its
        .onKeyEvent { it.type == KeyEventType.KeyUp && it.key == Key.Enter }
        .verticalScroll(scriptTextScroll),
      value = viewModel.scriptTextInput,
      readOnly = readOnly,
      onValueChange = { textFieldValue ->
        val nbLines = textFieldValue.annotatedString.count { it == '\n' } + 1
        if (nbLines != linesText) linesText = nbLines
        viewModel.onScriptTextChange(textFieldValue)
      },
      visualTransformation = viewModel,
    )
  }
}

重要的部分是滚动状态和这些状态的同步,这要归功于 2 LaunchedEffect。 输出看起来像这样。 textfield with line numbers

您可以在这里找到完整的代码示例

© www.soinside.com 2019 - 2024. All rights reserved.