Data Binding — الأساسيات، الربط ثنائي الاتجاه في Android

المؤلف: IT Sectr نُشر: 2026-02-20 وقت القراءة: 8 دق

Data Binding — مكتبة Android Jetpack التي تربط مكونات واجهة المستخدم من تخطيطات XML بمصادر البيانات في كود التطبيق من خلال صيغة تعريفية. نشرح الأساسيات: Data Binding يلغي كود findViewById() المتكرر ويسمح بتحديث واجهة المستخدم تلقائيًا عند تغير البيانات. وفقًا لـ Google (Android Developers، 2025)، يُستخدم Data Binding في 45% من مشاريع Android، وبالاقتران مع LiveData أو StateFlow يوفر ربطًا تفاعليًا بالكامل دون إدارة يدوية للاشتراكات.

النقاط الرئيسية

  • Data Binding — مكتبة Jetpack للربط التعريفي لتخطيطات XML بمصادر البيانات عبر علامات التخطيط.
  • الربط ثنائي الاتجاه — مزامنة تلقائية للبيانات بين View وViewModel عبر @={} في XML.
  • @BindingAdapter — أداة تعيين مخصصة لخصائص View تعمل على توسيع Data Binding القياسي.
  • Data Binding + LiveData — تفاعلية بدون Observer: تغييرات LiveData تحدث واجهة المستخدم تلقائيًا.
  • Data Binding يقوم بإنشاء كلاس Binding (مثل ActivityMainBinding) في وقت الترجمة، مما يوفر وصولًا type-safe إلى Views.

ما هو Data Binding في Android؟

Data Binding هي مكتبة دعم (Android Jetpack) ظهرت لأول مرة في 2015 في Google I/O وتم تثبيتها في Android Gradle Plugin 1.5. تسمح بربط مكونات واجهة المستخدم في XML بمصادر البيانات (POJO، ViewModel، LiveData) مباشرة في التخطيط، دون استدعاء findViewById() في كود Activity أو Fragment.

مبدأ العمل: يتم تغليف تخطيط XML في علامة <layout>، والتي تعلن عن <variable> بنوع بيانات. داخل التخطيط، يتم إدراج البيانات من خلال تعبيرات بين الأقواس المتعرجة @{}. في وقت الترجمة، يقوم Android Gradle Plugin بإنشاء كلاس Binding (مثل ActivityMainBinding) يحتوي على مراجع مباشرة للـ Views بأنواع صحيحة وطرق لتعيين البيانات.

وفقًا لاستطلاع Android Developers (2024)، يقلل Data Binding من حجم كود واجهة المستخدم في Activity/Fragment بنسبة 30–50% عن طريق نقل منطق الربط إلى XML. عدد الأخطاء المتعلقة بأنواع View غير الصحيحة (ClassCastException مع findViewById()) ينخفض إلى الصفر، حيث يتم التحقق من جميع الأنواع في وقت الترجمة.

Data Binding مقابل ViewBinding: أيهما تختار

ViewBinding هو بديل أخف لـ Data Binding، تم تقديمه في Android Studio 3.6 (2020). ViewBinding ينشئ كلاس Binding لكل ملف تخطيط، ولكن بدون دعم للتعبيرات والمتغيرات والتفاعلية. مقارنة حسب المعايير الرئيسية:

المعيارData BindingViewBinding
إنشاء كلاس Bindingنعمنعم
التعبيرات في XML (@{})نعملا
الربط ثنائي الاتجاهنعملا
تفاعلي (LiveData)نعملا
@BindingAdapterنعملا
سرعة الترجمةأبطأ (معالجة التعبيرات)أسرع
التعقيدعالٍمنخفض

توصية Google (Android Developers، 2025): لمعظم المشاريع، ViewBinding كافٍ — فهو يوفر وصولًا type-safe إلى Views دون عبء Data Binding. اختر Data Binding إذا كنت بحاجة إلى: (1) ربط تفاعلي مع LiveData/StateFlow من XML، (2) ربط ثنائي الاتجاه للنماذج، (3) BindingAdapter لخصائص مخصصة، (4) تعبيرات في XML للتنسيق. في IT Sectr، نستخدم ViewBinding للشاشات البسيطة وData Binding للنماذج ولوحات المعلومات المعقدة.

الربط ثنائي الاتجاه: @{} و @={}

الربط أحادي الاتجاه (@{}) ينقل البيانات من المصدر (ViewModel) إلى View. الربط ثنائي الاتجاه (@={}) يزامن البيانات في كلا الاتجاهين: التغييرات في View (إدخال النص، تبديل Switch) تحدث المصدر تلقائيًا.

xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.app.LoginViewModel" />
    </data>

    <LinearLayout ...>
        <!-- اتجاه واحد: بيانات من ViewModel إلى TextView -->
        <TextView
            android:text="@{viewModel.userName}" />

        <!-- اتجاهان: تغييرات EditText → ViewModel، ViewModel → EditText -->
        <EditText
            android:text="@{=viewModel.email}" />

        <CheckBox
            android:checked="@{=viewModel.agreeToTerms}" />
    </LinearLayout>
</layout>

للربط ثنائي الاتجاه، يجب أن يستخدم ViewModel ObservableField أو LiveData أو StateFlow. عندما تتغير البيانات عبر إدخال المستخدم، يقوم Data Binding تلقائيًا باستدعاء setter المصدر. مهم: الربط ثنائي الاتجاه يعمل مع الخصائص التي لها @InverseBindingAdapter محدد. يوفر Android محولات مضمنة لـ: text، checked، visibility، progress، rating وخصائص قياسية أخرى.

@BindingAdapter: الخصائص المخصصة والمحولات

@BindingAdapter هو تعليق توضيحي لدوال توسيع Kotlin يسمح بتعريف منطق ربط مخصص لأي خاصية View. على سبيل المثال، تحميل صورة عبر Glide عند تحديد URL في XML، أو تنسيق تاريخ عند الربط مع TextView.

kotlin
// BindingAdapter لتحميل الصورة عبر URL
@BindingAdapter("imageUrl")
fun ImageView.setImageUrl(url: String?) {
    Glide.with(this.context)
        .load(url)
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .into(this)
}

// BindingAdapter بخصائص متعددة
@BindingAdapter("visibleGone")
fun View.setVisibleGone(visible: Boolean) {
    visibility = if (visible) View.VISIBLE else View.GONE
}

// BindingAdapter مع محول (formatDate)
@BindingAdapter("formattedDate")
fun TextView.setFormattedDate(timestamp: Long) {
    text = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(Date(timestamp))
}
xml
<!-- استخدام BindingAdapter في XML -->
<ImageView
    imageUrl="@{user.avatarUrl}"
    android:layout_width="48dp"
    android:layout_height="48dp" />

<TextView
    formattedDate="@{message.createdAt}"
    visibleGone="@{message.isVisible}" />

@BindingAdapter يمكنه قبول خصائص متعددة (requireAll = true/false)، مما يسمح بدمج القيم. على سبيل المثال، @BindingAdapter("imageUrl"، "circleCrop") — إذا كان circleCrop صحيحًا، يطبق Glide تحويل CircleCrop. وفقًا لـ Google (Android Performance، 2024)، يعالج BindingAdapter مع Glide في Data Binding ما يصل إلى 60 إطارًا في الثانية عند تمرير RecyclerView، حيث أن التحميل غير المتزامن لا يحظر خيط واجهة المستخدم.

Data Binding مع LiveData وStateFlow

Data Binding يدعم LiveData بشكل أصلي منذ Android Architecture Components 1.0. إذا كان المتغير في التخطيط من نوع LiveData، فإن Binding يشترك تلقائيًا فيه ويحدث واجهة المستخدم عندما تتغير القيمة. للتشغيل الصحيح، يجب تعيين LifecycleOwner في كلاس Binding: binding.lifecycleOwner = viewLifecycleOwner.

kotlin
// ViewModel مع LiveData
class WeatherViewModel : ViewModel() {
    private val _temperature = MutableLiveData("--")
    val temperature: LiveData<String> get() = _temperature

    val cityName = MutableLiveData("موسكو")
    val weatherIcon = MutableLiveData(R.drawable.ic_sunny)

    fun refresh() {
        viewModelScope.launch {
            _temperature.value = weatherRepository.getTemperature()
        }
    }
}

// في Fragment:
val binding = FragmentWeatherBinding.inflate(inflater, container, false)
binding.viewModel = weatherViewModel
binding.lifecycleOwner = viewLifecycleOwner  // ← مطلوب لـ LiveData
xml
<layout>
    <data>
        <variable name="viewModel" type="com.example.app.WeatherViewModel" />
    </data>

    <LinearLayout ...>
        <TextView
            android:text="@string/temperature_format(viewModel.temperature)" />
        <TextView android:text="@{viewModel.cityName}" />
        <ImageView
            android:src="@{viewModel.weatherIcon}"
            contentDescription="@{viewModel.cityName}" />
    </LinearLayout>
</layout>

Data Binding يدعم StateFlow بدءًا من lifecycle 2.5.0 عبر Flow.asLiveData() أو التحويل المباشر. عند استخدام StateFlow في Data Binding، تأكد من تعيين دورة الحياة عبر binding.lifecycleOwner. بدون تعيين LifecycleOwner، لن يقوم LiveData/StateFlow بتحديث واجهة المستخدم لأن Binding لا يعرف متى يكون المشترك نشطًا.

أمثلة كود: Data Binding في Kotlin وXML

مثال 1: شاشة الملف الشخصي مع Data Binding

شاشة ملف شخصي كاملة مع صورة رمزية واسم وسيرة وزر تحرير. يستخدم ViewModel ObservableField للتفاعلية.

xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable name="profile" type="com.example.app.ProfileViewModel" />
    </data>

    <androidx.constraintlayout...>
        <ImageView
            app:imageUrl="@{profile.avatarUrl}"
            android:contentDescription="@{profile.name}" />

        <TextView
            android:text="@{profile.name}"
            android:textStyle="bold" />

        <TextView
            android:text="@{profile.bio}"
            android:visibility="@{profile.hasBio ? View.VISIBLE : View.GONE}" />

        <Button
            android:onClick="@{() -> profile.onEdit()}"
            android:text="@string/edit" />
    </androidx.constraintlayout...>
</layout>
kotlin
class ProfileViewModel : ViewModel() {
    val name = ObservableField("آنا بيتروفا")
    val bio = ObservableField("مطور Android، خبرة 5 سنوات")
    val avatarUrl = ObservableField("https://example.com/avatar.jpg")
    val hasBio = ObservableBoolean(true)

    fun onEdit() {
        // منطق تحرير الملف الشخصي
    }
}

// في Fragment:
val binding = FragmentProfileBinding.inflate(inflater, container, false)
binding.profile = profileViewModel
binding.lifecycleOwner = viewLifecycleOwner

مثال 2: نموذج تسجيل الدخول مع الربط ثنائي الاتجاه

نموذج تسجيل دخول مع التحقق من الحقول وزر الدخول. الربط ثنائي الاتجاه (@={}) يزامن إدخال المستخدم مع ViewModel.

xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="login" type="com.example.app.LoginViewModel" />
    </data>

    <LinearLayout ...>
        <TextInputLayout>
            <TextInputEditText
                android:text="@{=login.email}"
                android:hint="@string/email_hint" />
        </TextInputLayout>

        <TextInputLayout>
            <TextInputEditText
                android:text="@{=login.password}"
                android:inputType="textPassword" />
        </TextInputLayout>

        <Button
            android:onClick="@{() -> login.onLogin()}"
            android:enabled="@{login.isValid}"
            android:text="@string/login" />

        <ProgressBar
            android:visibility="@{login.isLoading ? View.VISIBLE : View.GONE}" />
    </LinearLayout>
</layout>

في XML، تُستخدم التعبيرات: @{login.isValid} لحالة الزر (مفعل/غير مفعل)، @{login.isLoading ? View.VISIBLE : View.GONE} لمؤشر التحميل، @{=login.email} للمزامنة ثنائية الاتجاه. كل منطق التحقق موجود في ViewModel؛ View يعرض الحالة فقط. وفقًا لـ Google (Android Guide، 2025)، يقلل هذا النهج من عدد أخطاء منطق واجهة المستخدم بنسبة 50–60%.

الأسئلة المتداولة

هل يمكن استخدام Data Binding مع Compose؟

لا، Jetpack Compose هو نظام واجهة مستخدم مستقل بآلية التفاعل الخاصة به (دوال Composable + State). Data Binding مصمم حصريًا لتخطيطات XML وغير متوافق مع Compose. عند الترحيل من XML إلى Compose، لا يُستخدم Data Binding — بدلاً من ذلك يتم تطبيق mutableStateOf() و collectAsState() و remember. يظل Data Binding مناسبًا فقط للمشاريع التي تحتفظ بتخطيطات XML.

هل Data Binding أبطأ من findViewById؟

في مرحلة الربط الأولى، يقوم Data Binding بالبحث عن View بواسطة المعرف (مثل findViewById). الفرق غير محسوس للمستخدم: شاشة نموذجية بـ 20–30 View يتم ربطها في 1–3 مللي ثانية. العبء الرئيسي لـ Data Binding يكون في وقت الترجمة (معالجة التعبيرات). في وقت التشغيل، لا يوجد فرق بين Data Binding و findViewById لمعظم الشاشات. بالنسبة لـ RecyclerView بآلاف العناصر، قد يكون ViewBinding أسرع بسبب كمية الكود الأقل المولدة.

كيفية تصحيح خطأ Data Binding؟

يقوم Data Binding بترجمة التعبيرات إلى كود في وقت البناء — تظهر الأخطاء في Build Output كـ Compilation errors مع الإشارة إلى سطر XML. الأخطاء النموذجية: نوع متغير غير صحيح، مشكلات أمان القيمة الخالية (استخدم ?? للقيم الافتراضية)، عدم استيراد الكلاس. فعّل buildFeatures.dataBinding = true في build.gradle (app) وتحقق من أن <layout> هو العلامة الجذرية لـ XML. لتصحيح التعبيرات في وقت التشغيل، استخدم BindingConversion والتسجيل في BindingAdapter.

ما هو BindingConversion؟

@BindingConversion هو تعليق توضيحي للطرق الثابتة التي تحول الأنواع تلقائيًا في تعبيرات Data Binding. على سبيل المثال، تحويل Color Int إلى ColorDrawable: @BindingConversion fun colorToDrawable(color: Int): ColorDrawable = ColorDrawable(color). بعد ذلك، سيعمل android:background="@{color.red}" تلقائيًا. BindingConversions عامة — يتم تطبيقها على جميع تعبيرات Binding في المشروع.

هل يجب إيقاف Data Binding لإصدارات release؟

لا، Data Binding يعمل في إصدارات release تمامًا كما في debug. قد يحذف تحسين ProGuard/R8 كلاسات Binding إذا لم تُستخدم مباشرة — أضف القاعدة: -keep class * extends android.databinding.ViewDataBinding { *; }. بدءًا من Android Gradle Plugin 7.0، يعالج R8 Data Binding بشكل صحيح دون قواعد إضافية. إيقاف Data Binding للإصدار release لا يحسن الأداء، لكنه يكسر جميع الشاشات التي تستخدمه.

الخلاصة

  • Data Binding — مكتبة Jetpack للربط التعريفي لـ XML بالبيانات عبر تعبيرات @{} و @={}.
  • ينشئ كلاسات Binding type-safe، مما يلغي findViewById() و ClassCastException.
  • ViewBinding — بديل خفيف بدون تعبيرات؛ اختر Data Binding لواجهات المستخدم التفاعلية والمعقدة.
  • الربط ثنائي الاتجاه @={} — مزامنة تلقائية View ↔ ViewModel للنماذج.
  • @BindingAdapter — خصائص مخصصة (تحميل الصور، التنسيق، الرؤية).
  • Data Binding يدعم بشكل أصلي LiveData و StateFlow عبر lifecycleOwner.
  • Data Binding يقلل كود واجهة المستخدم بنسبة 30–50% ويقلص أخطاء الربط إلى الصفر.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا