טיפול בקלט של משתמשים

TextField מאפשר למשתמשים להזין טקסט ולשנות אותו. בדף הזה נסביר איך מטמיעים את TextField, מעצבים את הקלט של TextField ומגדירים אפשרויות אחרות של TextField, כמו אפשרויות מקלדת וטרנספורמציה חזותית של קלט המשתמש.

בחירת הטמעה של TextField

יש שתי רמות של הטמעת TextField:

  1. TextField הוא ההטמעה של Material Design. מומלץ לבחור בהטמעה הזו כי היא תואמת להנחיות של Material Design:
    • עיצוב ברירת המחדל הוא filled
    • OutlinedTextField היא הגרסה של הסגנון outline
  2. BasicTextField מאפשר למשתמשים לערוך טקסט באמצעות מקלדת חומרה או תוכנה, אבל לא מספק קישוטים כמו רמז או placeholder.

@Composable
fun SimpleFilledTextFieldSample() {
    var text by remember { mutableStateOf("Hello") }

    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

שדה טקסט שניתן לעריכה שמכיל את המילה

@Composable
fun SimpleOutlinedTextFieldSample() {
    var text by remember { mutableStateOf("") }

    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

שדה טקסט שניתן לעריכה, עם מסגרת ותווית סגולות.

סגנון TextField

ל-TextField ול-BasicTextField יש הרבה פרמטרים משותפים להתאמה אישית. הרשימה המלאה של TextField זמינה בקוד המקור של TextField. זוהי רשימה חלקית של חלק מהפרמטרים השימושיים:

  • singleLine
  • maxLines
  • textStyle

@Composable
fun StyledTextField() {
    var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }

    TextField(
        value = value,
        onValueChange = { value = it },
        label = { Text("Enter text") },
        maxLines = 2,
        textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
        modifier = Modifier.padding(20.dp)
    )
}

שדה טקסט עם כמה שורות, עם שתי שורות שניתן לערוך וגם התווית

מומלץ להשתמש ב-TextField במקום ב-BasicTextField כשהעיצוב דורש חומר TextField או OutlinedTextField. עם זאת, צריך להשתמש ב-BasicTextField כשמפתחים עיצובים שלא צריכים את הקישוט של מפרט Material.

קלט סגנון באמצעות Brush API

אפשר להשתמש ב-Brush API כדי ליצור עיצוב מתקדם יותר ב-TextField. בקטע הבא מוסבר איך להשתמש במברשת כדי להוסיף שיפוע צבעוני לקלט של TextField.

מידע נוסף על שימוש ב-Brush API כדי לעצב טקסט זמין במאמר הפעלת עיצוב מתקדם באמצעות Brush API.

הטמעת מעברים צבעוניים באמצעות TextStyle

כדי להטמיע שינוי הדרגתי בצבע בזמן ההקלדה ב-TextField, מגדירים את המברשת הרצויה כ-TextStyle ב-TextField. בדוגמה הזו, אנחנו משתמשים במברשת מובנית עם linearGradient כדי להציג את אפקט הדרגתיות של קשת בזמן הקלדה של טקסט ב-TextField.

var text by remember { mutableStateOf("") }
val brush = remember {
    Brush.linearGradient(
        colors = rainbowColors
    )
}
TextField(
    value = text, onValueChange = { text = it }, textStyle = TextStyle(brush = brush)
)

שימוש ב-buildAnnotatedString וב-SpanStyle, יחד עם linearGradient, כדי להתאים אישית רק קטע טקסט.
איור 3. שימוש ב-buildAnnotatedString וב-SpanStyle, יחד עם linearGradient, כדי להתאים אישית רק קטע טקסט.

הגדרת אפשרויות מקלדת

TextField מאפשרת להגדיר אפשרויות של הגדרות מקלדת, כמו הפריסה של המקלדת, או להפעיל את התיקון האוטומטי אם המקלדת תומכת בו. יכול להיות שחלק מהאפשרויות לא יהיו זמינות אם מקלדת התוכנה לא תואמת לאפשרויות שמפורטות כאן. זוהי רשימת אפשרויות המקלדת הנתמכות:

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

פורמט הקלט

TextField מאפשר להגדיר VisualTransformation לערך הקלט, למשל החלפת תווים ב-* עבור סיסמאות, או הוספת מקפים כל 4 ספרות עבור מספר כרטיס אשראי:

@Composable
fun PasswordTextField() {
    var password by rememberSaveable { mutableStateOf("") }

    TextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Enter password") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

שדה להזנת טקסט של סיסמה, שבו הטקסט מוסתר

דוגמאות נוספות זמינות בקוד המקור של VisualTransformationSamples.

קלט נקי

משימה נפוצה בעריכת טקסט היא להסיר תווים מובילים, או לשנות את מחרוזת הקלט בכל פעם שהיא משתנה.

בתור מודל, צריך להניח שהמקלדת עשויה לבצע שינויים שרירותיים וגדולים בכל onValueChange. מצב כזה יכול לקרות, למשל, אם המשתמש משתמש בתיקון אוטומטי, מחליף מילה באמוג'י או משתמש בתכונות עריכה חכמות אחרות. כדי לטפל בבעיה הזו בצורה נכונה, צריך לכתוב את הלוגיקה של הטרנספורמציה בהנחה שהטקסט הנוכחי שמוענק ל-onValueChange לא קשור לערכים הקודמים או הבאים שיועברו ל-onValueChange.

כדי להטמיע שדה טקסט שאוסר על אפסים מובילים, אפשר להסיר את כל האפסים המובילים בכל שינוי ערך.

@Composable
fun NoLeadingZeroes() {
    var input by rememberSaveable { mutableStateOf("") }
    TextField(
        value = input,
        onValueChange = { newText ->
            input = newText.trimStart { it == '0' }
        }
    )
}

כדי לשלוט במיקום הסמן בזמן ניקוי הטקסט, משתמשים בעומס יתר של TextFieldValue של TextField כחלק מהמצב.

שיטות מומלצות לעבודה עם מצב

בהמשך מפורטות שיטות מומלצות להגדרה ולעדכון המצב של TextField כדי למנוע בעיות קלט באפליקציה.

  • שימוש ב-MutableState כדי לייצג את המצב של TextField: מומלץ להימנע משימוש בזרמים תגובתיים כמו StateFlow כדי לייצג את המצב של TextField, כי המבנים האלה עלולים לגרום לעיכובים אסינכרונים.

class SignUpViewModel : ViewModel() {

    var username by mutableStateOf("")
        private set

    /* ... */
}

  • הימנעות מעיכובים בעדכון המצב: כשאתם קוראים ל-onValueChange, עליכם לעדכן את TextField באופן סינכרוני ומיידי:

// SignUpViewModel.kt

class SignUpViewModel(private val userRepository: UserRepository) : ViewModel() {

    var username by mutableStateOf("")
        private set

    fun updateUsername(input: String) {
        username = input
    }
}

// SignUpScreen.kt

@Composable
fun SignUpScreen(/*...*/) {

    OutlinedTextField(
        value = viewModel.username,
        onValueChange = { username -> viewModel.updateUsername(username) }
        /*...*/
    )
}

  • איפה מגדירים את המצב: אם המצב TextField מחייב אימותים של לוגיקה עסקית בזמן ההקלדה, צריך להעביר את המצב ל-ViewModel. אם לא, אפשר להשתמש ב-composables או בכיתה של מחזיק המצב כמקור האמת. למידע נוסף על המיקום שבו צריך להעביר את המצב, עיינו במאמר בנושא העברת המצב.