0

使用 Jetpack Compose,如何格式化 TextField 值以遵循“dd/mm/yyyy”格式?

4

1 回答 1

4

更新的答案

接受 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 == '#' }
        }
    }
}

如何使用它:

@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 == '#' }
}

旧答案

正如 Jetpack Compose 提供密码的可视化转换一样,我们也可以进行自己的可视化转换。

class DateTransformation : VisualTransformation {

    // XX/XX/XXXX format
    override fun filter(text: AnnotatedString): TransformedText {
        var out = ""
        text.text.forEachIndexed { index, char ->
            when (index) {
                2 -> out += "/$char"
                4 -> out += "/$char"
                else -> out += char
            }
        }
        val numberOffsetTranslator = object : OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                if (offset <= 2) return offset
                if (offset <= 4) return offset + 1
                return offset + 2
            }

            override fun transformedToOriginal(offset: Int): Int {
                if (offset <= 2) return offset
                if (offset <= 5) return offset - 1
                return offset - 2
            }
        }
        return TransformedText(AnnotatedString(out), numberOffsetTranslator)
    }
}

我们返回一个应用了过滤器的 TransformedText 和一个在原始文本和转换后的文本之间转换的 OffsetMapping。

现在,我们可以在任何 TextField 中使用我们的 Visual Transformation 类。

var text by remember { mutableStateOf("") }
TextField(
    value = text,
    visualTransformation = DateTransformation(),
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
    onValueChange = {
        if (it.length < 9) text = it
    }
)
于 2021-09-24T05:09:56.050 回答