'Phone number visual transformation in Jetpack compose
How can I implement phone number visual transformation in jetpack compose? I have read this article for the card number.
And I want to format my phone number like this xx xxx xx xx
Solution 1:[1]
You can just modify some params from the example link that you provided according to the pattern you need. You need to consider the max length you want, how many spaces you need between each section.
For example in your given link here: http://zenandroid.io/using-the-jetpack-composes-visualtransformation-to-create-a-credit-card-text-input/
- They're adding space after every 4th character in
AnnotatedString.Builder()
You need it on 1, 4, 6. - then they've added 2 spaces that's why they're adding spaces like 2,4,6 in
originalToTransformed
but you need 1,2,3 & same for deducting intransformedToOriginal
Complete code example:
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)
}
Output:
Solution 2:[2]
For a north American version:
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)
}
For an even cleaner version, see this Medium Article:
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Mayur Gajra |
Solution 2 |