ضبط الحقول النصية

يسمح TextField للمستخدمين بإدخال النص وتعديله. هناك نوعان من حقول النص التي يمكنك استخدامها: حقول النص المستندة إلى الحالة وحقول النص المستندة إلى القيمة. اختَر النوع الذي تريد عرض المحتوى له:

ننصح باستخدام حقول نصية مستندة إلى الحالة، لأنّها توفّر طريقة أكثر اكتمالاً وموثوقية لإدارة حالة TextField. يوضح الجدول التالي الاختلافات بين هذه الأنواع من الحقول النصية، ويتضمن المزايا الرئيسية التي توفرها الحقول النصية المستندة إلى الحالة:

الميزة

الحقول النصية المستندة إلى القيمة

الحقول النصية المستندة إلى الحالة

مزايا خاصة بالولاية

إدارة الحالة

يتم تعديل حالة حقل النص باستخدام دالة الاستدعاء onValueChange. أنت مسؤول عن تعديل value في ولايتك استنادًا إلى التغييرات التي أبلغ عنها onValueChange.

يستخدم بشكل صريح عنصر TextFieldState لإدارة حالة إدخال النص (القيمة والاختيار والتركيب). ويمكن تذكُّر هذه الحالة ومشاركتها.

  • تمت إزالة دالة الاستدعاء onValueChange، ما يمنعك من تقديم سلوكيات غير متزامنة.
  • وتظل الحالة محفوظة حتى بعد إعادة الإنشاء وضبط الإعدادات وإيقاف العملية نهائيًا.

التحوّل المرئي

يستخدم VisualTransformation لتعديل طريقة ظهور النص المعروض. يتولّى هذا عادةً تنسيق كل من الإدخال والإخراج في خطوة واحدة.

يستخدم InputTransformation لتعديل إدخال المستخدم قبل إرساله إلى الحالة، وOutputTransformation لتنسيق محتوى حقل النص بدون تغيير بيانات الحالة الأساسية.

  • لم تعُد بحاجة إلى تقديم عملية الربط بين النص الأولي الأصلي والنص المحوَّل باستخدام OutputTransformation.

الحدود القصوى لعدد الأسطر

يقبل singleLine: Boolean, maxLines: Int وminLines: Int للتحكّم في عدد الأسطر.

يستخدم lineLimits: TextFieldLineLimits لضبط الحد الأدنى والحد الأقصى لعدد الأسطر التي يمكن أن يشغلها حقل النص.

  • تزيل هذه السياسة الغموض عند ضبط حدود السطور من خلال توفير مَعلمة lineLimits من النوع TextFieldLineLimits.

حقل نصي آمن

لا ينطبق

‫SecureTextField هي دالة مركّبة تستند إلى حقول نصية مستندة إلى الحالة لكتابة حقل كلمة مرور.

  • تتيح لك هذه الميزة تحسين الأمان في الخلفية، وتتضمّن واجهة مستخدم محدّدة مسبقًا مع textObfuscationMode.

توضّح هذه الصفحة كيفية تنفيذ TextField وتنسيق إدخال TextField وضبط خيارات TextField الأخرى، مثل خيارات لوحة المفاتيح وتحويل إدخال المستخدم بشكل مرئي.

اختيار طريقة تنفيذ TextField

هناك مستويان لتنفيذ TextField:

  1. ‫TextField هو تنفيذ التصميم المتعدد الأبعاد. ننصحك باختيار هذه الطريقة لأنّها تتّبع إرشادات التصميم المتعدد الأبعاد:
  2. يتيح BasicTextField للمستخدمين تعديل النص باستخدام لوحة المفاتيح المادية أو لوحة المفاتيح الافتراضية، ولكنّه لا يوفّر أيّ عناصر تزيينية، مثل التلميحات أو العناصر النائبة.

TextField(
    state = rememberTextFieldState(initialText = "Hello"),
    label = { Text("Label") }
)

حقل نص قابل للتعديل يحتوي على الكلمة

OutlinedTextField(
    state = rememberTextFieldState(),
    label = { Text("Label") }
)

حقل نص قابل للتعديل، مع حدود وتصنيف باللون الأرجواني

النمط TextField

يتشارك TextField وBasicTextField العديد من المَعلمات الشائعة للتخصيص. تتوفّر القائمة الكاملة لـ TextField في رمز مصدر TextField. في ما يلي قائمة غير شاملة ببعض المَعلمات المفيدة:

  • textStyle
  • lineLimits

TextField(
    state = rememberTextFieldState("Hello\nWorld\nInvisible"),
    lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 2),
    placeholder = { Text("") },
    textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
    label = { Text("Enter text") },
    modifier = Modifier.padding(20.dp)
)

حقل TextField متعدّد الأسطر، يتضمّن سطرَين قابلَين للتعديل بالإضافة إلى التصنيف

ننصح باستخدام TextField بدلاً من BasicTextField عندما يتطلّب تصميمك استخدام TextField أو OutlinedTextField من Material. ومع ذلك، يجب استخدام BasicTextField عند إنشاء تصميمات لا تحتاج إلى الزخارف من مواصفات Material.

ضبط حدود الخطوط

تتيح الدوال المركّبة TextField التمرير على طول محور واحد. يتم تحديد سلوك الانتقال من خلال المَعلمة lineLimits. يتم ضبط TextFields للانتقال أفقيًا في سطر واحد، بينما يتم الانتقال عموديًا في TextFields متعددة الأسطر.

استخدِم TextFieldLineLimits لاختيار إعدادات السطر المناسبة لـ TextField:

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine
)

حقل نصي من سطر واحد يحتوي على النص

يتميّز إعداد SingleLine بالسمات التالية:

  • لا يتم التفاف النص مطلقًا، ولا يسمح بأسطر جديدة.
  • يتميز عنصر "TextField" دائمًا بارتفاع ثابت.
  • إذا تجاوز النص المساحة المخصّصة له، يتم الانتقال إليه أفقيًا.

TextField(
    state = rememberTextFieldState("Hello\nWorld\nHello\nWorld"),
    lineLimits = TextFieldLineLimits.MultiLine(1, 4)
)

حقل نص متعدّد الأسطر يتضمّن النص

يتميّز إعداد MultiLine بالسمات التالية:

  • تقبل هذه الدالة مَعلمتَين: minHeightInLines وmaxHeightInLines.
  • يبلغ طول حقل النص minHeightInLines على الأقل.
  • إذا تجاوز النص الحد الأقصى، سيتم التفافه.
  • إذا كان النص يتطلّب المزيد من الأسطر، يتوسّع الحقل حتى يصل ارتفاعه إلى maxHeightInLines، ويتم التمرير عموديًا.

تنسيق الإدخال باستخدام Brush API

يمكنك استخدام Brush API لإضافة أنماط أكثر تقدّمًا في TextField. يوضّح القسم التالي كيفية استخدام فرشاة لإضافة تدرج لوني إلى إدخال TextField.

لمزيد من المعلومات حول استخدام Brush API لتنسيق النص، يُرجى الاطّلاع على مقالة تفعيل التنسيق المتقدّم باستخدام Brush API.

تنفيذ التدرجات الملونة باستخدام TextStyle

لتطبيق تدرّج الألوان أثناء الكتابة داخل TextField، اضبط الفرشاة التي تختارها على TextStyle لـ TextField. في هذا المثال، نستخدم فرشاة مدمجة مع linearGradient لعرض تأثير تدرج قوس قزح أثناء كتابة النص في TextField.

val brush = remember {
    Brush.linearGradient(
        colors = listOf(Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Magenta)
    )
}
TextField(
    state = rememberTextFieldState(), textStyle = TextStyle(brush = brush)
)

يتم كتابة نص في حقل نصي، ويظهر تأثير تدرج ألوان قوس قزح.
الشكل 1: تأثير متدرج بألوان قوس قزح لمحتوى TextField

إدارة حالة حقل النص

يستخدم TextField عنصر الاحتفاظ بالحالة المخصّص الذي يُسمى TextFieldState للمحتوى والاختيار. تم تصميم TextFieldState ليتم رفعه أينما كان مناسبًا في البنية. هناك سمتان رئيسيتان يوفّرهما TextFieldState:

  • ‫initialText: محتوى TextField.
  • ‫initialSelection: يشير إلى مكان المؤشر أو النص المحدّد.

ما يميّز TextFieldState عن الطرق الأخرى، مثل دالة الاستدعاء onValueChange، هو أنّ TextFieldState يغلّف بالكامل عملية الإدخال بأكملها. ويشمل ذلك استخدام هياكل البيانات الداعمة الصحيحة، وتضمين الفلاتر وأدوات التنسيق، بالإضافة إلى مزامنة جميع التعديلات الواردة من مصادر مختلفة.

يمكنك استخدام TextFieldState() لرفع الحالة في TextField. لذلك، ننصح باستخدام الدالة rememberTextFieldState(). تنشئ الدالة rememberTextFieldState() مثيل TextFieldState في الدالة المركّبة، وتتأكّد من تذكُّر كائن الحالة، وتوفّر وظيفة مدمجة للحفظ والاستعادة:

val usernameState = rememberTextFieldState()
TextField(
    state = usernameState,
    lineLimits = TextFieldLineLimits.SingleLine,
    placeholder = { Text("Enter Username") }
)

يمكن أن تحتوي الدالة rememberTextFieldState على مَعلمة فارغة أو قيمة أولية يتم تمريرها لتمثيل قيمة النص عند الإعداد. إذا تم تمرير قيمة مختلفة في إعادة تركيب لاحقة، لن يتم تعديل قيمة الحالة. لتعديل الحالة بعد إعدادها، يمكنك استدعاء طرق التعديل في TextFieldState.

TextField(
    state = rememberTextFieldState(initialText = "Username"),
    lineLimits = TextFieldLineLimits.SingleLine,
)

حقل TextField يظهر فيه النص "اسم المستخدم" داخل حقل النص.
الشكل 2. TextField مع "اسم المستخدم" كنص أوّلي.

تعديل النص باستخدام TextFieldBuffer

يُعدّ TextFieldBuffer حاوية نصية قابلة للتعديل، وهو مشابه في وظيفته لـ StringBuilder. يحتوي على كل من المحتوى النصي والمعلومات حول التحديد.

غالبًا ما تصادف TextFieldBuffer كنطاق استقبال في دوال مثل TextFieldState.edit أو InputTransformation.transformInput أو OutputTransformation.transformOutput. في هذه الدوال، يمكنك قراءة "TextFieldBuffer" أو تعديله حسب الحاجة. بعد ذلك، يتم إما تنفيذ هذه التغييرات في TextFieldState أو تمريرها إلى مسار العرض في حالة OutputTransformation.

يمكنك استخدام وظائف التعديل العادية، مثل append أو insert أو replace أو delete لتعديل محتوى المخزن المؤقت. لتغيير حالة الاختيار، يمكنك ضبط المتغيّر selection: TextRange مباشرةً أو استخدام دوال خدمية مثل placeCursorAtEnd أو selectAll. يتم تمثيل التحديد نفسه بواسطة TextRange، حيث يكون فهرس البداية شاملاً وفهرس النهاية حصريًا. يشير TextRange الذي يحتوي على قيم بداية ونهاية متطابقة، مثل (3, 3)، إلى موضع المؤشر بدون اختيار أي أحرف.

val phoneNumberState = rememberTextFieldState("1234567890")

TextField(
    state = phoneNumberState,
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Phone
    ),
    inputTransformation = InputTransformation.maxLength(10).then {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

تعديل النص في TextFieldState

هناك عدة طرق تتيح لك تعديل الحالة مباشرةً من خلال متغير الحالة:

  • ‫edit: تتيح لك تعديل محتوى الحالة وتوفّر لك TextFieldBuffer دالة لتتمكّن من استخدام طرق مثل insert وreplace وappend والمزيد.

    // Initial textFieldState text passed in is "I love Android"
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    textFieldState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    textFieldState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

  • ‫setTextAndPlaceCursorAtEnd: يمحو النص الحالي ويستبدله بالنص المحدّد ويضع المؤشر في نهاية النص.

    textFieldState.setTextAndPlaceCursorAtEnd("I really love Android")
    // textFieldState.text : I really love Android
    // textFieldState.selection : TextRange(21, 21)

  • ‫clearText: لمحو كل النص

    textFieldState.clearText()
    // textFieldState.text :
    // textFieldState.selection : TextRange(0, 0)

للاطّلاع على دوال TextFieldState الأخرى، يُرجى مراجعة مرجع TextFieldState.

تعديل إدخال المستخدم

توضّح الأقسام التالية كيفية تعديل إدخالات المستخدمين. تتيح لك ميزة تحويل الإدخال فلترة إدخال TextField أثناء كتابة المستخدم، بينما تعمل ميزة تحويل الإخراج على تنسيق إدخال المستخدم قبل عرضه على الشاشة.

فلترة إدخالات المستخدمين باستخدام عمليات تحويل الإدخالات

يتيح لك تحويل الإدخال فلترة الإدخال من المستخدم. على سبيل المثال، إذا كان TextField الخاص بك يتلقى رقم هاتف أمريكي، فأنت تريد فقط قبول 10 أرقام. يتم حفظ نتائج InputTransformation في TextFieldState.

تتوفّر فلاتر مدمجة لحالات استخدام InputTransformation الشائعة. للحدّ من طول الفيديو، يُرجى الاتصال بالرقم InputTransformation.maxLength():

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine,
    inputTransformation = InputTransformation.maxLength(10)
)

تحويلات الإدخال المخصّصة

‫InputTransformation هي واجهة دالة واحدة. عند تنفيذ InputTransformation المخصّص، عليك إلغاء TextFieldBuffer.transformInput:

class CustomInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
    }
}

بالنسبة إلى رقم الهاتف، أضِف تحويلاً مخصّصًا للإدخال يسمح فقط بكتابة الأرقام في TextField:

class DigitOnlyInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    }
}

عمليات تحويل إدخال السلسلة

لإضافة فلاتر متعددة على النص الذي تم إدخاله، يمكنك ربط InputTransformations باستخدام دالة الإضافة then. يتم تنفيذ الفلاتر بشكل تسلسلي. كأفضل ممارسة، طبِّق الفلاتر الأكثر انتقائية أولاً لتجنُّب إجراء عمليات تحويل غير ضرورية على البيانات التي ستتم فلترتها في النهاية.

TextField(
    state = rememberTextFieldState(),
    inputTransformation = InputTransformation.maxLength(6)
        .then(CustomInputTransformation()),
)

بعد إضافة عمليات تحويل الإدخال، يقبل الإدخال TextField 10 أرقام كحد أقصى.

تنسيق الإدخال قبل عرضه

تتيح لك OutputTransformations تنسيق إدخال المستخدم قبل عرضه على الشاشة. على عكس InputTransformation، لا يتم حفظ التنسيق الذي يتم إجراؤه من خلال OutputTransformation في TextFieldState. بناءً على مثال رقم الهاتف السابق، تحتاج إلى إضافة أقواس وشرطات في الأماكن المناسبة:

رقم هاتف أمريكي، منسق بشكل صحيح مع أقواس وشرطات وفهارس مقابلة.
الشكل 3. رقم هاتف أمريكي بتنسيق مناسب وفهارس مقابلة

هذه هي الطريقة المعدَّلة للتعامل مع VisualTransformation في TextField المستندة إلى القيمة، مع اختلاف رئيسي يتمثل في أنّه ليس عليك احتساب عمليات ربط الإزاحة.

‫OutputTransformation هي واجهة طريقة مجرّدة واحدة. لتنفيذ OutputTransformation مخصّص، عليك إلغاء الإجراء transformOutput:

class CustomOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
    }
}

لتنسيق رقم هاتف، أضِف قوسًا مفتوحًا عند الفهرس 0 وقوسًا مغلقًا عند الفهرس 4 وشرطة عند الفهرس 8 إلى OutputTransformation:

class PhoneNumberOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
}

بعد ذلك، أضِف OutputTransformation إلى TextField:

TextField(
    state = rememberTextFieldState(),
    outputTransformation = PhoneNumberOutputTransformation()
)

كيفية عمل عمليات التحويل معًا

يوضح المخطط التالي التدفق من إدخال النص إلى التحويل إلى الإخراج:

صورة توضيحية لكيفية معالجة النص المُدخَل قبل أن يصبح نصًا مُخرَجًا
الشكل 4. مخطّط بياني يوضّح كيف يمرّ النص المُدخل بعمليات تحويل قبل أن يصبح نصًا مُخرجًا
  1. يتم تلقّي الإدخال من مصدر الإدخال.
  2. تتم فلترة الإدخال من خلال InputTransformation، ويتم حفظه في TextFieldState.
  3. يتم تمرير الإدخال من خلال OutputTransformation للتنسيق.
  4. يتم عرض الإدخال في TextField.

ضبط خيارات لوحة المفاتيح

يتيح لك TextField ضبط خيارات إعدادات لوحة المفاتيح، مثل تنسيق لوحة المفاتيح، أو تفعيل ميزة التصحيح التلقائي إذا كانت لوحة المفاتيح تتيح ذلك. قد لا تكون بعض الخيارات مضمونة إذا كانت لوحة المفاتيح الافتراضية لا تتوافق مع الخيارات المقدَّمة هنا. في ما يلي قائمة بخيارات لوحة المفاتيح المتوافقة:

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

تتضمّن فئة KeyboardOptions الآن مَعلمة منطقية جديدة، وهي showKeyboardOnFocus، والتي تستخدمها خصيصًا لمكوّنات TextField المدمجة مع TextFieldState. يتحكّم هذا الخيار في سلوك لوحة المفاتيح الافتراضية عندما يتم التركيز على TextField من خلال وسائل أخرى غير تفاعل المستخدم المباشر (على سبيل المثال، آليًا).

عند ضبط السمة KeyboardOptions.showKeyboardOnFocus على "صحيح"، لا تظهر لوحة المفاتيح الافتراضية تلقائيًا إذا تم التركيز على TextField بشكل غير مباشر. في هذه الحالات، يجب أن ينقر المستخدم صراحةً على TextField نفسه لإظهار لوحة المفاتيح.

تحديد منطق التفاعل مع لوحة المفاتيح

يتيح زر الإجراء في لوحة المفاتيح الافتراضية على Android تقديم ردود تفاعلية داخل تطبيقك. لمزيد من المعلومات حول ضبط زر الإجراء، يُرجى الاطّلاع على قسم ضبط خيارات لوحة المفاتيح.

زر إجراء في لوحة المفاتيح الافتراضية (رمز علامة صح) محاط بدائرة حمراء
الشكل 5. زر إجراء لوحة المفاتيح الافتراضية

لتحديد ما يحدث عندما ينقر المستخدم على زر الإجراء هذا، استخدِم المَعلمة onKeyboardAction. تقبل هذه المَعلمة واجهة وظيفية اختيارية باسم KeyboardActionHandler. تحتوي واجهة KeyboardActionHandler على طريقة واحدة، وهي onKeyboardAction(performDefaultAction: () -> Unit). من خلال توفير طريقة تنفيذ لهذه الطريقة onKeyboardAction، يمكنك إدخال منطق مخصّص يتم تنفيذه عندما يضغط المستخدم على زر الإجراء في لوحة المفاتيح.

تتوفّر عدة أنواع عادية من إجراءات لوحة المفاتيح مع إعدادات تلقائية مضمّنة. على سبيل المثال، سيؤدي اختيار ImeAction.Next أو ImeAction.Previous كنوع الإجراء إلى نقل التركيز تلقائيًا إلى حقل الإدخال التالي أو السابق على التوالي. وبالمثل، يؤدي زر الإجراء الذي تم ضبطه على ImeAction.Done عادةً إلى إغلاق لوحة المفاتيح الافتراضية. يتم تنفيذ هذه الوظائف التلقائية تلقائيًا ولا تتطلّب منك تقديم KeyboardActionHandler.

يمكنك أيضًا تنفيذ سلوك مخصّص بالإضافة إلى هذه الإجراءات التلقائية. عند تقديم KeyboardActionHandler، تتلقّى طريقة onKeyboardAction دالة performDefaultAction. يمكنك استدعاء دالة performDefaultAction() هذه في أي وقت ضمن المنطق المخصّص لتفعيل الإعداد التلقائي المرتبط بإجراء "محرر أسلوب الإدخال" الحالي أيضًا.

TextField(
    state = textFieldViewModel.usernameState,
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    onKeyboardAction = { performDefaultAction ->
        textFieldViewModel.validateUsername()
        performDefaultAction()
    }
)

يوضّح هذا المقتطف حالة استخدام شائعة على شاشة تسجيل تتضمّن حقل اسم المستخدم. بالنسبة إلى هذا الحقل، تم اختيار ImeAction.Next لزر إجراء لوحة المفاتيح. يتيح هذا الخيار التنقل السريع والسلس إلى حقل كلمة المرور اللاحق.

بالإضافة إلى هذا التنقّل العادي، هناك شرط لبدء عملية التحقّق من اسم المستخدم في الخلفية أثناء انتقال المستخدم لإدخال كلمة المرور. للتأكّد من الاحتفاظ بسلوك تبديل التركيز التلقائي المتأصل في ImeAction.Next إلى جانب عملية تحقّق مخصّصة، يتم استدعاء الدالة performDefaultAction(). يؤدي استدعاء performDefaultAction() إلى تشغيل نظام إدارة التركيز الأساسي ضمنيًا لنقل التركيز إلى عنصر في واجهة المستخدم المناسب التالي، مع الحفاظ على مسار التنقل المتوقع.

إنشاء حقل كلمة مرور آمنة

‫SecureTextField هي دالة مركّبة تستند إلى حقول نصية مستندة إلى الحالة لكتابة حقل كلمة مرور. ننصح باستخدام SecureTextField لإنشاء حقول نصية لكلمات المرور، لأنّه يخفي الأحرف التي يتم إدخالها تلقائيًا ويوقف إجراءات القص والنسخ.

يحتوي SecureTextField على textObfuscationMode، والذي يتحكم في كيفية رؤية المستخدم لإدخال الأحرف. تتوفّر لـ "textObfuscationMode" الخيارات التالية:

  • ‫Hidden: لإخفاء جميع المدخلات الإعداد التلقائي على أجهزة الكمبيوتر المكتبي

    حقل نصي آمن يتم فيه إخفاء الأحرف المكتوبة.

  • ‫Visible: يعرض جميع المدخلات.

    حقل نصي آمن تظهر فيه الأحرف المكتوبة.

  • RevealLastTyped: لإخفاء كل الإدخالات باستثناء الحرف الأخير الإعداد التلقائي على الأجهزة الجوّالة

    حقل نصي آمن يتم فيه الكشف عن آخر حرف تمت كتابته لفترة وجيزة فقط.

مراجع إضافية