Two-Way Bindingin ne olduğunu öğrenin — mobil uygulamalarda model ve görünümü otomatik olarak senkronize eden çift yönlü veri bağlama. findViewById aracılığıyla manuel UI güncellemesinin aksine, bağlama mekanizması kullanıcı girişi değiştiğinde modeli ve veri değiştiğinde görünümü günceller. Google I/O 2024'e göre, bağlama Android ve iOS projelerinde kalıplaşmış UI kodunu %30–50 oranında azaltır. Bu yaklaşım Jetpack Compose ve SwiftUI'den Flutter ve React Native'e kadar çerçevelerde kullanılır.
Önemli Noktalar
Two-Way Binding (çift yönlü veri bağlama) — veri modelindeki değişikliklerin otomatik olarak kullanıcı arayüzüne yansıdığı ve UI'daki değişikliklerin modeli hemen güncellediği bir mimari mekanizmadır. Verilerin yalnızca modelden görünüme aktığı tek yönlü bağlamanın aksine, çift yönlü bağlama her güncellemeyi manuel olarak kodlamadan kapalı bir senkronizasyon döngüsü oluşturur.
Android Developers Blog'a (2023) göre, 2015'te tanıtılan DataBinding kütüphanesi, ticari Android uygulamalarının %42'sinde kullanılmaktadır. Mekanizma özellikle metin alanları, anahtarlar, kaydırıcılar ve onay kutuları gibi giriş formlarında talep görmektedir; burada kullanıcı girişi anında modele ve programatik değişiklikler UI'ya yansımalıdır. Tüm bu senaryolarda, geliştirici bir "dinleyici + ayarlayıcı" çifti yerine bir bağlama yazar.
IT Sectr'de, 2017'den beri projelerde çift yönlü bağlama uyguladık ve bilinçli olarak kullanılmasını öneriyoruz: basit giriş alanları için, ancak bağımlılıkları olan karmaşık durumlar için değil.
Two-Way Binding mekanizması üç temel öğe üzerine kuruludur: gözlemlenebilir alan (observable), değişiklik dinleyicisi ve ters senkronizasyon mekanizması. Bir kullanıcı EditText alanına metin yazdığında, sistem TextWatcher olayını yakalar, yeni değeri bağlı değişkene yazar ve değişken koddan değiştiyse UI'ya yeniden çizim yapmasını bildirir.
Perde arkasında, Android DataBinding kütüphanesi derleme zamanında tüm bağlama mantığını içeren bir Binding sınıfı oluşturur. @={variable} özniteliğine sahip her View için, geçersiz kılma ile bir setter + getter çifti oluşturulur. SwiftUI'de, @Binding propertyWrapper Combine mekanizması aracılığıyla değeri senkronize ederek benzer bir iş yapar. SwiftUI, @Published özellikleri aracılığıyla değişiklikleri izler ve bağlı değişkendeki herhangi bir değişiklikte View'ı otomatik olarak yeniden çizer.
WWDC Session 10033'e (2023) göre, SwiftUI'deki @Binding mekanizması, giriş alanlarını senkronize ederken saniyede 60 kareye kadar işlem yapabilir ve bu da onu gecikmesiz etkileşimli formlar için uygun hale getirir. Her iki çerçevede de Two-Way Binding, Observer deseni üzerinde sözdizimsel şekerdir ve abonelik ile bildirimi otomatikleştirir.
Android'de çift yönlü bağlama iki varyasyonda mevcuttur: @={} özniteliği aracılığıyla klasik XML DataBinding ve çift yönlü durum referansları aracılığıyla Jetpack Compose. Her iki yaklaşım da aynı sorunu — UI ve modeli senkronize etme — çözer ancak sözdizimi ve uygulama kapsamı açısından farklılık gösterir.
XML işaretlemesinde, çift yönlü bağlama @={variable.property} sözdizimi ile gösterilir — süslü parantezler içindeki eşittir işareti onu tek yönlü @{variable}'den ayırır. Özel Views için ters öznitelikle birlikte @BindingAdapter ek açıklaması gereklidir.
<layout>
<data>
<variable name="viewModel" type="com.example.LoginViewModel" />
</data>
<EditText
android:text="@{viewModel.email}" />
<CheckBox
android:checked="@{viewModel.agreeToTerms}" />
</layout>Örnek, e-posta ve onay kutusuyla basit bir form gösterir — her iki alan da çift yönlü bağlama kullanır, bu da Activity kodunda TextWatcher ve OnCheckedChangeListener yazma ihtiyacını ortadan kaldırır. Kullanıcı metni değiştirdiğinde, viewModel.email alanı otomatik olarak güncellenir.
@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() }
}RatingBar için özel bir BindingAdapter, DataBinding kütüphanesinin View'dan değeri nasıl okuyacağını (ters geri bildirim) ve View'a nasıl yazacağını (doğrudan bağlama) bilmesi için bir çift ek açıklama — @BindingAdapter ve @InverseBindingAdapter — kullanır. AttrChanged sonekine sahip üçüncü bağdaştırıcı, kullanıcı tarafından başlatılan değer değişikliklerini sisteme bildirir.
Jetpack Compose @={} sözdizimini desteklemez ancak mutableStateOf ve açık setter işlevi iletme yoluyla benzer bir mekanizma sağlar. Compose'da çift yönlü bağlama, alt bileşenlere State ve bir geri çağırma işlevi (value, onValueChange) iletilerek oluşturulur.
@Composable
fun LoginScreen() {
var email by remember { mutableStateOf("") }
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("E-posta") }
)
}
@Composable
fun CustomRatingBar(
rating: Float,
onRatingChange: (Float) -> Unit
) {
Slider(
value = rating,
onValueChange = onRatingChange,
valueRange = 0f..5f
)
}Compose'da çift yönlü iletişim, state + callback çifti aracılığıyla öykünülür — üst öğe geçerli değeri ve bir güncelleme işlevini iletir, alt bileşen kullanıcı etkileşiminde callback'i çağırır. Bu yaklaşım, veri akışı yönünü açıkça gösterir ve örtük DataBinding senkronizasyonuna kıyasla hata ayıklamayı basitleştirir.
SwiftUI'de çift yönlü bağlama, üst View'e ait bir veri kaynağına okuma-yazma referansı oluşturan @Binding propertyWrapper aracılığıyla uygulanır. @Binding değeri kendi başına depolamaz — üst öğenin @State veya @StateObject'i aracılığıyla okur ve yazar.
struct LoginView: View {
@State private var email = ""
@State private var agreeToTerms = false
var body: some View {
Form {
TextField("Email", text: $email)
Toggle("Şartları kabul ediyorum", isOn: $agreeToTerms)
ChildRatingView(rating: $rating)
}
}
}
struct ChildRatingView: View {
@Binding var rating: Double
var body: some View {
Slider(value: $rating, in: 0...5)
}
}Bir değişken adından önceki $ sembolü bir Binding referansı oluşturur: $email'in türü Binding
Apple WWDC 2023'e göre, SwiftUI yeniden çizimleri en aza indirmek için bir fark bulma algoritması kullanır: @Binding değeri değişirse ancak View bu değere bağlı değilse, yeniden çizim gerçekleşmez. Bu, UIKit ile karşılaştırılabilir performans sağlar (ProMotion ekranlarda 120 FPS'ye kadar).
Çift yönlü bağlama ile tek yönlü veri akışı (UDF) arasındaki seçim, mobil geliştirmedeki kilit mimari kararlardan biridir. Two-Way Binding, yerel form durumları için idealdir ve kullanıcının her adımının ek kod olmadan hemen modele yansıması gerektiği durumlarda kullanılır. UDF, değişikliklerin öngörülebilirliğinin geliştirme hızından daha önemli olduğu genel uygulama durumu için tercih edilir.
| Kriter | Two-Way Binding | UDF |
|---|---|---|
| Formdaki kod hacmi | 1 satır (@={} özniteliği) | 5–7 satır (State, Intent, Reducer) |
| Veri akışı hata ayıklama | Zor (kim değiştirdi — UI mı kod mu?) | Kolay (tüm değişiklikler Intent ile) |
| Performans | Yüksek (yerel senkronizasyon) | Orta (Reducer + Redux katmanı) |
| Ölçeklenebilirlik | Karmaşık formlarda doğrulama ile düşer | Ekran sayısıyla artar |
| Durum öngörülebilirliği | Düşük (döngülerden yan etkiler) | Yüksek (reducer tek gerçek kaynağıdır) |
Öneri: Karmaşık doğrulama olmadan 3–5 alanlı formlarda basit giriş alanları (metin, onay kutuları, anahtarlar) için Two-Way Binding kullanın. Genel durum, ağ istekleri ve bağımlı alanlar içeren ekranlar için açık olay işleme ile tek yönlü akışlı UDF kullanın. IT Sectr'de her iki yaklaşımı birleştiriyoruz: formların içinde Two-Way Binding, gezinme ve iş mantığı için UDF.
Sonsuz güncelleme döngüsü — Two-Way Binding kullanırken en yaygın sorun. Döngü, bir model değişikliğinin bir UI güncellemesini tetiklemesi ve bunun da modeli tekrar değiştirmesiyle oluşur. DataBinding'de bu, @InverseBindingAdapter'deki getter'ın bir setter çağrısından hemen sonra yeni bir değer döndürmesi durumunda gerçekleşir. Çözüm, geri yazmadan önce değerin değişip değişmediğini kontrol etmektir (koruma koşulu).
İkinci yaygın hata, hesaplanan alanları bağlamaktır. Bir alan başka bir alana bağlıysa (örneğin, toplam maliyet = fiyat × miktar), çift yönlü bağlama tutarsız bir duruma yol açabilir. Örneğin, kullanıcı miktarı değiştirir, bu bir maliyet yeniden hesaplamasını tetikler ve bu da miktarı tekrar değiştirir. Hesaplanan alanlar için Flow veya Combine ile tek yönlü bağlama kullanın.
Üçüncü hata, LifecycleOwner olmadan Observable alanları bağlamaktır. Android DataBinding'de, bağlamaya bir LifecycleOwner iletilmelidir, aksi takdirde Activity yok edildiğinde gözlemciler temizlenmez ve bu da bellek sızıntılarına yol açar. Fragment'larda her zaman viewLifecycleOwner'ı ve Activity'de this'i iletin.
Google Issue Tracker'a (2024) göre, DataBinding hata raporlarının yaklaşık %15'i döngüsel güncellemelerle ilgilidir. Teşhis için Android Studio Layout Inspector'ı kullanın — ekrandaki tüm bağlamaların geçerli değerlerini gösterir ve sonsuz döngünün kaynağını bulmayı kolaylaştırır.
Sıkça Sorulan Sorular
Tek yönlü bağlama (One-Way Binding) verileri yalnızca modelden görünüme aktarır — model değiştiğinde UI güncellenir, ancak kullanıcı girişi modeli doğrudan değiştirmez. Two-Way Binding verileri her iki yönde senkronize eder: UI'daki bir değişiklik otomatik olarak modeli günceller ve bunun tersi de geçerlidir. DataBinding sözdiziminde fark, @{} (Tek Yönlü) ve @={} (Çift Yönlü) ile belirtilir.
Bağımlı alanlar, hesaplanan değerler veya özel doğrulama içeren karmaşık formlar için çift yönlü bağlama kullanmayın — bu senaryolarda veri akışı tahmin edilemez hale gelir. Ayrıca, her öğenin bağlaması olan çok sayıda öğe içeren RecyclerView gibi listelerde de kaçının: çok sayıda gözlemci nedeniyle performans düşer. UDF, tek yönlü akış ve Intent tabanlı olay işleme ile daha iyi ölçeklenir.
Jetpack Compose'un yerleşik @={} sözdizimi yoktur, ancak çift yönlü senkronizasyon State + callback (onValueChange) çifti aracılığıyla uygulanır. Üst öğe geçerli değeri (State) ve bir güncelleme işlevini iletir, alt bileşen değişiklikte callback'i çağırır. Bu örtük değil, açık bağlamadır — veri akışı görünür ve izlenebilir kalır.
DataBinding'de döngüleri hata ayıklamak için Android Studio Layout Inspector kullanın — ekrandaki tüm bağlı değişkenlerin geçerli değerlerini gösterir. @InverseBindingAdapter'ye günlük ekleyin ve getter'ın yeni yazılan değerden farklı bir değer döndürüp döndürmediğini kontrol edin. Standart çözüm, geri yazmadan önce bir koruma koşuludur: if (newValue != currentValue).
Flutter'da yerleşik çift yönlü bağlama yoktur, ancak TextEditingController ve onChanged callback'inin bir kombinasyonu aracılığıyla öykünülebilir. StatefulWidget için geliştirici, denetleyici değişikliklerine manuel olarak abone olur ve modeli günceller. Provider ve Riverpod'da, çift yönlü senkronizasyon Selector aracılığıyla oluşturulur; bu, model değiştiğinde widget'ı yeniden oluşturur ve kullanıcı girişinde bir callback çağırır.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun