上一篇只做了写文本页的工具栏,对于下方的写作区还没有进行代码编写。
其实是在使用Textfield的过程中遇到了一些问题:
1、它的高度不能更改,好像是固定56dp,用起来很不好用
2、它的文本输入的地方不是贴边靠左,有一些padding值,这个在这个场景下很不好
为了解决这些问题就要自定义
这里主要将自定义的方法和思路
1、打开Textfield的定义文件
方法就是随便在哪个地方写个Textfield函数,然后ctrl+单击左键跳转到代码定义页
我们直接将这段代码完全复制到新建的文件中
@Composable
fun TextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.filledShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
) {
// If color is not provided via the text style, use content color as a default
val textColor = textStyle.color.takeOrElse {
colors.textColor(enabled).value
}
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
CompositionLocalProvider(LocalTextSelectionColors provides colors.selectionColors) {
@OptIn(ExperimentalMaterial3Api::class)
BasicTextField(
value = value,
modifier = modifier
.defaultMinSize(
minWidth = TextFieldDefaults.MinWidth,
minHeight = TextFieldDefaults.MinHeight
),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = mergedTextStyle,
cursorBrush = SolidColor(colors.cursorColor(isError).value),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
decorationBox = @Composable { innerTextField ->
// places leading icon, text field with label and placeholder, trailing icon
TextFieldDefaults.TextFieldDecorationBox(
value = value.text,
visualTransformation = visualTransformation,
innerTextField = innerTextField,
placeholder = placeholder,
label = label,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
supportingText = supportingText,
shape = shape,
singleLine = singleLine,
enabled = enabled,
isError = isError,
interactionSource = interactionSource,
colors = colors
)
}
)
}
}
直接复制会报很多错,可以慢慢解决
首先是TextFieldValue,不知道是啥,输入框文本应该是String,所以改成String
接下来,因为现在不设置文本颜色什么的,所以直接将Basic上边的都删掉:
// If color is not provided via the text style, use content color as a default
val textColor = textStyle.color.takeOrElse {
colors.textColor(enabled).value
}
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
CompositionLocalProvider(LocalTextSelectionColors provides colors.selectionColors) {
@OptIn(ExperimentalMaterial3Api::class)
将Basic里的mergedTextStyle改为textStyle,并将传入参数改为
textStyle: TextStyle =TextStyle.Default,
将剩余错误解决后,
剩下的代码应该是:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle =TextStyle.Default,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.filledShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
) {
BasicTextField(
value = value,
modifier = modifier
.defaultMinSize(
minWidth = TextFieldDefaults.MinWidth,
minHeight = TextFieldDefaults.MinHeight
),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
cursorBrush = SolidColor(Color.Black),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
decorationBox = @Composable { innerTextField ->
// places leading icon, text field with label and placeholder, trailing icon
innerTextField()
}
)
}
这样,一个基础的自定义TextField就完成了,接下来就是在BasicTextField的decorationBox 定义它的外观,下划线等样式。
比如一个简单的下划线:
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier
.padding(top = 4.dp, bottom = 4.dp)
.indicatorLine(drawLine, isError, interactionSource, colors,
focusedIndicatorLineThickness, unfocusedIndicatorLineThickness
)
)
这个indicatorLine是modifier的一个属性,可以打开看看。
而想要装饰输入框,就是在decorationBox中搞你自己的布局就行了,只不过别忘记加
innerTextField()
最后附上我在这个应用中的自定义TextField:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SelfTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
drawLine: Boolean ,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = TextStyle.Default,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
supportingText: @Composable (() -> Unit)? = null,
isError: Boolean = false,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.filledShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors(containerColor = Color.Transparent, cursorColor = Color.Black),
focusedIndicatorLineThickness: Dp = 1.dp,
unfocusedIndicatorLineThickness: Dp = 1.dp,
cursorBrush: Color = Color.Black,
) {
BasicTextField(
value = value,
modifier = modifier,
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
cursorBrush = SolidColor(cursorBrush),
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
decorationBox = @Composable { innerTextField ->
if (drawLine) {
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier
.padding(top = 4.dp, bottom = 4.dp)
.indicatorLine(
drawLine, isError, interactionSource, colors,
focusedIndicatorLineThickness, unfocusedIndicatorLineThickness
)
)
{
if (label != null) {
label()
}
if (placeholder != null && value.isEmpty()) {
placeholder()
}
innerTextField()
}
}else{
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier
.padding(top = 4.dp, bottom = 4.dp)
)
{
if (label != null) {
label()
}
if (placeholder != null && value.isEmpty()) {
placeholder()
}
innerTextField()
}
}
}
)
}
可以看到最后有个if else结构,这是我为了一种输入框有两种表现形式而给出的下策,之后看看有没有更好的方法吧,这样,上一篇的整个页面就完成了!剩余就是布局的事了,相信你应该已经会了!
放个页面的代码:
fun WriteTextPage() {
// val fictionName = backStackEntry.arguments?.getString("fiction_name")
var text by remember { mutableStateOf("") }
Scaffold(modifier = Modifier.fillMaxWidth(),
topBar = {
TopAppBar(modifier = Modifier.height(50.dp), // 设置高度为 50dp
title = { Text("") },
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = colorResource(id = R.color.toolbar_color)
),
actions = {
WriteTextTopBar()
})
}
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(innerPadding)
.verticalScroll(rememberScrollState(0)),
) {
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
val (left_spacer, right_spacer, chapter_number_text, chapter_name_text, content_text) = createRefs()
Spacer(modifier = Modifier
.width(16.dp)
.constrainAs(left_spacer) {
start.linkTo(parent.start)
})
Spacer(modifier = Modifier
.width(16.dp)
.constrainAs(right_spacer) {
end.linkTo(parent.end)
})
Text(text = "第1000章",
Modifier
.wrapContentWidth()
.constrainAs(chapter_number_text) {
baseline.linkTo(chapter_name_text.baseline)
start.linkTo(left_spacer.end)
}, fontSize = 20.sp
)
SelfTextField(
value = text,
onValueChange = { text = it },
singleLine = true,
drawLine = true,
maxLines = 1,
modifier = Modifier
.constrainAs(chapter_name_text) {
top.linkTo(parent.top, margin = 12.dp)
start.linkTo(chapter_number_text.end, margin = 12.dp)
end.linkTo(right_spacer.start, margin = 30.dp)
width = Dimension.fillToConstraints
},
textStyle = TextStyle.Default.copy(fontSize = 20.sp)
)
SelfTextField(
value = text,
onValueChange = { text = it },
singleLine = false,
drawLine = false,
modifier = Modifier
.constrainAs(content_text) {
top.linkTo(chapter_name_text.bottom, margin = 8.dp)
bottom.linkTo(parent.bottom)
start.linkTo(left_spacer.end)
end.linkTo(right_spacer.start)
width = Dimension.fillToConstraints
},
textStyle = TextStyle.Default.copy(fontSize = 20.sp, lineHeight = 1.5.em)
)
}
}
}
}