Adaptor binding bertanggung jawab membuat panggilan framework yang sesuai untuk
menetapkan nilai. Salah satu contohnya adalah menetapkan nilai properti, seperti memanggil metode setText()
. Contoh lainnya adalah menetapkan pemroses peristiwa, seperti memanggil metode setOnClickListener()
.
Dengan Library Data Binding, Anda dapat menentukan metode yang dipanggil untuk menetapkan nilai, menyediakan logika binding Anda sendiri, dan menentukan jenis objek yang ditampilkan menggunakan adaptor.
Menetapkan nilai atribut
Setiap kali nilai terikat berubah, class binding yang dihasilkan harus memanggil metode penyetel pada tampilan yang memiliki ekspresi binding. Anda dapat mengizinkan Library Data Binding secara otomatis menentukan metode, atau Anda dapat mendeklarasikan metode secara eksplisit atau memberikan logika kustom untuk memilih metode.
Pemilihan metode otomatis
Untuk atribut bernama example
, library akan otomatis menemukan metode
setExample(arg)
yang menerima jenis yang kompatibel sebagai argumen. Namespace
atribut tidak dipertimbangkan. Hanya nama dan jenis atribut yang digunakan
saat menelusuri metode.
Misalnya, dengan ekspresi android:text="@{user.name}"
, library
akan mencari metode setText(arg)
yang menerima jenis yang ditampilkan oleh
user.getName()
. Jika jenis nilai yang ditampilkan user.getName()
adalah String
, library akan mencari metode setText()
yang menerima argumen String
. Jika ekspresi menampilkan int
, library akan menelusuri metode setText()
yang menerima argumen int
. Ekspresi harus menampilkan jenis yang benar. Anda dapat
mengirimkan nilai hasil jika perlu.
Data binding berfungsi meskipun tidak ada atribut dengan nama yang ditentukan. Anda dapat
membuat atribut untuk penyetel apa pun dengan menggunakan data binding. Misalnya, class
dukungan
DrawerLayout
tidak memiliki atribut, tetapi memiliki banyak penyetel. Tata letak berikut
secara otomatis menggunakan metode
setScrimColor(int)
dan
addDrawerListener(DrawerListener)
sebagai penyetel untuk masing-masing atribut
app:scrimColor
dan app:drawerListener
:
<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, atribut dapat dikaitkan dengan penyetel menggunakan
anotasi
BindingMethods
. Anotasi ini digunakan dengan class dan dapat berisi beberapa anotasi
BindingMethod
, satu untuk setiap metode yang diganti namanya. Metode binding adalah anotasi yang
dapat Anda tambahkan ke class mana pun di aplikasi.
Pada contoh berikut, atribut android:tint
dikaitkan dengan
metode 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. Atribut tersebut sudah diimplementasikan menggunakan konvensi nama untuk menemukan metode yang cocok secara otomatis.
Menyediakan logika kustom
Beberapa atribut memerlukan logika binding kustom. Misalnya, tidak ada penyetel
terkait untuk atribut android:paddingLeft
. Sebagai gantinya, metode setPadding(left,
top, right, bottom)
disediakan. Metode adaptor binding statis dengan
anotasi BindingAdapter
memungkinkan Anda menyesuaikan cara pemanggilan penyetel untuk atribut.
Atribut class framework Android sudah memiliki anotasi
BindingAdapter
. 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 dikaitkan 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 dapat dipanggil dari thread pekerja untuk memuat gambar.
Anda juga dapat 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 yang ditunjukkan pada contoh berikut. Perlu
diperhatikan bahwa @drawable/venueError
merujuk pada resource dalam aplikasi Anda. Mengapit
resource dengan @{}
membuatnya menjadi 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
string, dan error
adalah
Drawable
. Jika Anda ingin
adaptor dipanggil saat salah satu atribut ditetapkan, setel flag
requireAll
opsional pada adaptor ke false
, seperti ditunjukkan dalam 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 bisa menggunakan nilai lama dalam pengendalinya. Metode yang mengambil nilai lama dan baru harus mendeklarasikan semua nilai lama untuk atribut terlebih dahulu, kemudian 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 yang memiliki satu metode abstrak, seperti ditunjukkan dalam 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 keduanya:
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 dapat memengaruhi pemroses yang lain, Anda memerlukan adaptor yang berfungsi untuk salah satu atribut atau keduanya. Anda dapat menetapkan requireAll
ke false
dalam
anotasi untuk menentukan bahwa tidak setiap atribut harus diberi ekspresi
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 metode
addOnAttachStateChangeListener()
dan
removeOnAttachStateChangeListener()
,
bukan metode penyetel untuk
OnAttachStateChangeListener
.
Class android.databinding.adapters.ListenerUtil
membantu melacak pemroses ini sehingga dapat dihapus di adaptor binding.
Konversi objek
Konversi objek otomatis
Saat Object
ditampilkan dari ekspresi
binding, library akan memilih metode yang digunakan untuk menetapkan nilai
properti. Object
ditransmisikan ke jenis parameter metode yang dipilih. Perilaku
ini praktis pada aplikasi yang menggunakan
class ObservableMap
untuk
menyimpan data, seperti yang ditunjukkan dalam contoh berikut:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Objek userMap
dalam ekspresi ini menampilkan nilai, yang otomatis
ditransmisikan ke jenis parameter yang ditemukan dalam metode setText(CharSequence)
yang digunakan untuk
menetapkan nilai atribut android:text
. Jika jenis parameter ambigu, transmisikan jenis nilai yang ditampilkan dalam ekspresi.
Konversi kustom
Dalam beberapa situasi, konversi kustom diperlukan di antara jenis-jenis tertentu. Misalnya, atribut android:background
dari sebuah tampilan mengharapkan Drawable
, tetapi
nilai color
yang ditentukan adalah bilangan bulat. Contoh berikut menunjukkan
atribut 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
menjadi ColorDrawable
.
Untuk melakukan konversi, gunakan metode statis dengan anotasi
BindingConversion
, 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 pada contoh berikut:
// 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