Android/Jetpack Compose

Jetpack Compose TextField 날짜, 시간 마스킹 처리

loppav6 2022. 11. 2. 15:54

TextField에 날짜나 시간 입력 시 마스킹 처리를 하고 싶을 때가 있다.

TextField의 VisualTransformation Interface를 활용해 아래와 같이 처리해보자.

ex1) 13:25

ex2) 2022-12-12

 

 

소스는 Jetpack Compose 키보드 내리기 소스를 참조하자.

https://loppav6.tistory.com/21

 

1. 시간 마스킹 VisualTransformation

private class TimeTransFormation() : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return timeFilter(text)
    }

    private fun timeFilter(text: AnnotatedString): TransformedText {
        val trimmed = if (text.text.length >= 4) text.text.substring(0..3) else text.text
        var out = ""

        for (i in trimmed.indices) {
            out += trimmed[i]
            if (i % 2 == 1 && i < 3) out += ":"
        }

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

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

        return TransformedText(AnnotatedString(out), numberOffsetTranslator)
    }
}

 

2. 날짜 마스킹 VisualTransformation

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 == 3 || i == 5) out += "-"
    }

    val numberOffsetTranslator = object : OffsetMapping {
        override fun originalToTransformed(offset: Int): Int {
            if (offset <= 4) return offset
            if (offset <= 5) 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)
}

 

3. UI

@Composable
fun MainView() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val textFieldTime = remember { mutableStateOf("") }
        val textFieldDate = remember { mutableStateOf("") }
        val focusManager = LocalFocusManager.current

        TextField(
            value = textFieldTime.value,
            onValueChange = { if (it.length <= 4) textFieldTime.value = it },
            keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done, keyboardType = KeyboardType.Number),
            visualTransformation = TimeTransFormation(),
            placeholder = { Text(text = "13:50") },
        )

        TextField(
            value = textFieldDate.value,
            onValueChange = { if (it.length <= 8) textFieldDate.value = it },
            keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
            keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done, keyboardType = KeyboardType.Number),
            visualTransformation = DateTransformation(),
            placeholder = { Text(text = "2022-11-02") },
        )

        Button(onClick = { focusManager.clearFocus() }, modifier = Modifier.fillMaxWidth()) {
            Text(text = "닫기")
        }
    }
}