lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Data Binding Library

Dokumen ini menjelaskan cara menggunakan Data Binding Library untuk menulis layout deklaratif dan meminimalkan kode perekat yang diperlukan untuk mengikat logika aplikasi dan layout Anda.

Data Binding Library menawarkan fleksibilitas dan kompatibilitas yang luas — ini adalah pustaka dukungan, sehingga Anda bisa menggunakannya untuk semua platform Android lama hingga versi Android 2.1 (API level 7+).

Untuk menggunakan pengikatan data, diperlukan Android Plugin untuk Gradle 1.5.0-alpha1 atau yang lebih tinggi.

Lingkungan Pembuatan

Untuk memulai Pengikatan Data, unduh pustaka dari repositori Support dalam Android SDK manager.

Untuk mengonfigurasi aplikasi agar menggunakan pengikatan data, tambahkan elemen dataBinding ke file build.gradle Anda dalam modul aplikasi.

Gunakan cuplikan kode berikut untuk mengonfigurasi pengikatan data:

android {
    ....
    dataBinding {
        enabled = true
    }
}

Jika Anda memiliki modul aplikasi yang bergantung pada pustaka yang menggunakan pengikatan data, modul aplikasi Anda juga harus mengonfigurasi pengikatan data dalam file build.gradle.

Juga, pastikan Anda menggunakan versi Android Studio yang kompatibel. Android Studio 1.3 dan yang lebih baru menyediakan dukungan untuk pengikatan data seperti yang dijelaskan dalam Dukungan Android Studio untuk Pengikatan Data.

File Layout Pengikatan Data

Menulis kumpulan ekspresi pengikatan data Anda yang pertama

File layout pengikatan-data sedikit berbeda dan diawali dengan tag akar layout lalu diikuti oleh elemen data dan elemen akar view. Elemen tampilan ini adalah tempat akar akan berada dalam file layout non-pengikatan. Contoh file akan terlihat seperti ini:

<?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>

Variabel user dalam data menjelaskan properti yang dapat digunakan dalam layout ini.

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

Ekspresi dalam layout ditulis dalam properti atribut menggunakan sintaks "@{}". Di sini, teks TextView disetel ke properti firstName pengguna:

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

Objek Data

Anggap saja saat ini Anda memiliki objek Java lama yang sederhana (POJO) untuk Pengguna:

public class User {
   public final String firstName;
   public final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}

Objek tipe ini memiliki data yang tidak pernah berubah. Hal ini biasa terjadi dalam aplikasi saat memiliki data yang hanya dibaca sekali dan tidak pernah berubah setelahnya. Adalah mungkin juga untuk menggunakan objek JavaBeans:

public class User {
   private final String firstName;
   private final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
   public String getFirstName() {
       return this.firstName;
   }
   public String getLastName() {
       return this.lastName;
   }
}

Dari perspektif pengikatan data, dua kelas ini setara. Ekspresi @{user.firstName} digunakan untuk atribut android:text TextView ini akan mengakses bidang firstName di bekas kelas dan metode getFirstName() di kelas terakhir. Atau, itu juga bisa memutuskan ke firstName() jika metode tersebut ada.

Mengikat Data

Secara default, kelas Pengikatan akan dihasilkan berdasarkan nama file layout, mengubahnya menjadi casing Pascal dan menambahkan akhiran "Binding" ke situ. File layout di atas adalah main_activity.xml sehingga kelas yang dihasilkan adalah MainActivityBinding. Kelas ini memegang semua pengikatan dari properti layout (mis. variabel user) untuk Tampilan layout dan mengetahui cara menetapkan nilai untuk ekspresi pengikatan. Cara termudah untuk membuat pengikatan adalah melakukannya saat membesar:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
}

Selesai! Jalankan aplikasi dan Anda akan melihat Test User pada UI. Atau, Anda bisa mendapatkan tampilan melalui:

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

Jika Anda menggunakan item pengikatan data di dalam ListView atau adaptor RecyclerView, Anda bisa memilih untuk menggunakan:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

Penangan Kejadian

Pengikatan Data memungkinkan Anda untuk menulis penangan kejadian ekspresi yang dikirim dari tampilan (mis. onClick). Nama atribut kejadian diatur oleh nama metode listener dengan beberapa pengecualian. Misalnya, View.OnLongClickListener memiliki metode onLongClick(), sehingga atribut untuk kejadian ini adalah android:onLongClick. Ada dua cara untuk menangani suatu kejadian.

  • Referensi Metode: Dalam ekspresi, Anda bisa mereferensikan metode yang sesuai dengan tanda tangan dari metode listener. Bila ekspresi mengevaluasi ke referensi metode, Pengikatan Data akan membungkus referensi metode dan objek pemilik dalam sebuah listener, dan menetapkan listener tersebut pada tampilan target. Jika ekspresi bernilai kosong, Pengikatan Data tidak akan menciptakan listener dan menetapkan listener kosong.
  • Pengikatan Listener: Ini adalah ekspresi lambda yang dievaluasi saat kejadian terjadi. Pengikatan Data selalu menciptakan listener, yang ditetapkan pada tampilan. Bila kejadian ini dikirim, listener akan mengevaluasi ekspresi lambda.

Referensi Metode

Kejadian bisa terikat dengan penangan metode secara langsung, serupa dengan cara android:onClick dapat ditetapkan ke suatu metode dalam Aktivitas. Salah satu keunggulan utama dibandingkan dengan atribut View#onClick adalah bahwa ekspresi diproses pada waktu kompilasi, jadi jika metode tidak ada atau tanda tangan tidak benar, Anda menerima kesalahan waktu kompilasi.

Perbedaan utama antara Referensi Metode dan Pengikatan Listener adalah bahwa implementasi listener aktual dibuat ketika data diikat, bukan ketika kejadian dipicu. Jika Anda lebih memilih untuk mengevaluasi ekspresi ketika terjadi kejadian, Anda harus menggunakan pengikatan listener.

Untuk menetapkan sebuah kejadian ke penangan-nya, gunakan ekspresi pengikatan biasa, dengan nilainya berupa nama metode yang dipanggil. Misalnya, jika objek data Anda memiliki dua metode:

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

Ekspresi pengikatan bisa menetapkan listener klik untuk Tampilan:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <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>

Perhatikan bahwa tanda tangan metode dalam ekspresi harus sama persis dengan tanda tangan metode dalam objek Listener.

Pengikatan Listener

Pengikatan Listener adalah ekspresi pengikatan yang berjalan ketika terjadi sebuah kejadian. Mereka mirip dengan referensi metode, namun membolehkan Anda secara bebas menjalankan ekspresi pengikatan data. Fitur ini tersedia pada Android Gradle Plugin untuk Gradle versi 2.0 dan yang lebih baru.

Dalam metode referensi, parameter metode harus sesuai dengan parameter dari event listener. Dalam Pengikatan Listener, hanya nilai kembali yang harus cocok dengan nilai pengembalian yang diharapkan dari listener (kecuali jika berharap kosong). Misalnya, Anda bisa memiliki kelas presenter yang memiliki metode berikut:

public class Presenter {
    public void onSaveClick(Task task){}
}
Kemudian Anda bisa mengikat kejadian klik untuk kelas seperti berikut:
  <?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>

Listener ditunjukkan oleh ekspresi lambda yang hanya diizinkan sebagai elemen akar dari ekspresi Anda. Bila callback digunakan dalam ekspresi, Pengikatan Data secara otomatis akan membuat listener yang diperlukan dan mendaftarkan untuk kejadian tersebut. Bila tampilan meluncurkan kejadian, Pengikatan Data akan mengevaluasi ekspresi yang diberikan. Seperti dalam ekspresi pengikatan biasa, Anda tetap mendapatkan Pengikatan Data kosong dan keamanan thread saat ekspresi listener ini dievaluasi.

Perhatikan bahwa dalam contoh di atas, kita belum mendefinisikan parameter view yang diteruskan ke onClick(android.view.View). Pengikatan listener menyediakan dua pilihan untuk parameter listener: Anda bisa mengabaikan semua parameter dengan metode atau memberi nama mereka semua. Jika Anda memilih untuk memberikan nama parameter tersebut, Anda bisa menggunakannya dalam ekspresi Anda. Misalnya, ekspresi di atas dapat dituliskan seperti ini:

  android:onClick="@{(view) -> presenter.onSaveClick(task)}"
Atau jika Anda ingin menggunakan parameter dalam ekspresi, itu bisa dilakukan seperti berikut:
public class Presenter {
    public void onSaveClick(View view, Task task){}
}
  android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
Anda bisa menggunakan ekspresi lambda dengan lebih dari satu parameter:
public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}
  <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

Jika kejadian yang sedang Anda dengarkan akan mengembalikan nilai yang bertipe bukan void, ekspresi Anda juga harus mengembalikan tipe nilai yang sama. Misalnya, jika Anda ingin mendengarkan untuk kejadian klik lama, ekspresi Anda harus mengembalikan boolean.

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}
  android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

Jika ekspresi tidak dapat dievaluasi karena objek null, Pengikatan Data mengembalikan nilai Java default untuk tipe tersebut. Misalnya, null untuk tipe referensi, 0 untuk int, false untuk boolean, dan lain-lain.

Jika Anda harus menggunakan ekspresi dengan predikat (mis. ternary), Anda bisa menggunakan void sebagai simbol.

  android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
Hindari Listener Kompleks
Ekspresi listener sangat kuat dan bisa membuat kode Anda sangat mudah untuk dibaca. Di sisi lain, listener yang berisi ekspresi kompleks membuat layout Anda sulit dibaca dan dipelihara. Ekspresi ini harus sama mudahnya seperti meneruskan data yang tersedia dari UI ke metode callback Anda. Anda harus mengimplementasikan logika bisnis dalam metode callback yang Anda panggil dari ekspresi listener.

Terdapat beberapa penangan kejadian klik khusus dan mereka membutuhkan atribut selain android:onClick untuk menghindari konflik. Atribut berikut telah dibuat untuk menghindari konflik seperti:

Kelas Setter Listener Atribut
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

Detail Layout

Impor

Elemen import kosong atau lebih dapat digunakan dalam elemen data. Ini memungkinkan referensi yang mudah untuk kelas dalam file layout Anda, seperti dalam Java.

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

Sekarang, Tampilan dapat digunakan dalam ekspresi pengikatan Anda:

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

Bila ada konflik nama kelas, salah satu kelas dapat diganti menjadi "alias:"

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

Sekarang, Vista dapat digunakan untuk mereferensikan com.example.real.estate.View dan View dapat digunakan untuk mereferensikan android.view.View dalam file layout. Tipe yang diimpor dapat digunakan sebagai referensi tipe dalam variabel dan ekspresi:

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List&lt;User&gt;"/>
</data>

Catatan: Android Studio belum menangani impor sehingga pelengkapan otomatis untuk variabel yang diimpor mungkin tidak bekerja di IDE Anda. Aplikasi Anda tetap akan melakukan kompilasi dengan baik dan Anda bisa memecahkan masalah IDE dengan menggunakan nama yang memenuhi syarat dalam definisi variabel Anda.

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Tipe yang diimpor juga dapat digunakan pada saat mereferensikan bidang statis dan metode dalam ekspresi:

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Sama seperti di Java, java.lang.* diimpor secara otomatis.

Variabel

Sejumlah elemen variable dapat digunakan dalam elemen data. Setiap elemen variable menjelaskan properti yang dapat diatur pada layout yang akan digunakan dalam ekspresi pengikatan dalam file layout.

<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>

Tipe variabel diperiksa pada waktu kompilasi, jadi jika variabel mengimplementasikan android.databinding.Observable atau sebuah kumpulan observable, itu harus direfleksikan dalam tipe. Jika variabel adalah kelas dasar atau antarmuka yang tidak mengimplementasikan antarmuka Observable*, variabel tidak akan diamati!

Bila ada file layout yang berbeda untuk beragam konfigurasi (misalnya lanskap atau potret), variabel akan dipadukan. Tidak boleh ada definisi variabel yang saling bertentangan antara file layout ini.

Kelas pengikat yang dihasilkan akan memiliki setter dan getter untuk setiap variabel yang dijelaskan. Variabel akan mengambil nilai Java default hingga setter dipanggil — null untuk tipe referensi, 0 untuk int, false untuk boolean, dll.

Sebuah variabel khusus bernama context dihasilkan untuk digunakan dalam ekspresi pengikatan jika diperlukan. Nilai untuk context adalah Context dari getContext() Tampilan akar. Variabel context akan diganti oleh deklarasi variabel eksplisit dengan nama tersebut.

Nama Kelas Pengikatan Khusus

Secara default, kelas Pengikatan dihasilkan berdasarkan pada nama file layout, diawali dengan huruf kapital, menghilangkan setrip bawah ( _ ) serta melakukan kapitalisasi huruf berikutnya dan kemudian memberi akhiran "Binding". Kelas ini akan ditempatkan dalam paket databinding di bawah paket modul. Misalnya, file layout contact_item.xml akan menghasilkan ContactItemBinding. Jika paket modul adalah com.example.my.app, maka akan ditempatkan dalam com.example.my.app.databinding.

Kelas pengikat dapat diubah namanya atau ditempatkan dalam paket yang berbeda dengan menyesuaikan atribut class dari elemen data. Misalnya:

<data class="ContactItem">
    ...
</data>

Ini akan menghasilkan kelas pengikatan sebagai ContactItem di paket databinding dalam paket modul. Jika kelas seharusnya dihasilkan pada paket yang berbeda dalam paket modul, maka dapat diawali dengan ".":

<data class=".ContactItem">
    ...
</data>

Dalam hal ini, ContactItem dihasilkan dalam paket modul secara langsung. Paket apa pun dapat digunakan jika disediakan paket lengkap:

<data class="com.example.ContactItem">
    ...
</data>

Meliputi

Variabel dapat diteruskan ke dalam pengikatan layout yang disertakan dari layout yang memuat dengan menggunakan namespace aplikasi dan nama variabel dalam atribut:

<?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>

Di sini, harus ada variabel user di file layout name.xml dan contact.xml.

Pengikatan data tidak mendukung include sebagai anak langsung dari elemen gabungan. Misalnya, layout berikut tidak didukung:

<?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>
   <merge>
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </merge>
</layout>

Bahasa Ekspresi

Fitur Umum

Bahasa ekspresi yang mirip seperti ekspresi Java. Begitu juga dengan ini:

  • Matematis + - / * %
  • Penyambungan string +
  • Logis && ||
  • Biner & | ^
  • Unary + - ! ~
  • Shift >> >>> <<
  • Perbandingan == > < >= <=
  • instanceof
  • Pengelompokan ()
  • Literal - karakter, String, angka, null
  • Transmisi
  • Panggilan metode
  • Akses bidang
  • Akses larik []
  • Operator ternary ?:

Contoh:

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

Operasi yang Hilang

Beberapa operasi yang hilang dari sintaks ekspresi yang bisa Anda gunakan di Java.

  • this
  • super
  • new
  • Permintaan umum eksplisit

Operator Penggabungan Kosong

Operator penggabungan kosong (??) akan memilih operand kiri jika tidak kosong atau kanan jika kosong.

android:text="@{user.displayName ?? user.lastName}"

Ini secara fungsional sama dengan:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

Referensi Properti

Yang pertama sudah dibahas dalam Menulis ekspresi pengikatan data Anda yang pertama di atas: bentuk singkat referensi JavaBean. Bila suatu ekspresi merujuk properti pada kelas, format yang sama akan digunakan untuk bidang, getter, dan ObservableFields.

android:text="@{user.lastName}"

Menghindari NullPointerException

Kode pengikatan data yang dihasilkan akan secara otomatis memeriksa jika ada nilai kosong dan menghindari pengecualian pointer kosong. Misalnya, dalam ekspresi @{user.name}, jika user bernilai kosong, user.name akan diberikan nilai default (kosong). Jika Anda mereferensikan user.age, dengan usia adalah int, maka secara default akan bernilai 0.

Kumpulan

Kumpulan umum: larik, daftar, daftar sparse, dan peta, dapat diakses menggunakan operator [] agar lebih mudah.

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String&gt;"/>
    <variable name="sparse" type="SparseArray&lt;String&gt;"/>
    <variable name="map" type="Map&lt;String, String&gt;"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"

String Literal

Saat menggunakan tanda kutip tunggal dekat nilai atribut, mudah juga untuk menggunakan tanda kutip ganda dalam ekspresi:

android:text='@{map["firstName"]}'

Dimungkinkan juga penggunaan tanda kutip ganda untuk mengurung nilai atribut. Bila melakukannya, String literal harus menggunakan ' atau tanda kutip belakang (`).

android:text="@{map[`firstName`}"
android:text="@{map['firstName']}"

Sumber Daya

Dimungkinkan juga untuk mengakses sumber daya sebagai bagian dari ekspresi menggunakan sintaks normal:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

Format string dan plural dapat dievaluasi dengan menyediakan parameter:

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

Bila plural mengambil beberapa parameter, semua parameter harus diteruskan:


  Have an orange
  Have %d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"

Beberapa sumber daya memerlukan evaluasi tipe eksplisit.

Tipe Referensi Normal Referensi Ekspresi
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

Objek Data

Semua plain old Java object (POJO) dapat digunakan untuk pengikatan data, namun memodifikasi POJO tidak akan menyebabkan UI melakukan pembaruan. Kekuatan sebenarnya dari pengikatan data bisa digunakan dengan memberikan objek data Anda kemampuan untuk memberi tahu jika ada perubahan data. Ada tiga mekanisme notifikasi perubahan data yang berbeda, Objek Observable, bidang observable, dan kumpulan observable.

Bila salah satu dari objek data observable ini terikat pada UI dan properti objek data berubah, UI akan diperbarui secara otomatis.

Objek Observable

Sebuah kelas yang mengimplementasikan antarmuka android.databinding.Observable akan mengizinkan pengikatan untuk melampirkan satu listener ke objek terikat untuk mendengarkan perubahan dari semua properti pada objek tersebut.

Antarmuka android.databinding.Observable memiliki mekanisme untuk menambah dan menghapus listener, namun notifikasinya adalah terserah developer. Agar development lebih mudah, kelas dasar, android.databinding.BaseObservable, dibuat untuk mengimplementasikan mekanisme pendaftaran listener. Implementer kelas data tetap bertanggung jawab untuk memberi tahu ketika properti berubah. Ini dilakukan dengan memberikan anotasi android.databinding.Bindable untuk getter dan memberitahukan dalam setter.

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

Anotasi android.databinding.Bindable menghasilkan sebuah entri dalam file kelas BR selama kompilasi. File kelas BR akan dihasilkan dalam paket modul. Jika kelas dasar untuk kelas data tidak dapat diubah, antarmuka android.databinding.Observable dapat diimplementasikan dengan menggunakan android.databinding.PropertyChangeRegistry yang mudah untuk menyimpan dan memberi tahu listener dengan efisien.

ObservableFields

Sedikit upaya ikut terlibat dalam menciptakan kelas-kelas android.databinding.Observable, sehingga para developer yang ingin menghemat waktu atau memiliki beberapa properti dapat menggunakan android.databinding.ObservableField dan saudara-saudaranya android.databinding.ObservableBoolean, android.databinding.ObservableByte, android.databinding.ObservableChar, android.databinding.ObservableShort, android.databinding.ObservableInt, android.databinding.ObservableLong, android.databinding.ObservableFloat, android.databinding.ObservableDouble, dan android.databinding.ObservableParcelable. ObservableFields adalah objek observable mandiri yang memiliki satu bidang. Versi primitif menghindari boxing dan unboxing selama operasi akses. Untuk menggunakannya, buat bidang terakhir publik dalam kelas data:

private static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

Demikian saja! Untuk mengakses nilai, gunakan metode pengakses set dan get:

user.firstName.set("Google");
int age = user.age.get();

Kumpulan Observable

Beberapa aplikasi menggunakan struktur yang lebih dinamis untuk menyimpan data. Kumpulan Observable membolehkan akses berkunci untuk objek data ini. android.databinding.ObservableArrayMap akan berguna ketika kuncinya adalah tipe referensi, seperti String.

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

Dalam layout, peta dapat diakses melalui kunci String:

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
</data>
…
<TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

android.databinding.ObservableArrayList akan berguna apabila kuncinya adalah integer:

ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);

Dalam layout, daftar dapat diakses melalui indeks:

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList&lt;Object&gt;"/>
</data>
…
<TextView
   android:text='@{user[Fields.LAST_NAME]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Pengikatan yang Dihasilkan

Kelas pengikat yang dihasilkan menautkan variabel layout dengan Tampilan dalam layout. Seperti dibahas sebelumnya, nama dan paket Pengikatan dapat disesuaikan. Semua kelas pengikat yang dihasilkan memberikan android.databinding.ViewDataBinding.

Membuat

Pengikatan harus segera dibuat setelah membesar untuk memastikan bahwa hierarki Tampilan tidak terganggu sebelum menghubungkan Tampilan dengan ekspresi dalam layout. Ada beberapa cara untuk terhubung ke sebuah layout. Yang paling umum adalah dengan menggunakan metode statis pada kelas Pengikatan. Metode inflate menggelembungkan hierarki Tampilan dan menghubungkan semuanya ke situ dalam satu langkah. Ada versi lebih sederhana yang hanya menggunakan LayoutInflater dan yang menggunakan ViewGroup:

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

Jika layout diperbesar menggunakan mekanisme yang berbeda, itu dapat terikat secara terpisah:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

Kadang-kadang pengikatan tidak dapat diketahui sebelumnya. Dalam kasus tersebut, pengikatan bisa dibuat dengan menggunakan kelas android.databinding.DataBindingUtil:

ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

Tampilan Dengan ID

Bidang final publik akan dihasilkan untuk setiap Tampilan dengan ID dalam layout. Pengikatan melakukan penyaluran tunggal pada hierarki Tampilan, mengekstrak Tampilan dengan ID. Mekanisme ini lebih cepat daripada memanggil findViewById untuk sejumlah Tampilan. Misalnya:

<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}"
   android:id="@+id/firstName"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
  android:id="@+id/lastName"/>
   </LinearLayout>
</layout>

Akan menghasilkan kelas pengikat dengan:

public final TextView firstName;
public final TextView lastName;

ID tidak sama pentingnya seperti ketika tanpa pengikatan data, namun masih terdapat beberapa instance di mana akses ke Tampilan masih diperlukan dari kode.

Variabel

Masing-masing variabel akan diberikan metode pengakses.

<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>

akan menghasilkan setter dan getter dalam pengikatan:

public abstract com.example.User getUser();
public abstract void setUser(com.example.User user);
public abstract Drawable getImage();
public abstract void setImage(Drawable image);
public abstract String getNote();
public abstract void setNote(String note);

ViewStubs

ViewStub sedikit berbeda dari Tampilan biasa. Awalnya mereka tidak terlihat dan ketika mereka dibuat terlihat atau secara eksplisit disuruh untuk membesar, mereka menggantikan diri mereka sendiri dalam layout dengan membesarkan layout lain.

Karena ViewStub pada dasarnya menghilang dari hierarki Tampilan, Tampilan dalam objek pengikat juga harus menghilang untuk memungkinkan pengumpulan. Karena Tampilan adalah final, objek android.databinding.ViewStubProxy mengambil tempat ViewStub, memberikan akses developer untuk ViewStub ketika ada dan juga akses ke hierarki Tampilan yang membesar ketika ViewStub telah membesar.

Bila membesarkan layout lain, pengikatan harus ditetapkan untuk layout yang baru. Oleh karena itu, ViewStubProxy harus mendengarkan ViewStub ViewStub.OnInflateListener dan membuat pengikatan pada saat tersebut. Karena hanya bisa ada satu, ViewStubProxy memungkinkan developer untuk menyetel OnInflateListener di dalamnya yang akan dipanggil setelah membuat pengikatan.

Pengikatan Lanjutan

Variabel Dinamis

Saat ini, kelas pengikat khusus tidak akan diketahui. Misalnya, RecyclerView.Adapter yang beroperasi terhadap layout sembarang tidak akan mengetahui kelas pengikat khusus. Itu tetap harus menetapkan nilai pengikat selama onBindViewHolder(VH, int).

Dalam contoh ini, semua layout yang diikat RecyclerView memiliki "item" variabel. BindingHolder Memiliki metode getBinding yang mengembalikan dasar android.databinding.ViewDataBinding.

public void onBindViewHolder(BindingHolder holder, int position) {
   final T item = mItems.get(position);
   holder.getBinding().setVariable(BR.item, item);
   holder.getBinding().executePendingBindings();
}

Pengikatan Segera

Bila variabel atau observable berubah, pengikatan akan diatur untuk berubah sebelum frame berikutnya. Namun ada kalanya, saat pengikatan harus dilakukan dengan segera. Untuk memaksa eksekusi, gunakan metode ViewDataBinding#executePendingBindings().

Thread Latar Belakang

Anda bisa mengubah model data dalam thread latar belakang asalkan bukan kumpulan. Pengikatan data akan melokalkan setiap variabel / bidang saat mengevaluasi untuk menghindari masalah konkurensi.

Setter Atribut

Setiap kali terjadi perubahan nilai terikat, kelas pengikat yang dihasilkan harus memanggil metode setter pada Tampilan dengan ekspresi pengikatan. Kerangka kerja pengikatan data memiliki cara untuk menyesuaikan metode yang akan dipanggil untuk menyetel nilai.

Setter Otomatis

Untuk atribut, pengikatan data mencoba untuk menemukan metode setAttribute. Namespace untuk atribut tidak menjadi masalah, hanya nama atribut saja.

Misalnya, ekspresi yang berhubungan dengan atribut TextView android:text akan mencari setText (String). Jika ekspresi mengembalikan int, pengikatan data akan mencari metode setText(int). Berhati-hatilah agar ekspresi mengembalikan tipe yang tepat, transmisikan jika perlu. Perhatikan bahwa pengikatan data akan bekerja bahkan jika tidak ada atribut dengan nama yang diberikan. Kemudian Anda bisa dengan mudah "membuat" atribut untuk setter apa saja dengan menggunakan pengikatan data. Misalnya, DrawerLayout dukungan tidak memiliki atribut apa pun, namun memiliki banyak setter. Anda bisa menggunakan setter otomatis untuk menggunakan salah satunya.

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}"/>

Setter Berganti Nama

Beberapa atribut memiliki setter yang tidak cocok berdasarkan namanya. Untuk metode ini, atribut dapat dihubungkan dengan setter melalui anotasi android.databinding.BindingMethods. Ini harus dihubungkan dengan kelas dan berisi anotasi android.databinding.BindingMethod, satu untuk setiap metode yang diganti namanya. Misalnya, atribut android:tint benar-benar dihubungkan dengan setImageTintList(ColorStateList), tidak setTint.

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})

Sangat kecil kemungkinannya developer perlu mengubah nama setter; atribut framework android telah diimplementasikan.

Setter Khusus

Beberapa atribut membutuhkan logika pengikatan khusus. Misalnya, tidak ada setter yang dihubungkan untuk atribut android:paddingLeft. Sebagai gantinya, terdapat setPadding(left, top, right, bottom). Metode adaptor pengikat statis dengan anotasi android.databinding.BindingAdapter memungkinkan developer untuk menyesuaikan bagaimana setter untuk atribut akan dipanggil.

Atribut android telah membuat BindingAdapter. Misalnya, berikut adalah satu untuk paddingLeft:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}

Adaptor pengikat berguna untuk tipe penyesuaian lain. Misalnya, pemuat khusus bisa dipanggil dari luar-thread untuk memuat gambar.

Adaptor pengikat yang dibuat developer akan mengganti adaptor default pengikatan data ketika terjadi konflik.

Anda juga bisa memiliki adaptor yang menerima beberapa parameter.

@BindingAdapter({"bind:imageUrl", "bind:error"})
public static void loadImage(ImageView view, String url, Drawable error) {
   Picasso.with(view.getContext()).load(url).error(error).into(view);
}
<ImageView app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}"/>

Adaptor ini akan dipanggil jika imageUrl dan error digunakan untuk ImageView dan imageUrl adalah string dan error dapat digambar.

  • Namespace khusus diabaikan selama pencocokan.
  • Anda juga bisa menulis adaptor untuk namespace android.

Metode adaptor pengikat dapat secara opsional mengambil nilai-nilai lama dalam penangan mereka. Metode yang mengambil nilai lama dan baru harus memiliki semua nilai lama untuk atribut yang pertama kali datang, lalu diikuti oleh nilai-nilai baru:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
   if (oldPadding != newPadding) {
       view.setPadding(newPadding,
                       view.getPaddingTop(),
                       view.getPaddingRight(),
                       view.getPaddingBottom());
   }
}

Penangan kejadian hanya dapat digunakan dengan antarmuka atau kelas abstrak dengan satu metode abstrak. Misalnya:

@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
       View.OnLayoutChangeListener newValue) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        if (oldValue != null) {
            view.removeOnLayoutChangeListener(oldValue);
        }
        if (newValue != null) {
            view.addOnLayoutChangeListener(newValue);
        }
    }
}

Bila listener memiliki beberapa metode, itu harus dibagi menjadi beberapa listener. Misalnya, View.OnAttachStateChangeListener memiliki dua metode: onViewAttachedToWindow() dan onViewDetachedFromWindow(). Maka kita harus membuat dua antarmuka untuk membedakan atribut dan penangan untuk mereka.

@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
    void onViewDetachedFromWindow(View v);
}

@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
    void onViewAttachedToWindow(View v);
}

Karena mengubah satu listener juga akan memengaruhi yang lainnya, kita harus memiliki tiga adaptor pengikat yang berbeda, satu untuk masing-masing atribut dan satu lagi untuk keduanya, saat keduanya ditetapkan.

@BindingAdapter("android:onViewAttachedToWindow")
public static void setListener(View view, OnViewAttachedToWindow attached) {
    setListener(view, null, attached);
}

@BindingAdapter("android:onViewDetachedFromWindow")
public static void setListener(View view, OnViewDetachedFromWindow detached) {
    setListener(view, detached, null);
}

@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
public static void setListener(View view, final OnViewDetachedFromWindow detach,
        final OnViewAttachedToWindow attach) {
    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
        final OnAttachStateChangeListener newListener;
        if (detach == null && attach == null) {
            newListener = null;
        } else {
            newListener = new OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(View v) {
                    if (attach != null) {
                        attach.onViewAttachedToWindow(v);
                    }
                }

                @Override
                public void onViewDetachedFromWindow(View v) {
                    if (detach != null) {
                        detach.onViewDetachedFromWindow(v);
                    }
                }
            };
        }
        final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
                newListener, R.id.onAttachStateChangeListener);
        if (oldListener != null) {
            view.removeOnAttachStateChangeListener(oldListener);
        }
        if (newListener != null) {
            view.addOnAttachStateChangeListener(newListener);
        }
    }
}

Contoh di atas sedikit lebih rumit daripada biasanya karena Tampilan menggunakan tambahkan dan buang untuk listener sebagai ganti metode yang ditetapkan untuk View.OnAttachStateChangeListener. Kelas android.databinding.adapters.ListenerUtil membantu pelacakan listener sebelumnya sehingga mereka dapat dihapus dalam Pengikatan Adaptor.

Dengan membuat anotasi antarmuka OnViewDetachedFromWindow dan OnViewAttachedToWindow dengan @TargetApi(VERSION_CODES.HONEYCOMB_MR1), generator kode pengikatan data mengetahui bahwa listener hanya bisa dihasilkan ketika berjalan pada Honeycomb MR1 dan perangkat baru, versi yang sama didukung oleh addOnAttachStateChangeListener(View.OnAttachStateChangeListener).

Konverter

Konversi Objek

Bila Objek dikembalikan dari ekspresi pengikatan, setter akan dipilih dari setter otomatis, berganti nama, dan khusus. Objek akan ditransmisikan ke tipe parameter setter yang dipilih.

Ini adalah sebuah kemudahan bagi mereka yang menggunakan ObservableMaps untuk menyimpan data. Misalnya:

<TextView
   android:text='@{userMap["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

userMap mengembalikan sebuah Objek dan Objek tersebut akan secara otomatis ditransmisikan ke tipe parameter yang ditemukan dalam setter setText(CharSequence). Ketika mungkin ada kebingungan tentang tipe parameter, developer harus mentransmisikan ekspresi.

Konversi Khusus

Terkadang konversi seharusnya terjadi otomatis di antara tipe tertentu. Misalnya, ketika menetapkan latar belakang:

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Di sini, latar belakang mengambil Drawable, tapi warnanya adalah integer. Setiap kali Drawable diharapkan dan integer dikembalikan, int harus dikonversi ke ColorDrawable. Konversi ini dilakukan dengan menggunakan metode statis dengan anotasi BindingConversion:

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
   return new ColorDrawable(color);
}

Perhatikan bahwa konversi hanya terjadi di tingkat setter, sehingga tidak diperbolehkan untuk mencampur tipe seperti ini:

<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Dukungan Android Studio untuk Pengikatan Data

Android Studio mendukung banyak fitur pengeditan kode untuk kode pengikatan data. Misalnya, itu mendukung fitur berikut untuk ekspresi pengikatan data:

  • Menyorot Sintaks
  • Menandai kesalahan sintaks bahasa ekspresi
  • Pelengkapan kode XML
  • Referensi, seperti navigasi (seperti navigasi ke deklarasi) dan dokumentasi cepat

Catatan: Larik dan tipe generik, seperti kelas android.databinding.Observable, mungkin menampilkan kesalahan saat tidak ada kesalahan.

Panel Preview menampilkan nilai default untuk ekspresi pengikatan data jika disediakan. Dalam contoh cuplikan elemen berikut dari file XML layout, panel Preview menampilkan nilai teks default PLACEHOLDER dalam TextView.

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

Jika Anda perlu menampilkan nilai default selama tahap desain proyek, Anda juga bisa menggunakan atribut alat sebagai ganti nilai ekspresi default, seperti yang dijelaskan dalam Atribut Layout Designtime.