Jetpack Compose:自定义 TextField 设计

Jetpack Compose: Custom TextField design

总的来说,Jetpack Compose 中的大多数组件似乎都非常容易定制。

但是,TextField 就不同了。例如,假设我想做这样的事情:

人们会认为简单地包装 BaseTextField 就可以了。但是,似乎 BaseTextField 组件中出现了错误,而我有 opened an issue。此错误将不允许用户在焦点离开文本字段后再次聚焦文本字段,直到组件重新呈现。

据此,我尝试自定义 OutlinedTextFieldTextField 组件,但无法将它们自定义为如上图所示。要不是光标颜色用了activeColor属性,我也能搞定

创建类似于上面的可用文本字段的正确解决方法是什么?

嗯,在 issue I mentioned 解决之前,选择是:

  1. 回滚到 Compose 版本 1.0.0-alpha04(问题已在 alpha05 中引入)
  2. TextFieldOutlinedTextField 添加边框,如下所示:
    TextField(
        value = myValue,
        onValueChange = myOnChange,
        modifier = Modifier.clip(myShape).border(5.dp, myColor)
    )
    

您可以使用 TextField:

  • 删除带有 label = null
  • 的标签
  • 使用 TextFieldDefaults.textFieldColors 参数应用自定义颜色以隐藏指示器。
  • onValueChange 中添加一个函数来修复所描述的最大字符数

最后使用 Column 添加 2 Text 可组合项来完成外部标签和计数器文本。

类似于:

var text by remember { mutableStateOf("") }
val maxChar = 5

Column(){
    //External label
    Text(
        text = "Label",
        modifier = Modifier.fillMaxWidth(),
        textAlign = TextAlign.Start,
        color = Blue
    )

    TextField(
        value = text,
        onValueChange = {
            if (it.length <= maxChar) text = it
        },
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp),
        shape = RoundedCornerShape(8.dp),
        trailingIcon = {
            Icon(Icons.Filled.Add, "", tint = Blue)
        },
        colors = TextFieldDefaults.textFieldColors(
            backgroundColor = ....,
            focusedIndicatorColor =  Color.Transparent, //hide the indicator
            unfocusedIndicatorColor = .....)
    )
    //counter message
    Text(
        text = "${text.length} / $maxChar",
        textAlign = TextAlign.End,
        color = Blue,
        style = MaterialTheme.typography.caption, //use the caption text style
        modifier = Modifier.fillMaxWidth()
    )

通过这个例子你可以学到很多东西。对于 1.0.0,您可以这样做:

Column {
        var textState by remember { mutableStateOf("") }
        val maxLength = 110
        val lightBlue = Color(0xffd8e6ff)
        val blue = Color(0xff76a9ff)
        Text(
            text = "Caption",
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 4.dp),
            textAlign = TextAlign.Start,
            color = blue
        )
        TextField(
            modifier = Modifier.fillMaxWidth(),
            value = textState,
            colors = TextFieldDefaults.textFieldColors(
                backgroundColor = lightBlue,
                cursorColor = Color.Black,
                disabledLabelColor = lightBlue,
                focusedIndicatorColor = Color.Transparent,
                unfocusedIndicatorColor = Color.Transparent
            ),
            onValueChange = {
                if (it.length <= maxLength) textState = it
            },
            shape = RoundedCornerShape(8.dp),
            singleLine = true,
            trailingIcon = {
                if (textState.isNotEmpty()) {
                    IconButton(onClick = { textState = "" }) {
                        Icon(
                            imageVector = Icons.Outlined.Close,
                            contentDescription = null
                        )
                    }
                }
            }
        )
        Text(
            text = "${textState.length} / $maxLength",
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 4.dp),
            textAlign = TextAlign.End,
            color = blue
        )
    }

除了
如果您想为光标设置自定义颜色,可以使用以下方法实现:

Column(){
    //External label
    Text(
        ...
        ...
    )

    TextField(
        ...
        ...

        colors = TextFieldDefaults.textFieldColors(
            backgroundColor = ...,
            focusedIndicatorColor =  ..., 
            unfocusedIndicatorColor = ...,
            cursorColor = Color.Black)
    )
    //counter message
    Text(
        ...
        ...
    )