七、使用Jetpack Compsoe编写一个写小说的Android应用:自定义Textfield

上一篇只做了写文本页的工具栏,对于下方的写作区还没有进行代码编写。

其实是在使用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)
                )
            }
        }
    }
}

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HO灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值