OOZOU
تواصلوا معنا
العودة إلى المدونة

التحقق الحديث من صحة النماذج في Android باستخدام Data Binding

كيف تتحققون من صحة حقول النماذج دون تعابير if أو when

21 ديسمبر 2020
3 دقائق للقراءة
التحقق الحديث من صحة النماذج في Android باستخدام Data Binding

مقدمة

من الأمور الكثيرة التي يجب أن يتقنها مطوّر Android بناء النماذج (forms). ويأتي معها عبء التحقق من صحة المدخلات على جهة العميل (client side validation). هناك أساليب متنوعة لفعل ذلك، لكنني أود أن أشارككم أسلوبي المفضل.

لنبدأ

أولًا سننشئ صنف validator يتولى التحقق من صحة قيم LiveData

//A predicate is a function that evaluates to true when its param matches the condition of the predicate
typealias Predicate = (value: String?) -> Boolean

class LiveDataValidator(private val liveData: LiveData<String>) {
    private val validationRules = <em>mutableListOf</em><Predicate>()
    private val errorMessages = <em>mutableListOf</em><String>()

     var error = MutableLiveData<String?>()

    <strong>//For checking if the liveData value matches the error condition set in the validation rule predicate
    //The livedata value is said to be valid when its value doesn't match an error condition set in the predicate</strong>
    fun isValid(): Boolean {
        for (i in 0 <em>until </em>validationRules.size) {
            if (validationRules[i](liveData.<em>value</em>)) {
                emitErrorMessage(errorMessages[i])
                return false
            }
        }

        emitErrorMessage(null)
        return true
    }

    <strong>//For emitting error messages</strong>
    private fun emitErrorMessage(messageRes: String?) {
        error.<em>value </em>= messageRes
    }

    <strong>//For adding validation rules</strong>
    fun addRule(errorMsg: String, predicate: Predicate) {
        validationRules.add(predicate)
        errorMessages.add(errorMsg)
    }
}

بعد ذلك سننشئ ValidatorResolver يساعدنا على تجميع عدة validators خاصة بـ LiveData. وسيفيدنا هذا عندما نريد التحقق من أن جميع كائنات LiveData المرتبطة بالنموذج في حالة صالحة.

class LiveDataValidatorResolver(private val validators: List<LiveDataValidator>) {
    fun isValid(): Boolean {
        for (validator in validators) {
            if (!validator.isValid()) return false
        }
        return true
    }
}

ثم سننشئ قواعد التحقق داخل الـ ViewModel

class ExampleViewModel : ViewModel() {
   val usernameLiveData = MutableLiveData<String>()
   val usernameValidator = LiveDataValidator(usernameLiveData).<em>apply </em><strong>{
      //Whenever the condition of the predicate is true, the error message should be emitted
      </strong>addRule("username is required") <strong>{ it</strong>.<em>isNullOrBlank</em>() <strong>}
   }
   </strong>val passwordLiveData = MutableLiveData<String>()
   val passwordValidator = LiveDataValidator(passwordLiveData).<em>apply </em><strong>{
      //We can add multiple rules. 
      //However the order in which they are added matters because the rules are checked one after the other
      </strong>addRule("password is required") <strong>{ it</strong>.<em>isNullOrBlank</em>() <strong>}
      </strong>addRule("password length must be 10") <strong>{ it?.length != 10</strong> <strong>}
   }

   //We will use a mediator so we can update the error state of our form fields
   //and the enabled state of our login button as the form data changes
   </strong>val isLoginFormValidMediator = MediatorLiveData<Boolean>()

   init {
       isLoginFormValidMediator.<em>value </em>= false
       isLoginFormValidMediator.addSource(usernameLiveData) <strong>{ </strong>validateForm() <strong>}
       </strong>isLoginFormValidMediator.addSource(passwordLiveData) <strong>{ </strong>validateForm() <strong>}</strong>
   }

   //This is called whenever the usernameLiveData and passwordLiveData changes
   fun validateForm() {
       val validators = <em>listOf</em>(usernameValidator, passwordValidator)
       val validatorResolver = LiveDataValidatorResolver(validators)
       isLoginFormValidMediator.<em>value </em>= validatorResolver.isValid()
   }
}

وأخيرًا سننشئ نموذج تسجيل دخول بسيطًا يحتوي على حقلين، اسم المستخدم وكلمة المرور، وزرًا لإتمام تسجيل الدخول. يفترض ملف XML التالي أنكم على دراية بـ data binding.

<androidx.constraintlayout.widget.ConstraintLayout
<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/field_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:errorEnabled="true"
    <strong>app:error="@{viewModel.usernameValidator.error}"</strong>
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        <strong>android:text="@={viewModel.usernameLiveData}"</strong> />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/field_password"
    style="@style/TextInputLayoutTheme"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:errorEnabled="true"
    <strong>app:error="@{viewModel.passwordValidator.error}"</strong>
    app:layout_constraintEnd_toEndOf="parent"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toBottomOf="@+id/field_username">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        <strong>android:text="@={viewModel.passwordLiveData}"</strong> />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.button.MaterialButton
    android:id="@+id/button_log_in"
    android:layout_width="match_parent"
    android:layout_height="60dp"
     android:layout_marginTop="100dp"
    <strong>android:enabled="@{viewModel.isLoginFormValidMediator}"</strong>
    android:text="Log in"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/field_password"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<br>

الخلاصة

أفضّل هذه الطريقة لأنها تجنّبني كتابة تعابير if أو when طويلة كلما كان لدي نموذج ضخم بقواعد تحقق كثيرة. وإن كنتم تحبون استعراض مهاراتكم الإبداعية، فجرّبوها في مشروعكم القادم، وفكّروا في الانضمام إلى فريقنا في Oozou.

Related Articles

كيف أصمّم برمجيات؟ دليل المبتدئين لأصحاب الأعمال

كيف أصمّم برمجيات؟ دليل المبتدئين لأصحاب الأعمال

تصميم البرمجيات عملية ممتعة لكنها معقدة، تتطلب الإبداع والتفكير المنطقي ومهارات حل المشكلات.

اقرأوا المزيد
مستقبل الحوسبة الطرفية: ما تحتاج الشركات إلى معرفته

مستقبل الحوسبة الطرفية: ما تحتاج الشركات إلى معرفته

يتطور المشهد الرقمي بسرعة، وعلى الشركات أن تتكيّف لتواكب الطلب المتزايد على السرعة والكفاءة والمعالجة في الوقت الفعلي.

اقرأوا المزيد
مستقبل قطاع التجزئة: الذكاء الاصطناعي والواقع المعزّز واتجاهات التحوّل الرقمي

مستقبل قطاع التجزئة: الذكاء الاصطناعي والواقع المعزّز واتجاهات التحوّل الرقمي

يتطور قطاع التجزئة بوتيرة غير مسبوقة بفضل الذكاء الاصطناعي (AI) والواقع المعزّز (AR) وتقنيات التحوّل الرقمي. فلم تعد المتاجر التقليدية مجرد أماكن لبيع المنتجات، بل أصبحت متاجر ذكية وتفاعلية.

اقرأوا المزيد

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life with our expertise in web and mobile development.

ابدأوا المحادثةhello@oozou.com
OOZOU Logo

بانكوك · سنغافورة · هونغ كونغ

X
Facebook
Instagram
LinkedIn
OOZOU Co., Ltd.
548 One City Centre
Ploenchit Road
Bangkok 10330
Thailand
+66 2544 0000
hello@oozou.com

الخدمات

  • تطوير الويب
  • وكلاء الذكاء الاصطناعي والذكاء الاصطناعي التوليدي
  • تطوير تطبيقات الجوال
  • تحليلات البيانات وهندستها
  • تصميم تجربة المستخدم وواجهته (UX/UI) وتصميم المنتجات
  • التحوّل الرقمي

الشركة

  • من نحن
  • الوظائف
  • المدونة
  • دراسات الحالة
  • عروض حية
  • تعلّمت اليوم
  • تواصل معنا

المزيد

  • القطاعات
  • شبكة الشركاء
© 2026 OOZOU. جميع الحقوق محفوظة.
سياسة الخصوصيةمدونة قواعد السلوكسياسة مكافحة الرشوة والفساد