我正在 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()
}
}
)
但是,对齐问题仍然存在。
我设法通过使用 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,
)
}
}