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 за 30–50% захваљујући премештању логике повезивања у XML. Број грешака везаних за нетачан тип View (ClassCastException при findViewById()) пада на нулу, јер се сви типови проверавају у фази компилације.
ViewBinding — лакша алтернатива Data Binding, појављена у Android Studio 3.6 (2020). ViewBinding генерише Binding класу за сваки layout фајл, али без подршке за изразе, променљиве и реактивност. Поређење по кључним критеријумима:
| Критеријум | 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 аутоматски позива сетер извора. Важно: двосмерно повезивање ради са атрибутима за које је дефинисан @InverseBindingAdapter. Android пружа уграђене адаптере за: text, checked, visibility, progress, rating и друге стандардне атрибуте.
@BindingAdapter — анотација за Kotlin extension функције, која омогућава дефинисање прилагођене логике повезивања за било који атрибут 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 обрађује до 60 кадрова у секунди при скроловању RecyclerView, јер асинхроно учитавање не блокира 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 су у фази компилације (обрада израза). У runtime разлика између Data Binding и findViewById не постоји за већину екрана. За RecyclerView са хиљадама елемената, ViewBinding може бити бржи због мање количине генерисаног кода.
Data Binding компилује изразе у код у фази изградње — грешке се приказују у Build Output као Compilation errors са наведеном линијом XML. Типичне грешке: нетачан тип променљиве, null-безбедност (користите ?? за подразумеване вредности), недостатак импорта класе. Укључите buildFeatures.dataBinding = true у build.gradle (app) и проверите да је <layout> коренска XML ознака. За отклањање runtime израза користите BindingConversion и логирање у BindingAdapter.
@BindingConversion — анотација за статичке методе, која аутоматски конвертује типове у 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 креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође