Slide 1

Slide 1 text

©2023 RAKUS Co., Ltd. TextField 表示スタイル変更の 有効活用例 5 選 DroidKaigi.collect { #7@Tokyo } 2023/12/01 @akkiee76

Slide 2

Slide 2 text

自己紹介 ● Akihiko Sato ● Rakus Inc. ● 楽楽精算 ● @akkiee76 / X 2

Slide 3

Slide 3 text

表示スタイル変更の有効活用 5 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 3

Slide 4

Slide 4 text

表示スタイル変更の有効活用 5 3 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 4

Slide 5

Slide 5 text

TextField カスタマイズの基本 ● VisualTransformation(TextField / 表示スタイル変更) ● decorationBox(BasicTextField / カスタムデザイン) @Composable fun TextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, ・・・ visualTransformation: VisualTransformation = VisualTransformation.None, ・・・ colors: TextFieldColors = TextFieldDefaults.colors() ) 5

Slide 6

Slide 6 text

VisualTransformation class OriginalVisualTransformation: VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { return TransformedText( text = AnnotatedString(buildString { TODO("表示形式へ変換処理") }), object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { TODO("入力文字列の位置から表示文字列の位置へのマッピング") } override fun transformedToOriginal(offset: Int): Int { TODO("表示文字列の位置から入力文字列の位置へのマッピング") } } ) VisualTransformation の拡張クラスを利用することで表示形式の変換を行うことができます 6

Slide 7

Slide 7 text

decorationBox(BasicTextField) @Composable fun BasicTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier,   ・・・ decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit = @Composable { innerTextField -> innerTextField() } ) 独自の Composable を設定し、入力欄のカスタマイズを行うことができます 7

Slide 8

Slide 8 text

表示スタイル変更の有効活用 5 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 8

Slide 9

Slide 9 text

1. パスワード入力: TextField val visualTransformation = PasswordVisualTransformation('*') TextField( value = text, onValueChange = { value -> if (value.isDigitsOnly()) { text = value } }, modifier = modifier, visualTransformation = visualTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number ) ) 9

Slide 10

Slide 10 text

1. パスワード入力: VisualTransformation class PasswordVisualTransformation(val mask: Char = '\u2022') : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { return TransformedText( // 文字数分の mask に変換 AnnotatedString(mask.toString().repeat(text.text.length)), OffsetMapping.Identity ) } } 10

Slide 11

Slide 11 text

表示スタイル変更の有効活用 5 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 11

Slide 12

Slide 12 text

2. クレジットカード番号入力: TextField val visualTransformation = CreditCardVisualTransformation() TextField( value = text, onValueChange = { value -> if (value.isDigitsOnly()) { text = value } }, modifier = modifier, visualTransformation = visualTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number ) ) TextFieldはパスワード入力と同様の実装 12

Slide 13

Slide 13 text

class CreditCardNumberVisualTransformation : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { val trimmed = if (text.text.length >= 16) { text.text.substring(0..15) } else { text.text } var output = "" for (i in trimmed.indices) { output += trimmed[i] if (i % 4 == 3 && i != 15) output += "-" } val creditCardOffsetTranslator = object : OffsetMapping { ・・・ } return TransformedText(AnnotatedString(output), creditCardOffsetTranslator) 2. クレジットカード番号入力: VisualTransformation https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose 13

Slide 14

Slide 14 text

2. クレジットカード番号入力: VisualTransformation class CreditCardNumberVisualTransformation : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { val creditCardOffsetTranslator = object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { if (offset <= 3) return offset if (offset <= 7) return offset + 1 if (offset <= 11) return offset + 2 if (offset <= 16) return offset + 3 return 19 } override fun transformedToOriginal(offset: Int): Int { if (offset <= 4) return offset if (offset <= 9) return offset - 1 if (offset <= 14) return offset - 2 if (offset <= 19) return offset - 3 return 16 } } return TransformedText(AnnotatedString(output), creditCardOffsetTranslator) 14

Slide 15

Slide 15 text

表示スタイル変更の有効活用 5 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 15

Slide 16

Slide 16 text

3. カンマ区切り数値入力: TextField val visualTransformation = CommaSeparatorTransformation() TextField( value = text, onValueChange = { value -> if (value.isDigitsOnly()) { text = value } }, modifier = modifier, visualTransformation = visualTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number ) ) TextFieldは同様の実装 16

Slide 17

Slide 17 text

class CommaSeparatorTransformation : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { val output = buildString { val reversed = text.text.reversed() for (index in reversed.indices) { if (index > 0 && index % 3 == 0) { append(',') } append(reversed[index]) } }.reversed() val offsetMapping = object : OffsetMapping { ・・・ } return TransformedText(text = AnnotatedString(output), offsetMapping = offsetMapping) } } 3. カンマ区切り数値入力: VisualTransformation 17

Slide 18

Slide 18 text

class CommaSeparatorTransformation : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { val offsetMapping = object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { val totalSeparatorCount = (text.length - 1) / 3 val rightSeparatorCount = (text.length - 1 - offset) / 3 val leftSeparatorCount = totalSeparatorCount - rightSeparatorCount return offset + leftSeparatorCount } override fun transformedToOriginal(offset: Int): Int { val totalSeparatorCount = (text.length - 1) / 3 val rightSeparatorCount = (output.length - offset) / 4 val leftSeparatorCount = totalSeparatorCount - rightSeparatorCount return offset - leftSeparatorCount } } return TransformedText(text = AnnotatedString(output), offsetMapping = offsetMapping) 3. カンマ区切り数値入力: VisualTransformation 18

Slide 19

Slide 19 text

表示スタイル変更の有効活用 5 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 19

Slide 20

Slide 20 text

4. PINコード入力: TextField BasicTextField( value = text, onValueChange = { newValue -> if (newValue.length <= MaxDigits) { text = newValue } }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), interactionSource = interactionSource, modifier = modifier.border(1.dp, contentColor, RoundedCornerShape(8.dp)) ) { CompositionLocalProvider(LocalContentColor provides contentColor) { PinDigits(text, interactionSource.collectIsFocusedAsState().value) } } 20

Slide 21

Slide 21 text

@Composable fun PinDigits() { Row(horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = modifier.padding(16.dp)) { repeat(MaxDigits) { i -> Text( text = text.getOrNull(i)?.toString() ?: " ", modifier = Modifier.drawWithContent { if (i == text.length && focused) { drawRect(color = focusedColor) } drawContent() if (i >= text.length) { drawLine(・・・) } } 4. PINコード入力: decorationBox 21

Slide 22

Slide 22 text

表示スタイル変更の有効活用 5 選 1. パスワード入力 2. クレジットカード番号入力 3. カンマ区切り数値入力 4. PINコード入力 5. プレフィックス文字入力 22

Slide 23

Slide 23 text

5. プレフィックス文字入力: TextField val visualTransformation = PrefixVisualTransformation() TextField( value = text, onValueChange = { value -> if (value.isDigitsOnly() && value.length <= MAX_LENGTH) { text = value } }, modifier = modifier, visualTransformation = visualTransformation, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number ) ) TextFieldはパスワード入力と同様の実装 23

Slide 24

Slide 24 text

class PrefixVisualTransformation: VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { return TransformedText( text = AnnotatedString(buildString { text.forEachIndexed { i, char -> if (i == 0) { append('X') } append(char) } }), object : OffsetMapping { override fun originalToTransformed(offset: Int): Int { return if (offset < 1) offset else offset + 1 } override fun transformedToOriginal(offset: Int): Int { return if (offset < 1) offset else offset - 1 } } 5. プレフィックス文字入力: VisualTransformation 24

Slide 25

Slide 25 text

まとめ ● VisualTransformation で自由に表示テキストの変更が可能 ● dacorationBox を使うとデザインの幅も広がる 25

Slide 26

Slide 26 text

Thank you ! 26