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 — прилагођени сетер за атрибуте 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 за 30–50% захваљујући премештању логике повезивања у XML. Број грешака везаних за нетачан тип View (ClassCastException при findViewById()) пада на нулу, јер се сви типови проверавају у фази компилације.

Data Binding vs ViewBinding: шта изабрати

ViewBinding — лакша алтернатива Data Binding, појављена у Android Studio 3.6 (2020). ViewBinding генерише Binding класу за сваки layout фајл, али без подршке за изразе, променљиве и реактивност. Поређење по кључним критеријумима:

Критеријум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 аутоматски позива сетер извора. Важно: двосмерно повезивање ради са атрибутима за које је дефинисан @InverseBindingAdapter. Android пружа уграђене адаптере за: text, checked, visibility, progress, rating и друге стандардне атрибуте.

@BindingAdapter: прилагођени атрибути и конвертори

@BindingAdapter — анотација за Kotlin extension функције, која омогућава дефинисање прилагођене логике повезивања за било који атрибут 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 обрађује до 60 кадрова у секунди при скроловању RecyclerView, јер асинхроно учитавање не блокира 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 су у фази компилације (обрада израза). У runtime разлика између Data Binding и findViewById не постоји за већину екрана. За RecyclerView са хиљадама елемената, ViewBinding може бити бржи због мање количине генерисаног кода.

Како отклонити грешку Data Binding?

Data Binding компилује изразе у код у фази изградње — грешке се приказују у Build Output као Compilation errors са наведеном линијом XML. Типичне грешке: нетачан тип променљиве, null-безбедност (користите ?? за подразумеване вредности), недостатак импорта класе. Укључите buildFeatures.dataBinding = true у build.gradle (app) и проверите да је <layout> коренска XML ознака. За отклањање runtime израза користите BindingConversion и логирање у BindingAdapter.

Шта је BindingConversion?

@BindingConversion — анотација за статичке методе, која аутоматски конвертује типове у 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 са подацима кроз изразе @{} и @={}.
  • Генерише type-safe Binding класе, елиминишући findViewById() и ClassCastException.
  • ViewBinding — лака алтернатива без израза; Data Binding бирајте за реактивне и сложене UI.
  • Двосмерно повезивање @={} — аутоматска синхронизација View ↔ ViewModel за форме.
  • @BindingAdapter — прилагођени атрибути (учитавање слика, форматирање, видљивост).
  • Data Binding изворно подржава LiveData и StateFlow кроз lifecycleOwner.
  • Data Binding смањује UI код за 30–50% и своди број грешака повезивања на нулу.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође