Adaptor binding bertanggung jawab untuk membuat panggilan framework yang sesuai ke
tetapkan nilai. Salah satu contohnya adalah menetapkan nilai properti, seperti memanggil
Metode setText()
. Lainnya
misalnya adalah menyetel pemroses peristiwa, seperti memanggil
setOnClickListener()
.
Library Data Binding memungkinkan Anda menentukan metode yang dipanggil untuk menetapkan nilai, menyediakan logika binding Anda sendiri, dan menentukan jenis objek yang ditampilkan dengan menggunakan adaptor.
Menetapkan nilai atribut
Setiap kali nilai terikat berubah, class binding yang dihasilkan harus memanggil penyetel pada tampilan dengan ekspresi binding. Anda dapat membiarkan Data Binding Library secara otomatis menentukan metode, atau Anda dapat secara eksplisit mendeklarasikan atau menyediakan logika khusus untuk memilih metode.
Pemilihan metode otomatis
Untuk atribut bernama example
, library otomatis menemukan metode tersebut
setExample(arg)
yang menerima jenis yang kompatibel sebagai argumen. Namespace
atribut ini tidak dipertimbangkan. Hanya nama dan jenis atribut yang digunakan
saat mencari metode.
Misalnya, dengan ekspresi android:text="@{user.name}"
, library
mencari metode setText(arg)
yang menerima jenis yang ditampilkan oleh
user.getName()
. Jika jenis nilai yang ditampilkan user.getName()
adalah String
,
library mencari metode setText()
yang menerima argumen String
. Jika
menampilkan int
, library akan menelusuri metode setText()
yang
menerima argumen int
. Ekspresi harus menampilkan jenis yang benar. Anda dapat
gunakan nilai yang ditampilkan jika perlu.
Data binding berfungsi meskipun tidak ada atribut dengan nama yang ditentukan. Anda dapat
membuat atribut untuk penyetel apa pun
dengan menggunakan pengikatan data. Misalnya, dukungan
kelas
DrawerLayout
tidak memiliki atribut, tetapi memiliki banyak penyetel. Tata letak berikut
secara otomatis menggunakan
setScrimColor(int)
dan
addDrawerListener(DrawerListener)
metode sebagai penyetel untuk app:scrimColor
dan app:drawerListener
masing-masing:
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
Menentukan nama metode kustom
Beberapa atribut memiliki penyetel yang tidak melakukan pencocokan berdasarkan nama. Dalam situasi ini,
dapat dikaitkan dengan penyetel menggunakan
BindingMethods
anotasi. Anotasi digunakan dengan class dan dapat berisi beberapa
BindingMethod
anotasi, satu untuk setiap metode
yang diganti namanya. Metode binding adalah anotasi yang
yang dapat Anda tambahkan ke kelas mana pun di aplikasi Anda.
Pada contoh berikut, atribut android:tint
dikaitkan dengan
setImageTintList(ColorStateList)
—bukan dengan metode setTint()
:
Kotlin
@BindingMethods(value = [ BindingMethod( type = android.widget.ImageView::class, attribute = "android:tint", method = "setImageTintList")])
Java
@BindingMethods({ @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"), })
Biasanya, Anda tidak perlu mengganti nama penyetel di class framework Android. Tujuan sudah diimplementasikan menggunakan konvensi nama untuk menemukan metode yang cocok.
Menyediakan logika kustom
Beberapa atribut memerlukan logika binding kustom. Misalnya, tidak ada data terkait
penyetel untuk atribut android:paddingLeft
. Sebagai gantinya, metode setPadding(left,
top, right, bottom)
disediakan. Metode adaptor binding statis dengan
BindingAdapter
memungkinkan Anda menyesuaikan cara penyetel dipanggil.
Atribut class framework Android sudah memiliki BindingAdapter
anotasi. Contoh berikut menunjukkan adaptor binding untuk
Atribut paddingLeft
:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, padding: Int) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) }
Java
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int padding) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); }
Jenis parameter itu penting. Parameter pertama menentukan jenis tampilan yang terkait dengan atribut. Parameter kedua menentukan jenis yang diterima dalam ekspresi binding untuk atribut yang diberikan.
Adaptor binding juga berguna untuk jenis penyesuaian lainnya. Misalnya, loader kustom bisa dipanggil dari thread pekerja untuk memuat gambar.
Anda juga bisa memiliki adaptor yang menerima beberapa atribut, seperti yang ditunjukkan dalam contoh berikut:
Kotlin
@BindingAdapter("imageUrl", "error") fun loadImage(view: ImageView, url: String, error: Drawable) { Picasso.get().load(url).error(error).into(view) }
Java
@BindingAdapter({"imageUrl", "error"}) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.get().load(url).error(error).into(view); }
Anda dapat menggunakan adaptor di tata letak, seperti ditunjukkan dalam contoh berikut. Catatan
bahwa @drawable/venueError
merujuk ke resource dalam aplikasi Anda. Mengelilingi
resource dengan @{}
akan menjadikannya ekspresi binding yang valid.
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
Adaptor dipanggil jika imageUrl
dan error
digunakan untuk
Objek ImageView
, imageUrl
adalah
dan error
adalah
Drawable
Jika Anda ingin
adaptor yang akan dipanggil saat ada atribut yang diatur, atur atribut
requireAll
flag adaptor ke false
, seperti yang ditunjukkan pada contoh berikut:
Kotlin
@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false) fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } }
Java
@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false) public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } }
Metode adaptor binding dapat mengambil nilai lama di pengendalinya. Sebuah metode mengambil nilai lama dan baru harus mendeklarasikan semua nilai lama untuk atribut terlebih dahulu, diikuti dengan nilai baru, seperti yang ditunjukkan dalam contoh berikut:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) { if (oldPadding != newPadding) { view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) } }
Java
@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()); } }
Pengendali peristiwa hanya dapat digunakan dengan antarmuka atau class abstrak dengan satu abstrak, seperti yang ditunjukkan pada contoh berikut:
Kotlin
@BindingAdapter("android:onLayoutChange") fun setOnLayoutChangeListener( view: View, oldValue: View.OnLayoutChangeListener?, newValue: View.OnLayoutChangeListener? ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (oldValue != null) { view.removeOnLayoutChangeListener(oldValue) } if (newValue != null) { view.addOnLayoutChangeListener(newValue) } } }
Java
@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); } } }
Gunakan pengendali peristiwa ini dalam tata letak Anda sebagai berikut:
<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
Jika memiliki beberapa metode, suatu pemroses harus dipecah menjadi beberapa pemroses.
Misalnya,
View.OnAttachStateChangeListener
memiliki dua metode:
onViewAttachedToWindow(View)
dan
onViewDetachedFromWindow(View)
.
Library ini menyediakan dua antarmuka untuk membedakan atribut dan pengendali
untuk mereka:
Kotlin
// Translation from provided interfaces in Java: @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewDetachedFromWindow { fun onViewDetachedFromWindow(v: View) } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewAttachedToWindow { fun onViewAttachedToWindow(v: View) }
Java
@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 pemroses bisa memengaruhi yang lain, Anda memerlukan adaptor yang
berfungsi untuk salah satu atribut atau keduanya. Anda dapat menetapkan requireAll
ke false
di
anotasi untuk menentukan bahwa tidak setiap atribut harus diberi binding
seperti yang ditunjukkan dalam contoh berikut:
Kotlin
@BindingAdapter( "android:onViewDetachedFromWindow", "android:onViewAttachedToWindow", requireAll = false ) fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { val newListener: View.OnAttachStateChangeListener? newListener = if (detach == null && attach == null) { null } else { object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { attach.onViewAttachedToWindow(v) } override fun onViewDetachedFromWindow(v: View) { detach.onViewDetachedFromWindow(v) } } } val oldListener: View.OnAttachStateChangeListener? = ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener) if (oldListener != null) { view.removeOnAttachStateChangeListener(oldListener) } if (newListener != null) { view.addOnAttachStateChangeListener(newListener) } } }
Java
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"}, requireAll=false) public static void setListener(View view, OnViewDetachedFromWindow detach, OnViewAttachedToWindow attach) { if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) { 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); } } }; } 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 rumit karena
Class View
menggunakan
addOnAttachStateChangeListener()
dan
removeOnAttachStateChangeListener()
metode alih-alih metode penyetel untuk
OnAttachStateChangeListener
.
Class android.databinding.adapters.ListenerUtil
membantu memantau
pemroses sehingga dapat dihapus di adaptor binding.
Konversi objek
Konversi objek otomatis
Saat Object
ditampilkan dari binding
ekspresi tersebut, pustaka akan memilih metode yang digunakan untuk menyetel nilai
saat ini. Object
ditransmisikan ke jenis parameter metode yang dipilih. Ini
lebih praktis dalam aplikasi yang menggunakan
Class ObservableMap
ke
menyimpan data, seperti yang ditunjukkan pada contoh berikut:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Objek userMap
dalam ekspresi menampilkan nilai, yang secara otomatis
transmisi ke jenis parameter yang ditemukan dalam metode setText(CharSequence)
yang digunakan untuk
tetapkan nilai atribut android:text
. Jika jenis parameternya adalah
ambigu, mentransmisikan jenis nilai yang ditampilkan dalam ekspresi.
Konversi kustom
Dalam beberapa situasi, konversi kustom diperlukan di antara jenis-jenis tertentu. Sebagai
contoh, atribut android:background
tampilan mengharapkan Drawable
, tetapi
nilai color
yang ditentukan adalah bilangan bulat. Contoh berikut menunjukkan
yang mengharapkan Drawable
, tetapi yang diberikan adalah bilangan bulat:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Setiap kali Drawable
diharapkan dan bilangan bulat ditampilkan, konversikan int
ke ColorDrawable
.
Untuk melakukan konversi, gunakan metode statis dengan
BindingConversion
anotasi, sebagai berikut:
Kotlin
@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)
Java
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
Namun, jenis nilai yang diberikan dalam ekspresi binding harus konsisten. Anda tidak dapat menggunakan jenis yang berbeda dalam ekspresi yang sama, seperti yang ditunjukkan di bawah contoh:
// The @drawable and @color represent different value types in the same
// expression, which causes a build error.
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Referensi lainnya
Untuk mempelajari data binding lebih lanjut, lihat referensi berikut.
Contoh
Codelab
Postingan blog
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Library Data Binding
- Tata letak dan ekspresi binding
- View binding