Data Binding — مبانی، اتصال دوطرفه در Android

نویسنده: IT Sectr منتشر شده: 2026-02-20 زمان مطالعه: 8 دقیقه

Data Binding — کتابخانه Android Jetpack که کامپوننت‌های UI از طرح‌بندی XML را با منابع داده در کد برنامه از طریق نحو اعلانی پیوند می‌دهد. مبانی را توضیح می‌دهیم: Data Binding کدهای تکراری findViewById() را حذف می‌کند و به UI اجازه می‌دهد هنگام تغییر داده به‌طور خودکار به‌روزرسانی شود. طبق داده‌های Google (Android Developers, 2025)، Data Binding در 45% پروژه‌های Android استفاده می‌شود و در ترکیب با LiveData یا StateFlow اتصال کاملاً واکنش‌گرا بدون مدیریت دستی اشتراک‌ها فراهم می‌کند.

نکات اصلی

  • Data Binding — کتابخانه Jetpack برای اتصال اعلانی طرح‌بندی XML با منابع داده از طریق برچسب‌های layout.
  • اتصال دوطرفه — همگام‌سازی خودکار داده بین View و ViewModel از طریق @={} در XML.
  • @BindingAdapter — setter سفارشی برای ویژگی‌های View که امکان گسترش Data Binding استاندارد را فراهم می‌کند.
  • Data Binding + LiveData — واکنش‌گرایی بدون Observer: تغییرات LiveData به‌طور خودکار UI را به‌روزرسانی می‌کنند.
  • Data Binding کلاس Binding (مثلاً ActivityMainBinding) را در مرحله کامپایل تولید می‌کند و دسترسی type-safe به View را تضمین می‌کند.

Data Binding در Android چیست؟

Data Binding — کتابخانه پشتیبانی (Android Jetpack) که در سال 2015 در Google I/O معرفی و در Android Gradle Plugin 1.5 تثبیت شد. این کتابخانه امکان پیوند کامپوننت‌های UI در XML با منابع داده (POJO، ViewModel، LiveData) را مستقیماً در طرح‌بندی، بدون فراخوانی findViewById() در کد Activity یا Fragment فراهم می‌کند.

اصل کار: طرح‌بندی XML در برچسب <layout> قرار می‌گیرد که در آن یک متغیر <variable> با نوع داده اعلان می‌شود. در داخل طرح‌بندی، داده‌ها از طریق عبارات در آکولاد @{} جایگزین می‌شوند. در مرحله کامپایل، Android Gradle Plugin کلاس Binding (مثلاً ActivityMainBinding) را تولید می‌کند که شامل ارجاعات مستقیم به View با انواع صحیح و روش‌هایی برای تنظیم داده است.

طبق نظرسنجی Android Developers (2024)، Data Binding حجم کد UI در Activity/Fragment را با انتقال منطق اتصال به XML به میزان 30–50% کاهش می‌دهد. تعداد خطاهای مربوط به نوع نادرست View (ClassCastException در findViewById()) به صفر می‌رسد، زیرا همه انواع در مرحله کامپایل بررسی می‌شوند.

Data Binding در مقابل ViewBinding: کدام را انتخاب کنیم

ViewBinding — جایگزین سبک‌تر Data Binding که در Android Studio 3.6 (2020) ظاهر شد. ViewBinding برای هر فایل layout کلاس Binding تولید می‌کند، اما بدون پشتیبانی از عبارات، متغیرها و واکنش‌گرایی. مقایسه بر اساس معیارهای کلیدی:

معیارData BindingViewBinding
تولید کلاس Bindingبلهبله
عبارات در XML (@{})بلهخیر
اتصال دوطرفهبلهخیر
واکنش‌گرا (LiveData)بلهخیر
@BindingAdapterبلهخیر
سرعت کامپایلکندتر (پردازش عبارات)سریع‌تر
پیچیدگیزیادکم

توصیه Google (Android Developers, 2025): برای بیشتر پروژه‌ها ViewBinding کافی است — دسترسی type-safe به View بدون سربار 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 — annotation برای توابع توسعه 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 true باشد، Glide تبدیل CircleCrop را اعمال می‌کند. طبق Google (Android Performance, 2024)، BindingAdapter با Glide در Data Binding هنگام اسکرول RecyclerView تا 60 فریم در ثانیه پردازش می‌کند، زیرا بارگذاری ناهمگام thread UI را مسدود نمی‌کند.

Data Binding با LiveData و StateFlow

Data Binding به صورت بومی از LiveData پشتیبانی می‌کند از نسخه Android Architecture Components 1.0. اگر متغیر در layout از نوع LiveData باشد، Binding به‌طور خودکار در آن مشترک می‌شود و UI را هنگام تغییر مقدار به‌روزرسانی می‌کند. برای کار صحیح باید 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 UI را به‌روزرسانی نمی‌کنند، زیرا 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)، این رویکرد تعداد باگ‌ها در منطق UI را 50–60% کاهش می‌دهد.

سوالات متداول

آیا می‌توان از Data Binding با Compose استفاده کرد؟

خیر، Jetpack Compose یک سیستم UI مستقل با مکانیزم واکنش‌گرایی خود (توابع Composable + State) است. Data Binding منحصراً برای طرح‌بندی XML طراحی شده و با Compose ناسازگار است. هنگام مهاجرت از XML به Compose از Data Binding استفاده نمی‌شود — به جای آن از mutableStateOf()، collectAsState() و remember استفاده می‌شود. Data Binding فقط برای پروژه‌هایی که طرح‌بندی XML را حفظ می‌کنند مناسب باقی می‌ماند.

آیا Data Binding از findViewById کندتر است؟

در مرحله اتصال اول، Data Binding View را با ID جستجو می‌کند (مانند findViewById). تفاوت برای کاربر نامحسوس است: یک صفحه معمولی با 20–30 View در 1–3 ms متصل می‌شود. سربار اصلی Data Binding در مرحله کامپایل است (پردازش عبارات). در زمان اجرا تفاوت بین Data Binding و findViewById برای بیشتر صفحه‌ها وجود ندارد. برای RecyclerView با هزاران عنصر، ViewBinding به دلیل کد تولیدی کمتر می‌تواند سریع‌تر باشد.

چگونه خطای Data Binding را اشکال‌زدایی کنیم؟

Data Binding عبارات را در مرحله ساخت به کد کامپایل می‌کند — خطاها در Build Output به عنوان Compilation errors با مشخص کردن خط XML نمایش داده می‌شوند. خطاهای معمول: نوع متغیر نادرست، null-safety (برای مقادیر پیش‌فرض از ?? استفاده کنید)، عدم import کلاس. buildFeatures.dataBinding = true را در build.gradle (app) فعال کنید و بررسی کنید که <layout> برچسب ریشه XML است. برای اشکال‌زدایی عبارات زمان اجرا از BindingConversion و لاگ‌گیری در BindingAdapter استفاده کنید.

BindingConversion چیست؟

@BindingConversion — annotation برای متدهای ایستا که به‌طور خودکار انواع را در عبارات Data Binding تبدیل می‌کند. مثلاً تبدیل Color Int به ColorDrawable: @BindingConversion fun colorToDrawable(color: Int): ColorDrawable = ColorDrawable(color). پس از آن android:background="@{color.red}" به‌طور خودکار کار می‌کند. BindingConversion جهانی هستند — برای تمام عبارات 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 را برای UIهای واکنش‌گرا و پیچیده انتخاب کنید.
  • اتصال دوطرفه @={} — همگام‌سازی خودکار View ↔ ViewModel برای فرم‌ها.
  • @BindingAdapter — ویژگی‌های سفارشی (بارگذاری تصاویر، قالب‌بندی، visibility).
  • Data Binding به صورت بومی از LiveData و StateFlow از طریق lifecycleOwner پشتیبانی می‌کند.
  • Data Binding کد UI را 30–50% کاهش می‌دهد و تعداد باگ‌های اتصال را به صفر می‌رساند.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید