Data Binding — کتابخانه Android Jetpack که کامپوننتهای UI از طرحبندی XML را با منابع داده در کد برنامه از طریق نحو اعلانی پیوند میدهد. مبانی را توضیح میدهیم: Data Binding کدهای تکراری findViewById() را حذف میکند و به UI اجازه میدهد هنگام تغییر داده بهطور خودکار بهروزرسانی شود. طبق دادههای Google (Android Developers, 2025)، Data Binding در 45% پروژههای Android استفاده میشود و در ترکیب با LiveData یا StateFlow اتصال کاملاً واکنشگرا بدون مدیریت دستی اشتراکها فراهم میکند.
نکات اصلی
@={} در XML.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()) به صفر میرسد، زیرا همه انواع در مرحله کامپایل بررسی میشوند.
ViewBinding — جایگزین سبکتر Data Binding که در Android Studio 3.6 (2020) ظاهر شد. ViewBinding برای هر فایل layout کلاس Binding تولید میکند، اما بدون پشتیبانی از عبارات، متغیرها و واکنشگرایی. مقایسه بر اساس معیارهای کلیدی:
| معیار | Data Binding | ViewBinding |
|---|---|---|
| تولید کلاس 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) بهطور خودکار منبع را بهروزرسانی میکند.
<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 — annotation برای توابع توسعه Kotlin که امکان تعریف منطق اتصال سفارشی برای هر ویژگی View را فراهم میکند. مثلاً بارگذاری تصویر از طریق Glide هنگام مشخص کردن URL در XML، یا قالببندی تاریخ هنگام اتصال با TextView.
// 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))
}
<!-- استفاده از 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 پشتیبانی میکند از نسخه Android Architecture Components 1.0. اگر متغیر در layout از نوع LiveData باشد، Binding بهطور خودکار در آن مشترک میشود و UI را هنگام تغییر مقدار بهروزرسانی میکند. برای کار صحیح باید LifecycleOwner در کلاس Binding تنظیم شود: binding.lifecycleOwner = viewLifecycleOwner.
// 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
<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 نمیداند چه زمانی مشترک فعال است.
صفحه کامل پروفایل با آواتار، نام، بیو و دکمه ویرایش. ViewModel از ObservableField برای واکنشگرایی استفاده میکند.
<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>
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
فرم ورود با اعتبارسنجی فیلدها و دکمه ورود. اتصال دوطرفه (@={}) ورود کاربر را با ViewModel همگامسازی میکند.
<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% کاهش میدهد.
سوالات متداول
خیر، Jetpack Compose یک سیستم UI مستقل با مکانیزم واکنشگرایی خود (توابع Composable + State) است. Data Binding منحصراً برای طرحبندی XML طراحی شده و با Compose ناسازگار است. هنگام مهاجرت از XML به Compose از Data Binding استفاده نمیشود — به جای آن از mutableStateOf()، collectAsState() و remember استفاده میشود. Data Binding فقط برای پروژههایی که طرحبندی XML را حفظ میکنند مناسب باقی میماند.
در مرحله اتصال اول، Data Binding View را با ID جستجو میکند (مانند findViewById). تفاوت برای کاربر نامحسوس است: یک صفحه معمولی با 20–30 View در 1–3 ms متصل میشود. سربار اصلی Data Binding در مرحله کامپایل است (پردازش عبارات). در زمان اجرا تفاوت بین Data Binding و findViewById برای بیشتر صفحهها وجود ندارد. برای RecyclerView با هزاران عنصر، ViewBinding به دلیل کد تولیدی کمتر میتواند سریعتر باشد.
Data Binding عبارات را در مرحله ساخت به کد کامپایل میکند — خطاها در Build Output به عنوان Compilation errors با مشخص کردن خط XML نمایش داده میشوند. خطاهای معمول: نوع متغیر نادرست، null-safety (برای مقادیر پیشفرض از ?? استفاده کنید)، عدم import کلاس. buildFeatures.dataBinding = true را در build.gradle (app) فعال کنید و بررسی کنید که <layout> برچسب ریشه XML است. برای اشکالزدایی عبارات زمان اجرا از BindingConversion و لاگگیری در BindingAdapter استفاده کنید.
@BindingConversion — annotation برای متدهای ایستا که بهطور خودکار انواع را در عبارات Data Binding تبدیل میکند. مثلاً تبدیل Color Int به ColorDrawable: @BindingConversion fun colorToDrawable(color: Int): ColorDrawable = ColorDrawable(color). پس از آن android:background="@{color.red}" بهطور خودکار کار میکند. BindingConversion جهانی هستند — برای تمام عبارات Binding در پروژه اعمال میشوند.
خیر، Data Binding در نسخه release مانند debug کار میکند. بهینهسازی ProGuard/R8 ممکن است کلاسهای Binding را حذف کند اگر مستقیماً استفاده نشوند — قانون زیر را اضافه کنید: -keep class * extends android.databinding.ViewDataBinding { *; }. از Android Gradle Plugin 7.0 به بعد، R8 Data Binding را بدون قوانین اضافی به درستی پردازش میکند. غیرفعال کردن Data Binding برای release افزایش عملکرد نمیدهد، اما تمام صفحههایی که از آن استفاده میکنند را خراب میکند.
خلاصه
@{} و @={}.@={} — همگامسازی خودکار View ↔ ViewModel برای فرمها.lifecycleOwner پشتیبانی میکند.ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید