Data binding kütüphanesi, UI bileşenlerini uygulamanızdaki veri kaynaklarına bağlamanıza olanak tanıyan bir kütüphanedir.

Aşağıdaki kod, bir TextViewi, viewModel nesnesinin userName özelliğine bağlamak için findViewById() öğesini çağırır:

findViewById<TextView>(R.id.sample_text).apply {
    text = viewModel.userName
}

Aşağıdaki örnek, doğrudan layout dosyasında TextViewe değer atamak için data binding kütüphanesinin nasıl kullanılacağını gösterir.

<TextView
    android:text="@{viewmodel.userName}" />

Layout dosyası içerisinde bileşenleri bağlamak, activity içerisinde bir çok UI framework kullanımı azaltır ve bu bileşenlerin kullanımı kolay hale getirir. Ayrıca uygulamanın performansını iyileştirir ve bellek sızıntılarını ortadan kaldırır.

Kullanımı

Uygulamanızı data binding kullanacak şekilde yapılandırmak için, uygulama modülündeki build.gradle dosyanızdaki dataBinding derleme seçeneğini aşağıdaki örnekte gösterildiği gibi etkinleştirin:

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

Data binding kütüphanesi, layout içinde bulunan viewleri data nesnelerinizle bağlamak için gereken sınıfları otomatik olarak oluşturur. Data binding layout dosyaları biraz farklıdır ve layout tagi ile başlar. Ardından data elemanı ve view root elemanı gelir. Aşağıdaki kod, örnek bir layout dosyasını gösterir:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

data içindeki user değişkeni, bu layout içinde kullanılabilecek bir özelliği tanımlar.

<variable name="user" type="com.example.User" />

Layout içindeki ifadeler, "@{}" sözdizimi kullanılarak öznitelik özelliklerine yazılır. Burada TextView metni, user değişkeninin firstName özelliğine ayarlanır:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}" />

User adında data classımız olduğunu varsayalım.

data class User(val firstName: String, val lastName: String)

Her layout dosyası için bir binding sınıfı oluşturulur. Varsayılan olarak, sınıfın adı layout dosyasının adını temel alır ve sonuna Binding ekini ekler. Örneğin: activity_main.xml layout dosyası için oluşturulacak olan sınıf ActivityMainBinding‘tir. Binding oluşturmak için önerilen yöntem:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main)

    binding.user = User("Test", "User")
}

Alternatif olarak, aşağıdaki örnekte gösterildiği gibi bir LayoutInflater kullanarak görünümü elde edebilirsiniz:

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())

Fragment, ListView veya RecyclerView içerisinde data binding kullanıyorsanız, aşağıdaki kod örneğinde gösterildiği gibi binding sınıflarının veya DataBindingUtil sınıfının inflate() yöntemlerini kullanmayı tercih edebilirsiniz:

val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// yada
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)

Layout içerisinde bazı operatörler kullanılabilir. Örneğin:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

Databinding Event Handling

DataBinding kullanırken Event Handling olayını iki şekilde yapabiliriz.

1- Method References kullanarak

2- Listener Bindings kullanarak

Method References

Bir click olayını activity içerisinde yazmak yerine direkt olarak layout içerisinde bind edebiliriz. Bunu yapabilmek için android:onClick kullanılır. Bu kullanımın en büyük avantajı derleme zamanında işlenmesi, yani belirtilen metod tanımlanmamışsa derleme zamanında hata alınmasıdır. Click işlemini handle eden bir sınıf oluşturalım:

class MyHandlers {
    fun onClickFriend(view: View) { ... }
}

onClickFriend() metodu textView view’ine android:onClick kullanarak bağlanır.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

Listener Binding

Metod referansı bağlama ile listener bağlama arasındaki en önemli fark metod bağlama event trigger edildiğinde değil veri bağlama aşamasında oluşmasıdır. Listener bağlama, bir olay gerçekleştiğinde çalışan bağlama ifadeleridir. Örneğin, onSaveClick() yöntemine sahip olan aşağıdaki Presenter sınıfını düşünün:

class Presenter {
    fun onSaveClick(task: Task){}
}

Ardından, click olayını onSaveClick() metoduna aşağıdaki gibi bağlayabilirsiniz:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="task" type="com.android.example.Task" />
        <variable name="presenter" type="com.android.example.Presenter" />
    </data>
    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onClick="@{() -> presenter.onSaveClick(task)}" />
    </LinearLayout>
</layout>

Yukarıdaki örnekte, onClick(View) öğesine iletilen view parametresini tanımlamadık. Listener bağlamaları, listener parametreleri için iki seçenek sunar: metodun tüm parametrelerini yok sayabilir veya hepsini adlandırabilirsiniz. Parametreleri adlandırmayı tercih ederseniz, bunları ifadenizde kullanabilirsiniz. Örneğin, yukarıdaki ifade aşağıdaki gibi yazılabilir:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

Veya ifadedeki parametreyi kullanmak isterseniz Presenter sınıfını aşağıdaki gibi düzenleyebilirsiniz:

class Presenter {
    fun onSaveClick(view: View, task: Task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

Imports, variables, and includes

Data binding kütüphanesi, import, variable ve include gibi özellikler sağlar. Import, layout dosyalarınızın içindeki sınıflara başvurmayı kolaylaştırır. Variable, binding ifadelerinde kullanılabilecek bir özelliği tanımlamanıza olanak tanır. Include, uygulamanız genelinde karmaşık View’leri yeniden kullanmanıza olanak tanır.

<data>
    <import type="android.view.View"/>
</data>

Import

Import, layout dosyası içerisinde sınıfları kullanabilmeyi sağlar. data tagi içerisinde import anahtar kelimesi ile kullanılır. Aşağıdaki kod örneği, View sınıfını layout dosyasına aktarır:

<data>
    <import type="android.view.View"/>
</data>

View sınıfını import etmek, binding ifadelerinde kullanabilmemizi sağlar. Aşağıdaki örnek, View sınıfının VISIBLE ve GONE sabitlerinin nasıl kullanıldığını gösterir:

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

Variable

data öğesinin içinde birden çok değişken kullanılabilir. Aşağıdaki örnek, user, image ve note değişkenlerini gösterir:

<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user" type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note" type="String"/>
</data>

Include

Değişkenler, include edilen bir layout dosyasına bind edilebilir.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </LinearLayout>
</layout>