Rez*_*aji 3 android kotlin android-jetpack-compose android-compose-textfield
如何实现千位分隔符视觉转换,该转换也适用于小数。我找到了 Int 数字的千位分隔符视觉转换的实现,但问题是当我想将它用于十进制数字时,我必须控制小数分隔符的计数不超过 1 次。
您可以使用:
onValueChange使用正则表达式模式将允许的字符限制为十进制数的属性visualTransformation使用千位分隔符格式化数字就像是:
val pattern = remember { Regex("^\\d*\\.?\\d*\$") }
TextField(
value = text,
onValueChange = {
if (it.isEmpty() || it.matches(pattern)) {
text = it
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
visualTransformation = ThousandSeparatorTransformation()
)
class ThousandSeparatorTransformation : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val symbols = DecimalFormat().decimalFormatSymbols
val decimalSeparator = symbols.decimalSeparator
var outputText = ""
var integerPart = 0L
var decimalPart = ""
if (text.text.isNotEmpty()) {
val number = text.text.toDouble()
integerPart = number.toLong()
outputText += NumberFormat.getIntegerInstance().format(integerPart)
if (text.text.contains(decimalSeparator)) {
decimalPart = text.text.substring(text.text.indexOf(decimalSeparator))
if (decimalPart.isNotEmpty()) {
outputText += decimalPart
}
}
}
val numberOffsetTranslator = object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return outputText.length
}
override fun transformedToOriginal(offset: Int): Int {
return text.length
}
}
return TransformedText(
text = AnnotatedString(outputText),
offsetMapping = numberOffsetTranslator
)
}
}
Run Code Online (Sandbox Code Playgroud)
这样,OffsetMapping光标将保持静止在值的末尾。否则,您必须计算数千个分隔符计数并根据它修复偏移量。
Decimal Amount Visual Transformation - Jetpack 为十进制输入编写视觉转换. 光标工作得很好!
小数金额转换
private val groupingSymbol = ' '
private val decimalSymbol = '.'
private val numberFormatter: DecimalFormat = DecimalFormat("#,###").apply {
decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault()).apply {
groupingSeparator = groupingSymbol
decimalSeparator = decimalSymbol
}
}
class DecimalAmountTransformation : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val transformation = reformat(text.text)
return TransformedText(
AnnotatedString(transformation.formatted ?: ""),
object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return transformation.originalToTransformed[offset]
}
override fun transformedToOriginal(offset: Int): Int {
return transformation.transformedToOriginal[offset]
}
},
)
}
private fun reformat(original: String): Transformation {
val parts = original.split(decimalSymbol)
check(parts.size < 3) { "original text must have only one dot (use filteredDecimalText)" }
val hasEndDot = original.endsWith('.')
var formatted = original
Log.d("original_tag", original)
if (original.isNotEmpty() && parts.size == 1) {
formatted = numberFormatter.format(BigDecimal(parts[0]))
if (hasEndDot) {
formatted += decimalSymbol
}
} else if (parts.size == 2) {
val numberPart = numberFormatter.format(BigDecimal(parts[0]))
val decimalPart = parts[1]
formatted = "$numberPart.$decimalPart"
}
val originalToTransformed = mutableListOf<Int>()
val transformedToOriginal = mutableListOf<Int>()
var specialCharsCount = 0
formatted.forEachIndexed { index, char ->
if (groupingSymbol == char) {
specialCharsCount++
} else {
originalToTransformed.add(index)
}
transformedToOriginal.add(index - specialCharsCount)
}
originalToTransformed.add(originalToTransformed.maxOrNull()?.plus(1) ?: 0)
transformedToOriginal.add(transformedToOriginal.maxOrNull()?.plus(1) ?: 0)
return Transformation(formatted, originalToTransformed, transformedToOriginal)
}
}
data class Transformation(
val formatted: String?,
val originalToTransformed: List<Int>,
val transformedToOriginal: List<Int>,
)
Run Code Online (Sandbox Code Playgroud)
我们还需要过滤输入以获得所需的结果 DecimalnputFilter.kt
private val decimalSymbol = '.'
object InputFilterRegex {
val DecimalInput by lazy { Regex("^(\\d*\\.?)+\$") }
}
fun filteredDecimalText(input: TextFieldValue): TextFieldValue {
var inputText = input.text.replaceFirst(regex = Regex("^0+(?!$)"), "")
var startsWithDot = input.text.startsWith(decimalSymbol)
var selectionStart = input.selection.start
var selectionEnd = input.selection.end
if (startsWithDot) {
inputText = "0$inputText"
if (selectionStart == selectionEnd) {
selectionStart++
selectionEnd++
} else {
selectionEnd++
}
}
val parts = inputText.split(decimalSymbol)
var text = if (parts.size > 1) {
parts[0] + decimalSymbol + parts.subList(1, parts.size).joinToString("")
} else {
parts.joinToString("")
}
if (text.startsWith(decimalSymbol)) {
text = "0$text"
}
return input.copy(text = text, selection = TextRange(selectionStart, selectionEnd))
}
Run Code Online (Sandbox Code Playgroud)
最后,用法将如下所示:
BasicTextField(
value = value,
onValueChange = {
if (!it.text.contains(InputFilterRegex.DecimalInput)) {
return@BasicTextField
}
onValueChange(filteredDecimalText(it))
},
visualTransformation = DecimalAmountTransformation(),
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1921 次 |
| 最近记录: |