Phone Jetpack compose 中的数字视觉转换

Phone number visual transformation in Jetpack compose

如何在 Jetpack Compose 中实现 phone 数字视觉转换?我已阅读此 article 卡号。

我想像这样格式化我的 phone 号码 xx xxx xx xx

您可以根据您需要的模式修改您提供的示例link中的一些参数。您需要考虑您想要的最大长度,每个部分之间需要多少 space。

例如,在您给定的 link 中:http://zenandroid.io/using-the-jetpack-composes-visualtransformation-to-create-a-credit-card-text-input/

  1. 他们在 AnnotatedString.Builder() 中每第 4 个字符后添加 space 您需要在 1、4、6 上使用它。
  2. 然后他们添加了 2 spaces 这就是为什么他们在 originalToTransformed 中添加 spaces 就像 2,4,6 但你需要 1,2,3 & transformedToOriginal
  3. 中的扣除相同

完整代码示例:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Test()
                }
            }
        }
    }
}


@Composable
fun Test() {

    var mobileNumber by rememberSaveable { mutableStateOf("") }
    Column {
        Row(modifier = Modifier.padding(all = 10.dp)) {
            Text(
                text = "Mobile number",
                fontSize = 14.sp,
                modifier = Modifier.weight(1f)
            )
            BasicTextField(
                value = mobileNumber,
                onValueChange = { mobileNumber = it },
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                visualTransformation = { mobileNumberFilter(it) }
            )
        }
        Box(
            modifier = Modifier
                .height(1.dp)
                .padding(start = 10.dp)
                .fillMaxWidth()
                .background(Color.Gray)
        )

        Spacer(modifier = Modifier.height(20.dp))
        Text(text = "Actual value:\n$mobileNumber")
    }

}

const val mask = "xx xxx xx xx"

fun mobileNumberFilter(text: AnnotatedString): TransformedText {
    // change the length
    val trimmed =
        if (text.text.length >= 9) text.text.substring(0..8) else text.text

    val annotatedString = AnnotatedString.Builder().run {
        for (i in trimmed.indices) {
            append(trimmed[i])
            if (i == 1 || i == 4 || i == 6) {
                append(" ")
            }
        }
        pushStyle(SpanStyle(color = Color.LightGray))
        append(mask.takeLast(mask.length - length))
        toAnnotatedString()
    }

    val phoneNumberOffsetTranslator = object : OffsetMapping {
        override fun originalToTransformed(offset: Int): Int {
            if (offset <= 1) return offset
            if (offset <= 4) return offset + 1
            if (offset <= 6) return offset + 2
            if (offset <= 9) return offset + 3
            return 12
        }

        override fun transformedToOriginal(offset: Int): Int {
            if (offset <= 1) return offset
            if (offset <= 4) return offset - 1
            if (offset <= 6) return offset - 2
            if (offset <= 9) return offset - 3
            return 9
        }
    }

    return TransformedText(annotatedString, phoneNumberOffsetTranslator)
}

输出:

对于北美版本:

const val mask = "(xxx) xxx-xxxx"
fun mobileNumberFilter(text: AnnotatedString, formType: FormType): 
TransformedText {
    if (formType != FormType.SHIPPING_PHONE) {
        return VisualTransformation.None.filter(text)
    }

    // change the length
    val trimmed =
    if (text.text.length >= 14) text.text.substring(0..13) else text.text

    val annotatedString = AnnotatedString.Builder().run {
        for (i in trimmed.indices) {
            val trimmedPortion = trimmed[i]
            if (i == 0) {
               append("($trimmedPortion")
            } else {
               append(trimmedPortion)
            }
            if (i == 2) {
               append(") ")
            }
            if (i == 5) {
               append("-")
            }
        }
        pushStyle(
            SpanStyle(color = Color.LightGray)
        )
        try {
            append(mask.takeLast(mask.length - length))
        } catch (e: IllegalArgumentException) {
            Timber.d(e.localizedMessage?.plus(" reached end of phone number"))
        }

        toAnnotatedString()
    }

    val translator = object : OffsetMapping {
        override fun originalToTransformed(offset: Int): Int {

            if (offset <= 1) return offset
            if (offset <= 4) return offset + 1
            if (offset <= 9) return offset + 2
            return 14

        }

        override fun transformedToOriginal(offset: Int): Int {

            if (offset <= 1) return offset
            if (offset <= 4) return offset - 1
            if (offset <= 9) return offset - 2
            return 14

        }
    }

    return TransformedText(annotatedString, translator)
}

要获得更简洁的版本,请参阅这篇中篇文章:

Adding Phone Number Visual transformation