Two-Way Binding: co to je, obousměrné vázání v Android a iOS

Autor: IT Sectr Publikováno: 2026-02-20 Doba čtení: 12 min

Zjistěte, co je Two-Way Binding — obousměrné vázání dat, které automaticky synchronizuje model a zobrazení v mobilních aplikacích. Na rozdíl od ruční aktualizace UI pomocí findViewById, mechanismus vázání aktualizuje model při změně vstupu uživatele a zobrazení při změně dat. Podle Google I/O 2024 snižuje vázání šablonový UI kód o 30–50% v projektech Android a iOS. Tento přístup se uplatňuje v frameworkách — od Jetpack Compose a SwiftUI po Flutter a React Native.

Hlavní body

  • Two-Way Binding — mechanismus, který automaticky synchronizuje data mezi modelem (ViewModel) a zobrazením (View) v obou směrech.
  • V Android se implementuje pomocí @BindingAdapter a @= v DataBinding, v iOS — pomocí @Binding v SwiftUI.
  • Podle Google, DataBinding snižuje objem UI kódu o 30–50% ve srovnání s ručním vázáním přes findViewById.
  • Hlavní nebezpečí — nekonečné cykly aktualizace při nesprávném nastavení posluchačů změn.
  • V moderním vývoji je preferován jednosměrný tok (UDF) s explicitními událostmi a Two-Way Binding se používá bodově pro vstupní formuláře.

Co je Two-Way Binding?

Two-Way Binding (obousměrné vázání dat) — architektonický mechanismus, při kterém se změny v datovém modelu automaticky projeví v uživatelském rozhraní a změny v UI okamžitě aktualizují model. Na rozdíl od jednosměrného vázání, kde tok dat jde pouze od modelu k zobrazení, obousměrné vázání vytváří uzavřený synchronizační cyklus bez ručního kódování každé aktualizace.

Podle Android Developers Blog (2023) je knihovna DataBinding, zavedená v roce 2015, používána ve 42% komerčních Android aplikací. Mechanismus je obzvláště žádaný ve vstupních formulářích — textová pole, přepínače, posuvníky a zaškrtávací políčka — kde se vstup uživatele musí okamžitě projevit v modelu a programové změny v UI. Ve všech těchto scénářích programátor napíše jedno vázání místo dvojice „posluchač + setter”.

V IT Sectr jsme používali obousměrné vázání v projektech od roku 2017 a doporučujeme ho používat vědomě: pro jednoduchá vstupní pole, ale ne pro složité stavy se závislostmi.

Jak funguje obousměrné vázání?

Mechanismus Two-Way Binding je postaven na třech klíčových prvcích: pozorovatelné pole (observable), posluchač změn (listener) a mechanismus zpětné synchronizace. Když uživatel zadá text do pole EditText, systém zachytí událost TextWatcher, zapíše novou hodnotu do vázané proměnné a upozorní UI na nutnost překreslení, pokud se proměnná změnila z kódu.

Pod kapotou generuje knihovna DataBinding v Android třídu Binding ve fázi kompilace, která obsahuje veškerou logiku vázání. Pro každé View s atributem @={variable} vzniká pár setter + getter s invalidací. V SwiftUI vykonává podobnou práci propertyWrapper @Binding, který synchronizuje hodnotu prostřednictvím mechanismu Combine. SwiftUI sleduje změny pomocí vlastností @Published a automaticky překresluje View při každé změně vázané proměnné.

Podle WWDC Session 10033 (2023) zpracovává mechanismus @Binding v SwiftUI až 60 snímků za sekundu při synchronizaci vstupních polí, což ho činí vhodným pro interaktivní formuláře bez zpoždění. V obou frameworkách je Two-Way Binding syntaktický cukr nad vzorem Observer, automatizující přihlášení k odběru a oznamování.

Two-Way Binding v Android: DataBinding a Jetpack Compose

V Android je obousměrné vázání k dispozici ve dvou variantách: klasický XML-DataBinding pomocí atributu @={} a Jetpack Compose pomocí obousměrných odkazů na stav. Oba přístupy řeší stejný úkol — synchronizaci UI a modelu — ale liší se syntaxí a oblastí použití.

DataBinding s @BindingAdapter a @=

V XML značení se obousměrné vázání označuje syntaxí @={variable.property} — rovnítko uvnitř složených závorek ho odlišuje od jednosměrného @{variable}. Pro vlastní View je vyžadována anotace @BindingAdapter s uvedením atributu inverse.

XML
<layout>
    <data>
        <variable name="viewModel" type="com.example.LoginViewModel" />
    </data>
    <EditText
        android:text="@{viewModel.email}" />
    <CheckBox
        android:checked="@{viewModel.agreeToTerms}" />
</layout>

Příklad ukazuje nejjednodušší formulář s emailem a zaškrtávacím políčkem — obě pole používají obousměrné vázání, což eliminuje potřebu psát TextWatcher a OnCheckedChangeListener v kódu Activity. Při změně textu uživatelem se pole viewModel.email automaticky aktualizuje.

Kotlin
@BindingAdapter("app:rating")
fun RatingBar.setRating(rating: Float) {
    if (rating != this.rating) {
        this.rating = rating
    }
}

@InverseBindingAdapter("app:rating")
fun RatingBar.getRating(): Float = this.rating

@BindingAdapter("app:ratingAttrChanged")
fun RatingBar.setListeners(
    listener: InverseBindingListener?
) {
    this.onRatingBarChangeListener =
        RatingBar.OnRatingBarChangeListener { _, _, _ -> listener?.onChange() }
}

Vlastní BindingAdapter pro RatingBar používá dvojici anotací — @BindingAdapter a @InverseBindingAdapter — aby knihovna DataBinding věděla, jak číst hodnotu z View (zpětná vazba) a jak zapisovat do View (přímé vázání). Třetí adaptér s příponou AttrChanged upozorňuje systém na změnu hodnoty uživatelem.

Two-Way Binding v Jetpack Compose

Jetpack Compose nepodporuje syntaxi @={}, ale poskytuje podobný mechanismus prostřednictvím mutableStateOf a explicitního předání funkce setter. Obousměrné vázání v Compose je založeno na předání State a callback funkce (value, onValueChange) podřízeným komponentám.

Kotlin
@Composable
fun LoginScreen() {
    var email by remember { mutableStateOf("") }

    OutlinedTextField(
        value = email,
        onValueChange = { email = it },
        label = { Text("Email") }
    )
}

@Composable
fun CustomRatingBar(
    rating: Float,
    onRatingChange: (Float) -> Unit
) {
    Slider(
        value = rating,
        onValueChange = onRatingChange,
        valueRange = 0f..5f
    )
}

V Compose je obousměrné vázání simulováno pomocí páru state + callback — rodič předá aktuální hodnotu a funkci pro její aktualizaci, podřízená komponenta zavolá callback při interakci uživatele. Tento přístup explicitně ukazuje směr toku dat, což zjednodušuje ladění ve srovnání s implicitní synchronizací DataBinding.

Two-Way Binding v iOS: @Binding v SwiftUI

V SwiftUI je obousměrné vázání realizováno pomocí propertyWrapper @Binding, který vytváří read-write odkaz na zdroj dat patřící nadřazenému View. @Binding neukládá hodnotu samostatně — čte a zapisuje prostřednictvím @State nebo @StateObject rodiče.

Swift
struct LoginView: View {
    @State private var email = ""
    @State private var agreeToTerms = false

    var body: some View {
        Form {
            TextField("Email", text: $email)
            Toggle("Souhlasím s podmínkami", isOn: $agreeToTerms)
            ChildRatingView(rating: $rating)
        }
    }
}

struct ChildRatingView: View {
    @Binding var rating: Double

    var body: some View {
        Slider(value: $rating, in: 0...5)
    }
}

Symbol $ před názvem proměnné vytváří odkaz Binding: $email má typ Binding<String>, nikoli String. SwiftUI automaticky propojuje změnu textu v TextField s aktualizací vlastnosti email prostřednictvím mechanismu Combine. Nadřazené View předá podřízené komponentě Binding na svůj @State, což umožňuje změnu stavu z libovolné úrovně hierarchie bez delegátů nebo callbacků.

Podle Apple WWDC 2023 používá SwiftUI diffing algoritmus k minimalizaci překreslování: pokud se hodnota @Binding změnila, ale View na této hodnotě nezávisí, k překreslení nedojde. To zajišťuje výkon srovnatelný s UIKit (až 120 FPS na displejích ProMotion).

Two-Way Binding versus UDF: kdy co zvolit

Volba mezi obousměrným vázáním a jednosměrným tokem dat (UDF) je jedním z klíčových architektonických rozhodnutí v mobilním vývoji. Two-Way Binding je optimální pro lokální stavy formuláře, kde se každý krok uživatele musí okamžitě projevit v modelu bez dalšího kódu. UDF je preferován pro globální stav aplikace, kde je předvídatelnost změn důležitější než rychlost vývoje.

KritériumTwo-Way BindingUDF
Objem kódu ve formuláři1 řádek (atribut @={})5–7 řádků (State, Intent, Reducer)
Ladění toku datObtížné (kdo změnil — UI nebo kód?)Snadné (všechny změny přes Intent)
VýkonVysoký (nativní synchronizace)Střední (vrstva Reducer + Redux)
ŠkálovatelnostKlesá u složitých formulářů s validacíRoste s počtem obrazovek
Předvídatelnost stavůNízká (vedlejší účinky cyklů)Vysoká (reducer — jediný zdroj pravdy)

Doporučení: používejte Two-Way Binding pro jednoduchá vstupní pole (text, zaškrtávací políčka, přepínače) ve formulářích se 3–5 poli bez složité validace. Pro obrazovky s globálním stavem, síťovými požadavky a závislými poli aplikujte UDF s jednosměrným tokem a explicitním zpracováním událostí. V IT Sectr kombinujeme oba přístupy: Two-Way Binding uvnitř formuláře, UDF pro navigaci a obchodní logiku.

Typické chyby při obousměrném vázání

Nekonečný cyklus aktualizace — nejčastější problém při používání Two-Way Binding. Cyklus vzniká, když změna modelu způsobí aktualizaci UI, která znovu změní model. V DataBinding k tomu dochází, pokud getter v @InverseBindingAdapter vrátí novou hodnotu ihned po volání setter. Řešení — zkontrolujte, zda se hodnota změnila před zpětným zápisem (podmínka guard).

Druhá častá chyba — vázání vypočítávaných polí. Pokud pole závisí na jiném poli (např. celková cena = cena × množství), obousměrné vázání může vést k nekonzistentnímu stavu. Například uživatel změní množství, spustí se přepočet ceny, který znovu změní množství. Pro vypočítávaná pole používejte jednosměrné vázání s Flow nebo Combine.

Třetí chyba — vázání Observable polí bez LifecycleOwner. V Android DataBinding je třeba předat LifecycleOwner do bindingu, jinak pozorovatelé nebudou vyčištěni při zničení Activity, což vede k úniku paměti. Vždy předávejte viewLifecycleOwner ve fragmentech a this v Activity.

Podle Google Issue Tracker (2024) je přibližně 15% hlášení chyb týkajících se DataBinding spojeno s cyklickými aktualizacemi. Pro diagnostiku používejte Android Studio Layout Inspector — zobrazuje aktuální hodnoty všech bindingů na obrazovce, což zjednodušuje nalezení zdroje nekonečného cyklu.

Často kladené otázky

Čím se liší obousměrné vázání od jednosměrného?

Jednosměrné vázání (One-Way Binding) přenáší data pouze od modelu k zobrazení — při změně modelu se UI aktualizuje, ale vstup uživatele model přímo nemění. Two-Way Binding synchronizuje data v obou směrech: změna v UI automaticky aktualizuje model a naopak. V syntaxi DataBinding je rozdíl označen symboly @{} (One-Way) a @={} (Two-Way).

Kdy nepoužívat Two-Way Binding?

Nepoužívejte obousměrné vázání pro složité formuláře se závislými poli, vypočítávanými hodnotami nebo vlastní validací — v těchto scénářích se tok dat stává nepředvídatelným. Vyhněte se mu také v seznamech RecyclerView s velkým počtem prvků, kde každý prvek má binding: výkon klesá kvůli mnoha pozorovatelům. UDF s jednosměrným tokem a zpracováním událostí přes Intent se lépe škáluje.

Podporuje Jetpack Compose obousměrné vázání?

Jetpack Compose nemá vestavěnou syntaxi @={}, ale obousměrná synchronizace je realizována pomocí páru State + callback (onValueChange). Rodič předá aktuální hodnotu (State) a funkci aktualizace, podřízená komponenta zavolá callback při změně. Jedná se o explicitní, nikoli implicitní vázání — tok dat zůstává viditelný a sledovatelný.

Jak ladit nekonečný cyklus v DataBinding?

Pro ladění cyklů v DataBinding použijte Android Studio Layout Inspector — zobrazuje aktuální hodnoty všech vázaných proměnných na obrazovce. Přidejte logování do @InverseBindingAdapter a zkontrolujte, zda getter nevrací hodnotu odlišnou od právě zapsané. Standardní řešení — podmínka guard: if (newValue != currentValue) před zpětným zápisem.

Existuje Two-Way Binding ve Flutter?

Ve Flutter není vestavěné obousměrné vázání, ale je emulováno pomocí kombinace TextEditingController a callbacku onChanged. Pro StatefulWidget se programátor ručně přihlásí k odběru změn kontroléru a aktualizuje model. V Provider a Riverpod je obousměrná synchronizace postavena přes Selector, který přestaví widget při změně modelu a zavolá callback při vstupu uživatele.

Shrnutí

  • Two-Way Binding — mechanismus automatické obousměrné synchronizace mezi modelem a zobrazením, který eliminuje nutnost ručního psaní posluchačů a setterů.
  • V Android je implementován pomocí DataBinding se syntaxí @={} a anotacemi @BindingAdapter/@InverseBindingAdapter.
  • V iOS SwiftUI poskytuje propertyWrapper @Binding, vytvářející read-write odkaz na @State rodiče.
  • DataBinding snižuje objem UI kódu o 30–50%, ale ztěžuje ladění při výskytu nekonečných cyklů.
  • Pro formuláře se 3–5 poli je Two-Way Binding efektivní; pro globální stav a složitou validaci zvolte UDF.
  • V Jetpack Compose je obousměrné vázání simulováno pomocí State + callback onValueChange, zachovávající explicitní tok dat.
  • Hlavní rizika — cyklické aktualizace, vázání vypočítávaných polí a úniky paměti při absenci LifecycleOwner.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také