如何使用 Jetpack Compose 在 TextField 中应用掩码日期 (mm/dd/yyyy)?

Spa*_*tta 7 android textfield android-jetpack-compose android-compose-textfield

我有一个文本字段,其中不能超过 10 个字符,并且要求用户以“mm/dd/yyyy”格式输入日期。每当用户键入前 2 个字符时,我都会附加“/”,当用户键入接下来的 2 个字符时,我会再次附加“/”。

为了实现这一目标,我做了以下操作:

            var maxCharDate = 10

            TextField(
                value = query2,
                onValueChange = {
                    if (it.text.length <= maxCharDate) {
                        if (it.text.length == 2 || it.text.length == 5)
                            query2 = TextFieldValue(it.text + "/", selection = TextRange(it.text.length+1))
                        else
                            query2 = it
                    }
                    emailErrorVisible.value = false
                },
                label = {
                    Text(
                        "Date of Birth (mm/dd/yyyy)",
                        color = colorResource(id = R.color.bright_green),
                        fontFamily = FontFamily(Font(R.font.poppins_regular)),
                        fontSize = with(LocalDensity.current) { dimensionResource(id = R.dimen._12ssp).toSp() })
                },
                  .
                  .
                  .
Run Code Online (Sandbox Code Playgroud)

它正在工作,只是附加的“/”在按退格键时不会被删除,而其他字符会被删除。

如何使按退格键时“/”也被删除?

Gab*_*tti 12

您可以使用onValueChange定义最大字符数并使用visualTransformation显示您喜欢的格式来执行不同的操作,而无需更改 中的值TextField

val maxChar = 8
TextField(
    singleLine = true,
    value = text,
    onValueChange = {
        if (it.length <= maxChar) text = it
    },
    visualTransformation = DateTransformation()
)
Run Code Online (Sandbox Code Playgroud)

在哪里:

class DateTransformation() : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return dateFilter(text)
    }
}

fun dateFilter(text: AnnotatedString): TransformedText {

    val trimmed = if (text.text.length >= 8) text.text.substring(0..7) else text.text
    var out = ""
    for (i in trimmed.indices) {
        out += trimmed[i]
        if (i % 2 == 1 && i < 4) out += "/"
    }

    val numberOffsetTranslator = object : OffsetMapping {
        override fun originalToTransformed(offset: Int): Int {
            if (offset <= 1) return offset
            if (offset <= 3) return offset +1
            if (offset <= 8) return offset +2
            return 10
        }

        override fun transformedToOriginal(offset: Int): Int {
            if (offset <=2) return offset
            if (offset <=5) return offset -1
            if (offset <=10) return offset -2
            return 8
        }
    }

    return TransformedText(AnnotatedString(out), numberOffsetTranslator)
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Thi*_*uza 6

在此输入图像描述

接受 Jetpack Compose TextField 任何类型掩码的 VisualTranformation 实现:

class MaskVisualTransformation(private val mask: String) : VisualTransformation {

    private val specialSymbolsIndices = mask.indices.filter { mask[it] != '#' }

    override fun filter(text: AnnotatedString): TransformedText {
        var out = ""
        var maskIndex = 0
        text.forEach { char ->
            while (specialSymbolsIndices.contains(maskIndex)) {
                out += mask[maskIndex]
                maskIndex++
            }
            out += char
            maskIndex++
        }
        return TransformedText(AnnotatedString(out), offsetTranslator())
    }

    private fun offsetTranslator() = object : OffsetMapping {
        override fun originalToTransformed(offset: Int): Int {
            val offsetValue = offset.absoluteValue
            if (offsetValue == 0) return 0
            var numberOfHashtags = 0
            val masked = mask.takeWhile {
                if (it == '#') numberOfHashtags++
                numberOfHashtags < offsetValue
            }
            return masked.length + 1
        }

        override fun transformedToOriginal(offset: Int): Int {
            return mask.take(offset.absoluteValue).count { it == '#' }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用它:

@Composable
fun DateTextField() {
    var date by remember { mutableStateOf("") }
    TextField(
        value = date,
        onValueChange = {
            if (it.length <= DATE_LENGTH) {
                date = it
            }
        },
        visualTransformation = MaskVisualTransformation(DATE_MASK)
    )
}

object DateDefaults {
    const val DATE_MASK = "##/##/####"
    const val DATE_LENGTH = 8 // Equals to "##/##/####".count { it == '#' }
}
Run Code Online (Sandbox Code Playgroud)