Bahasa ekspresi memungkinkan Anda menulis ekspresi yang menangani peristiwa yang dikirim oleh tampilan. Library Data Binding otomatis menghasilkan class yang diperlukan untuk mengikat tampilan di tata letak dengan objek data Anda.
File tata letak data binding sedikit berbeda dan dimulai dengan tag root
layout
, diikuti dengan elemen data
dan elemen root view
. Elemen
tampilan ini adalah tempat root Anda berada dalam file tata letak non-binding. Kode berikut
menunjukkan contoh file tata letak:
<?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 tata letak ini:
<variable name="user" type="com.example.User" />
Ekspresi dalam tata letak ditulis di properti atribut menggunakan sintaksis @{}
. Pada contoh berikut, teks TextView
ditetapkan ke properti firstName
dari variabel user
:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
Objek data
Misalnya Anda memiliki objek biasa untuk mendeskripsikan entity User
:
Kotlin
data class User(val firstName: String, val lastName: String)
Java
public class User { public final String firstName; public final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
Jenis objek ini memiliki data yang tidak pernah berubah. Umumnya aplikasi memiliki data yang dibaca sekali dan tidak pernah berubah setelahnya. Anda juga dapat menggunakan objek yang mengikuti serangkaian konvensi, seperti menggunakan metode pengakses dalam bahasa pemrograman Java, seperti yang ditunjukkan dalam contoh berikut:
Kotlin
// Not applicable in Kotlin. data class User(val firstName: String, val lastName: String)
Java
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 data binding, kedua class ini setara. Ekspresi
@{user.firstName}
yang digunakan untuk atribut
android:text
mengakses kolom firstName
di class sebelumnya dan metode
getFirstName()
di class kedua. Class ini juga di-resolve ke
firstName()
, jika metode tersebut ada.
Mengikat data
Class binding dibuat untuk setiap file tata letak. Secara default, nama
class didasarkan pada nama file tata letak, yang dikonversi ke Pascal case, dengan
akhiran Binding yang ditambahkan ke dalamnya. Misalnya, nama file tata letak sebelumnya adalah
activity_main.xml
, sehingga class binding terkait yang dihasilkan adalah
ActivityMainBinding
.
Class ini menyimpan semua binding dari properti tata letak—misalnya,
variabel user
—ke tampilan tata letak dan mengetahui cara menetapkan nilai
untuk ekspresi binding. Sebaiknya buat binding saat meng-inflate
tata letak, seperti yang ditunjukkan dalam contoh berikut:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMainBinding = DataBindingUtil.setContentView( this, R.layout.activity_main) binding.user = User("Test", "User") }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Test", "User"); binding.setUser(user); }
Saat runtime, aplikasi akan menampilkan pengguna Test di UI. Atau, Anda bisa
mendapatkan tampilan menggunakan
LayoutInflater
, seperti ditunjukkan dalam
contoh berikut:
Kotlin
val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
Java
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
Jika menggunakan item data binding di dalam adaptor
Fragment
,
ListView
, atau
RecyclerView
, Anda mungkin lebih memilih menggunakan metode
inflate()
class binding atau class
DataBindingUtil
, seperti
yang ditunjukkan dalam contoh kode berikut:
Kotlin
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false) // or val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
Java
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); // or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
Bahasa ekspresi
Fitur umum
Bahasa ekspresi sangat mirip dengan ekspresi yang ditemukan dalam kode terkelola. Anda dapat menggunakan operator dan kata kunci berikut dalam bahasa ekspresi:
- Matematika:
+ - / * %
- Penyambungan string:
+
- Logika:
&& ||
- Biner:
& | ^
- Unary:
+ - ! ~
- Shift:
>> >>> <<
- Perbandingan:
== > < >= <=
(<
harus di-escape sebagai<
) instanceof
- Pengelompokan:
()
- Literal, seperti karakter, String, numerik,
null
- Cast
- Panggilan metode
- Akses kolom
- Akses array:
[]
- Operator ternary:
?:
Berikut beberapa contohnya:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
Operasi yang tidak ada
Operasi berikut tidak ada di sintaksis ekspresi yang dapat Anda gunakan dalam kode terkelola:
this
super
new
- Panggilan umum eksplisit
Operator penggabungan null
Operator penggabungan null (??
) akan memilih operand kiri jika nilainya bukan null
atau kanan jika yang pertama adalah null
:
android:text="@{user.displayName ?? user.lastName}"
Secara fungsional, tindakan ini sama seperti berikut:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
Referensi properti
Ekspresi dapat mereferensikan properti di sebuah class menggunakan format berikut, yang sama untuk kolom, pengambil, dan objek ObservableField
:
android:text="@{user.lastName}"
Menghindari pengecualian pointer null
Kode data binding yang dihasilkan akan otomatis memeriksa nilai null
dan menghindari
pengecualian pointer null. Misalnya, dalam ekspresi @{user.name}
, jika
user
bernilai null, user.name
diberi nilai defaultnya yaitu null
. Jika Anda
mereferensikan user.age
, di mana usia berjenis int
, data binding akan menggunakan
nilai default 0
.
Referensi tampilan
Ekspresi dapat mereferensikan tampilan lain dalam tata letak menurut ID, menggunakan sintaksis berikut:
android:text="@{exampleText.text}"
Pada contoh berikut, tampilan TextView
mereferensikan tampilan EditText
dalam
tata letak yang sama:
<EditText
android:id="@+id/example_text"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<TextView
android:id="@+id/example_output"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{exampleText.text}"/>
Koleksi
Anda dapat mengakses koleksi umum, seperti array, daftar, daftar sparse, dan
peta, menggunakan operator []
untuk memudahkan.
<data>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<import type="java.util.List"/>
<variable name="list" type="List<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
...
android:text="@{list[index]}"
...
android:text="@{sparse[index]}"
...
android:text="@{map[key]}"
Anda juga dapat merujuk ke sebuah nilai di peta menggunakan notasi object.key
. Misalnya, Anda dapat mengganti @{map[key]}
pada contoh sebelumnya dengan
@{map.key}
.
Literal string
Anda dapat menggunakan tanda kutip tunggal untuk mengapit nilai atribut, yang memungkinkan Anda menggunakan tanda kutip ganda dalam ekspresi, seperti yang ditunjukkan dalam contoh berikut:
android:text='@{map["firstName"]}'
Anda juga dapat menggunakan tanda kutip ganda untuk mengapit nilai atribut. Saat melakukannya,
literal string harus diapit dengan tanda kutip terbalik `
, seperti yang ditunjukkan
di sini:
android:text="@{map[`firstName`]}"
Referensi
Sebuah ekspresi dapat mereferensikan resource aplikasi dengan sintaks berikut:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
Anda dapat mengevaluasi string format dan bentuk jamak dengan memberikan parameter:
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
Anda dapat meneruskan referensi properti dan referensi tampilan sebagai parameter resource:
android:text="@{@string/example_resource(user.lastName, exampleText.text)}"
Jika bentuk jamak mengambil beberapa parameter, teruskan semua parameter:
Have an orange
Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
Beberapa resource memerlukan evaluasi jenis eksplisit, seperti ditunjukkan dalam tabel berikut:
Jenis | 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 |
Pengendalian peristiwa
Data binding memungkinkan Anda menulis ekspresi yang menangani peristiwa yang dikirim dari
tampilan—misalnya, metode
onClick()
. Nama atribut peristiwa ditentukan berdasarkan nama metode pemroses,
dengan beberapa pengecualian. Misalnya,
View.OnClickListener
memiliki
metode onClick()
, sehingga atribut untuk peristiwa ini adalah android:onClick
.
Ada beberapa pengendali peristiwa khusus untuk peristiwa klik yang memerlukan
atribut selain android:onClick
untuk menghindari konflik. Anda dapat menggunakan
atribut berikut untuk menghindari jenis konflik ini:
Class | Setter pemroses | Atribut |
---|---|---|
SearchView |
setOnSearchClickListener(View.OnClickListener) |
android:onSearchClick |
ZoomControls |
setOnZoomInClickListener(View.OnClickListener) |
android:onZoomIn |
ZoomControls |
setOnZoomOutClickListener(View.OnClickListener) |
android:onZoomOut |
Anda dapat menggunakan dua mekanisme ini, yang dijelaskan secara mendetail di bagian berikut, untuk menangani peristiwa:
- Referensi metode: dalam ekspresi, Anda dapat mereferensikan metode yang sesuai dengan tanda tangan metode pemroses. Saat
ekspresi dievaluasi ke referensi metode, data binding menggabungkan referensi
metode dan objek pemilik dalam pemroses dan menetapkan pemroses tersebut pada
tampilan target. Jika ekspresi bernilai
null
, data binding tidak membuat pemroses dan menetapkan pemrosesnull
. - Binding pemroses: ini adalah ekspresi lambda yang dievaluasi saat peristiwa terjadi. Data binding selalu membuat pemroses, yang ditetapkan di tampilan. Saat peristiwa dikirim, pemroses akan mengevaluasi ekspresi lambda.
Referensi metode
Anda dapat mengikat peristiwa ke metode pengendali secara langsung, mirip dengan cara
menetapkan
android:onClick
ke
metode dalam aktivitas. Salah satu keunggulan dibandingkan dengan
atribut onClick
View
adalah
ekspresi diproses pada waktu kompilasi. Jadi, jika metode tersebut tidak ada atau
tanda tangannya salah, Anda akan menerima error waktu kompilasi.
Perbedaan utama antara referensi metode dan binding pemroses adalah penerapan pemroses yang sebenarnya dibuat saat data terikat, bukan saat peristiwa dipicu. Jika Anda memilih untuk mengevaluasi ekspresi saat peristiwa terjadi, gunakan binding pemroses.
Untuk menetapkan peristiwa ke pengendalinya, gunakan ekspresi binding normal, dengan nilainya berupa nama metode yang akan dipanggil. Misalnya, perhatikan contoh objek data tata letak berikut:
Kotlin
class MyHandlers { fun onClickFriend(view: View) { ... } }
Java
public class MyHandlers { public void onClickFriend(View view) { ... } }
Ekspresi binding dapat menetapkan pemroses klik untuk tampilan ke
metode onClickFriend()
, sebagai berikut:
<?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:
</LinearLayout>
</layout>
Binding pemroses
Binding pemroses adalah ekspresi binding yang berjalan saat peristiwa terjadi. Class ini mirip dengan referensi metode, tetapi memungkinkan Anda menjalankan ekspresi data binding arbitrer. Fitur ini tersedia di Plugin Android Gradle untuk Gradle versi 2.0 dan yang lebih baru.
Dalam referensi metode, parameter metode harus cocok dengan parameter pemroses peristiwa. Dalam binding pemroses, hanya nilai return yang harus cocok dengan
nilai return yang diharapkan dari pemroses, kecuali jika mengharapkan void
. Misalnya, pertimbangkan class presenter berikut yang memiliki metode
onSaveClick()
:
Kotlin
class Presenter { fun onSaveClick(task: Task){} }
Java
public class Presenter { public void onSaveClick(Task task){} }
Anda dapat mengikat peristiwa klik ke metode onSaveClick()
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: -> presenter.onSaveClick(task)}" />
</LinearLayout>
</layout>
Saat callback digunakan dalam ekspresi, data binding secara otomatis membuat pemroses yang diperlukan dan mendaftarkannya untuk peristiwa tersebut. Saat tampilan mengaktifkan peristiwa, data binding akan mengevaluasi ekspresi yang diberikan. Seperti ekspresi binding reguler, Anda akan mendapatkan keamanan data binding null dan thread saat ekspresi pemroses ini dievaluasi.
Pada contoh sebelumnya, parameter view
yang diteruskan ke onClick(View)
tidak ditentukan. Binding pemroses menyediakan dua pilihan untuk parameter pemroses:
Anda dapat mengabaikan semua parameter ke metode atau menamai semuanya. Jika memilih
untuk memberi nama parameter, Anda dapat menggunakannya dalam ekspresi. Misalnya, Anda
dapat menulis ekspresi sebelumnya sebagai berikut:
android: -> presenter.onSaveClick(task)}"
Jika ingin menggunakan parameter dalam ekspresi, Anda dapat melakukannya seperti berikut:
Kotlin
class Presenter { fun onSaveClick(view: View, task: Task){} }
Java
public class Presenter { public void onSaveClick(View view, Task task){} }
android: -> presenter.onSaveClick(theView, task)}"
Anda juga dapat menggunakan ekspresi lambda dengan lebih dari satu parameter:
Kotlin
class Presenter { fun onCompletedChanged(task: Task, completed: Boolean){} }
Java
public class Presenter { public void onCompletedChanged(Task task, boolean completed){} }
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
android: isChecked) -> presenter.completeChanged(task, isChecked)}" />
Jika peristiwa yang Anda proses menampilkan nilai yang jenisnya bukan void
, ekspresi Anda juga harus menampilkan jenis nilai yang sama. Misalnya, jika Anda ingin
memproses peristiwa sentuh lama (klik lama), ekspresi Anda harus menampilkan
boolean.
Kotlin
class Presenter { fun onLongClick(view: View, task: Task): Boolean { } }
Java
public class Presenter { public boolean onLongClick(View view, Task task) { } }
android: -> presenter.onLongClick(theView, task)}"
Jika ekspresi tidak dapat dievaluasi karena objek null
, data binding akan menampilkan
nilai default untuk jenis tersebut, seperti null
untuk jenis referensi, 0
untuk
int
, atau false
untuk boolean
.
Jika Anda perlu menggunakan ekspresi dengan predikat—misalnya, ternary—Anda dapat menggunakan void
sebagai simbol:
android: -> v.isVisible() ? doSomething() : void}"
Menghindari pemroses yang rumit
Ekspresi pemroses sangat canggih dan dapat membuat kode Anda lebih mudah dibaca. Di sisi lain, pemroses yang berisi ekspresi kompleks membuat tata letak Anda lebih sulit dibaca dan dikelola. Buat ekspresi Anda tetap sederhana seperti meneruskan data yang tersedia dari UI ke metode callback Anda. Terapkan logika bisnis apa pun dalam metode callback yang Anda panggil dari ekspresi pemroses.
Import, variable, dan include
Library Data Binding menyediakan fitur seperti impor, variabel, dan sertakan. Impor membuat class yang mudah dirujuk di dalam file tata letak Anda. Variabel memungkinkan Anda mendeskripsikan properti yang dapat digunakan dalam ekspresi binding. Include memungkinkan Anda menggunakan kembali tata letak kompleks di berbagai aplikasi.
Import
Impor memungkinkan Anda mereferensikan class di dalam file tata letak, seperti dalam kode terkelola.
Anda dapat menggunakan nol atau beberapa elemen import
di dalam elemen data
. Contoh
kode berikut mengimpor class View
ke file tata letak:
<data>
<import type="android.view.View"/>
</data>
Mengimpor class View
memungkinkan Anda mereferensikannya dari ekspresi binding.
Contoh berikut menunjukkan cara mereferensikan
konstanta VISIBLE
dan
GONE
class View
:
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
Mengetik alias
Jika ada konflik nama class, Anda dapat mengganti nama salah satu class menjadi
alias. Contoh berikut mengganti nama class View
dalam
paket com.example.real.estate
menjadi Vista
:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
Anda kemudian dapat menggunakan Vista
untuk mereferensikan com.example.real.estate.View
dan View
untuk mereferensikan android.view.View
dalam file tata letak.
Mengimpor class lain
Anda dapat menggunakan jenis yang diimpor sebagai referensi jenis dalam variabel dan ekspresi. Contoh berikut menunjukkan User
dan List
yang digunakan sebagai jenis variabel:
<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
Anda dapat menggunakan jenis yang diimpor untuk mentransmisikan bagian ekspresi. Contoh berikut
mentransmisikan properti connection
ke jenis User
:
<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Anda juga dapat menggunakan jenis yang diimpor saat mereferensikan kolom dan metode statis dalam ekspresi. Kode berikut mengimpor class MyStringUtils
dan mereferensikan
metode capitalize
-nya:
<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 dalam kode terkelola, java.lang.*
otomatis diimpor.
Variabel
Anda dapat menggunakan beberapa elemen variable
di dalam elemen data
. Setiap
elemen variable
menjelaskan properti yang dapat ditetapkan pada tata letak yang akan digunakan
dalam ekspresi binding dalam file tata letak. Contoh berikut mendeklarasikan
variabel user
, image
, dan note
:
<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>
Jenis variabel diperiksa pada waktu kompilasi, jadi jika sebuah variabel mengimplementasikan
Observable
atau merupakan
koleksi yang dapat diamati,
hal itu harus tercermin dalam jenisnya. Jika variabel adalah class dasar atau antarmuka yang tidak menerapkan antarmuka Observable
, variabel tidak akan diamati.
Jika ada file tata letak berbeda untuk berbagai konfigurasi (misalnya, lanskap atau potret), variabel akan digabungkan. Tidak boleh ada definisi variabel yang bertentangan di antara file tata letak ini.
Class binding yang dihasilkan memiliki penyetel dan pengambil untuk setiap variabel
yang dijelaskan. Variabel ini mengambil nilai kode terkelola default hingga penyetel
dipanggil—null
untuk jenis referensi, 0
untuk int
, false
untuk
boolean
, dll.
Variabel khusus bernama context
dibuat untuk digunakan dalam ekspresi binding
sesuai kebutuhan. Nilai untuk context
adalah
objek Context
dari metode
getContext()
tampilan root. Variabel
context
diganti oleh deklarasi variabel eksplisit dengan nama
tersebut.
Include
Anda dapat meneruskan variabel ke dalam binding tata letak yang disertakan dari tata letak
yang memuatnya menggunakan namespace aplikasi dan nama variabel dalam atribut. Contoh
berikut menunjukkan variabel user
yang disertakan dari file tata letak name.xml
dan
contact.xml
:
<?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>
Data binding tidak mendukung penyertaan sebagai turunan langsung dari elemen penggabungan. Misalnya, tata letak 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><!-- Doesn't work -->
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</merge>
</layout>
Referensi lainnya
Untuk mempelajari data binding lebih lanjut, lihat referensi tambahan berikut.