Bahasa ekspresi memungkinkan Anda menulis ekspresi yang menangani peristiwa yang dikirim menurut penayangan. 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 {i>root<i}
layout
, diikuti dengan elemen data
dan elemen root view
. Tampilan ini
adalah akar Anda 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 dalam properti atribut menggunakan atribut
Sintaksis @{}
. Pada contoh berikut,
Teks TextView
disetel ke
Properti firstName
dari variabel user
:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
Objek data
Misalkan 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. Dimungkinkan juga untuk menggunakan objek yang mengikuti serangkaian konvensi, seperti menggunakan metode pengakses di 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. Tujuan
ekspresi @{user.firstName}
yang digunakan untuk
android:text
mengakses kolom firstName
di class sebelumnya dan
getFirstName()
di class kedua. Hal ini juga diselesaikan untuk
firstName()
, jika metode tersebut ada.
Mengikat data
Class binding dibuat untuk setiap file tata letak. Secara {i>default<i}, nama
didasarkan pada nama file tata letak, dikonversi ke Pascal case, dengan
akhiran Binding 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 dapat
dapatkan tampilannya menggunakan
LayoutInflater
, seperti yang ditunjukkan dalam
contoh berikut:
Kotlin
val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
Java
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
Jika Anda menggunakan item data binding di dalam
Fragment
,
ListView
, atau
RecyclerView
adaptor, Anda dapat memilih untuk menggunakan
inflate()
dari class binding atau
DataBindingUtil
, sebagai
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:
+
- Logis:
&& ||
- Biner:
& | ^
- Uner:
+ - ! ~
- Shift:
>> >>> <<
- Perbandingan:
== > < >= <=
(<
harus di-escape sebagai<
) instanceof
- Pengelompokan:
()
- Literal, seperti karakter, String, numerik,
null
- Transmisi
- 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 (??
) memilih operand kiri jika nilainya bukan null
atau kanan jika yang pertama adalah null
:
android:text="@{user.displayName ?? user.lastName}"
Secara fungsional, ini setara dengan hal berikut:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
Referensi properti
Ekspresi dapat mereferensikan properti di class dengan menggunakan format berikut,
yang sama untuk {i>field<i}, pengambil, dan
ObservableField
objek:
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
adalah null, user.name
diberi nilai default null
. Jika Anda
referensi user.age
, jika usia berjenis int
, maka data binding menggunakan
nilai default 0
.
Referensi tampilan
Ekspresi dapat merujuk tampilan lain dalam tata letak menurut ID, menggunakan elemen sintaksis:
android:text="@{exampleText.text}"
Dalam contoh berikut, tampilan TextView
mereferensikan tampilan EditText
di
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, list, list 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
. Sebagai
misalnya, Anda dapat mengganti @{map[key]}
dalam contoh sebelumnya dengan
@{map.key}
.
Literal string
Anda dapat menggunakan tanda kutip tunggal untuk mengapit nilai atribut, sehingga Anda dapat menggunakan tanda kutip ganda dalam ekspresi, seperti yang ditunjukkan pada 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 tampilan referensi sebagai parameter resource:
android:text="@{@string/example_resource(user.lastName, exampleText.text)}"
Jika bentuk jamak memerlukan beberapa parameter, teruskan semua parameter:
Have an orange
Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
Beberapa resource memerlukan evaluasi jenis eksplisit, seperti yang ditunjukkan dalam contoh berikut tabel:
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 penanganan peristiwa yang dikirim dari
tampilan—misalnya,
onClick()
. Nama atribut peristiwa ditentukan oleh 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
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 kedua mekanisme ini, yang dijelaskan secara rinci di bagian yang ikuti, untuk menangani peristiwa:
- Referensi metode: dalam ekspresi, Anda dapat
metode referensi yang sesuai dengan tanda tangan metode pemroses. Kapan
ekspresi dievaluasi ke referensi metode, data binding menggabungkan metode
objek pemilik dan referensi dalam pemroses dan menyetel pemroses tersebut di
tampilan target. Jika ekspresi bernilai
null
, data binding tidak buat pemroses dan tetapkan pemrosesnull
sebagai gantinya. - Binding pemroses: ini adalah ekspresi lambda yang dievaluasi saat peristiwa terjadi. Data binding selalu membuat pemroses, yang ditetapkannya pada tampilan. Ketika peristiwa dikirim, metode akan mengevaluasi ekspresi lambda.
Referensi metode
Anda bisa mengikat peristiwa ke metode pengendali secara langsung, mirip dengan cara
tugaskan
android:onClick
ke
dalam suatu aktivitas. Satu keuntungan dibandingkan dengan
Atribut onClick
View
adalah
ekspresi tersebut diproses pada waktu kompilasi. Jadi, jika metode itu
tidak ada, atau
tanda tangan salah, Anda akan menerima kesalahan waktu kompilasi.
Perbedaan utama antara referensi metode dan binding pemroses adalah bahwa implementasi pemroses aktual dibuat saat data terikat, bukan saat dipicu. Jika Anda memilih untuk mengevaluasi ekspresi saat peristiwa terjadi, gunakan binding pemroses.
Untuk menetapkan sebuah peristiwa ke pengendalinya, gunakan ekspresi binding normal, dengan menjadi nama metode yang akan dipanggil. Misalnya, perhatikan contoh berikut objek data tata letak:
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
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:onClick="@{handlers::onClickFriend}"/>
</LinearLayout>
</layout>
Binding pemroses
Binding pemroses adalah ekspresi binding yang berjalan saat peristiwa terjadi. Mereka mirip dengan referensi metode, tetapi memungkinkan Anda menjalankan ekspresi. Fitur ini tersedia dengan 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 yang ditampilkan yang harus cocok dengan
nilai hasil yang diharapkan dari pemroses, kecuali jika nilai yang ditampilkan adalah void
. Sebagai
contoh, perhatikan class presenter berikut yang memiliki onSaveClick()
berikut:
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:onClick="@{() -> presenter.onSaveClick(task)}" />
</LinearLayout>
</layout>
Saat callback digunakan dalam ekspresi, data binding secara otomatis akan membuat pemroses yang diperlukan dan mendaftarkannya ke peristiwa tersebut. Saat tampilan mengaktifkan , data binding mengevaluasi ekspresi yang diberikan. Seperti halnya binding biasa ekspresi, Anda akan mendapatkan keamanan null dan thread dari data binding sementara ekspresi pemroses yang akan 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
pada metode atau memberi nama semuanya. Jika Anda lebih suka
untuk memberi nama parameter, Anda dapat
menggunakannya dalam ekspresi. Sebagai contoh, Anda
dapat menulis ekspresi sebelumnya sebagai berikut:
android:onClick="@{(view) -> 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:onClick="@{(theView) -> 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:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
Jika peristiwa yang Anda proses menampilkan nilai yang jenisnya bukan void
,
ekspresi akan mengembalikan jenis nilai yang sama. Misalnya, jika Anda ingin
untuk mendengarkan sentuhan & tahan (klik lama), ekspresi Anda harus mengembalikan
boolean.
Kotlin
class Presenter { fun onLongClick(view: View, task: Task): Boolean { } }
Java
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
, data binding akan ditampilkan
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:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
Hindari pemroses yang rumit
Ekspresi pemroses sangat canggih dan dapat membuat kode Anda lebih mudah dibaca. Pada sedangkan pemroses yang berisi ekspresi kompleks akan membuat tata letak Anda lebih sulit untuk dibaca dan dikelola. Membuat ekspresi Anda sesederhana meneruskan data yang tersedia dari UI ke metode callback Anda. Implementasikan logika bisnis apa pun di dalam yang Anda panggil dari ekspresi pemroses.
Import, variable, dan include
Pustaka Data Binding menyediakan fitur seperti impor, variabel, dan disertakan. Impor membuat class yang mudah dirujuk di dalam file tata letak Anda. Variabel memungkinkan Anda menjelaskan 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
. Tujuan
contoh kode berikut mengimpor class View
ke file tata letak:
<data>
<import type="android.view.View"/>
</data>
Dengan mengimpor class View
, Anda dapat mereferensikannya dari ekspresi binding.
Contoh berikut menunjukkan cara mereferensikan
VISIBLE
dan
Konstanta GONE
dari 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 kelas, Anda dapat mengganti nama salah satu class menjadi
alias. Contoh berikut mengganti nama class View
di
Paket com.example.real.estate
ke 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. Tujuan
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. Hal berikut
contoh 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 statis dan metode di
ekspresi. Kode berikut mengimpor class dan referensi MyStringUtils
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
. Masing-masing
Elemen variable
menjelaskan properti yang dapat ditetapkan pada tata letak yang akan digunakan
dalam ekspresi binding di 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 variabel mengimplementasikan
Observable
atau merupakan
koleksi yang dapat diamati,
yang harus tercermin dalam jenisnya. Jika variabelnya adalah antarmuka atau class dasar
yang tidak mengimplementasikan antarmuka Observable
, variabelnya tidak
diamati.
Ketika ada file tata letak yang berbeda untuk berbagai konfigurasi (misalnya, lanskap atau potret), variabel tersebut 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. Variabel 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
secara permanen sesuai kebutuhan. Nilai untuk context
adalah nilai
Objek Context
dari tampilan root
Metode getContext()
. Tujuan
Variabel context
diganti oleh deklarasi variabel eksplisit dengannya
nama.
Include
Anda dapat meneruskan variabel ke binding tata letak yang disertakan dari elemen
dengan menggunakan namespace aplikasi dan nama variabel dalam atribut. Tujuan
contoh berikut menunjukkan variabel user
yang disertakan dari name.xml
dan
File tata letak 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 include sebagai turunan langsung dari elemen gabungan. 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.