Bộ chuyển đổi liên kết chịu trách nhiệm thực hiện các lệnh gọi khung thích hợp đến
đặt giá trị. Một ví dụ là đặt giá trị thuộc tính, như gọi thuộc tính
Phương thức setText()
. Khác
Ví dụ: thiết lập trình nghe sự kiện, chẳng hạn như gọi
setOnClickListener()
.
Thư viện liên kết dữ liệu cho phép bạn chỉ định phương thức được gọi để đặt giá trị, cung cấp logic liên kết của riêng bạn và chỉ định loại đối tượng được trả về bằng cách bằng cách sử dụng bộ chuyển đổi.
Đặt giá trị thuộc tính
Bất cứ khi nào một giá trị ràng buộc thay đổi, lớp liên kết được tạo phải gọi một phương thức setter trên khung hiển thị có biểu thức liên kết. Bạn có thể cho phép Liên kết dữ liệu Thư viện sẽ tự động xác định phương thức hoặc bạn có thể khai báo rõ ràng hoặc cung cấp logic tuỳ chỉnh để chọn một phương thức.
Lựa chọn phương thức tự động
Đối với thuộc tính có tên example
, thư viện sẽ tự động tìm phương thức
setExample(arg)
chấp nhận các loại tương thích làm đối số. Không gian tên
thuộc tính đó sẽ không được xem xét. Chỉ sử dụng tên và loại thuộc tính
khi tìm kiếm một phương thức.
Ví dụ: với biểu thức android:text="@{user.name}"
, thư viện
tìm phương thức setText(arg)
chấp nhận loại được trả về bởi
user.getName()
. Nếu loại dữ liệu trả về của user.getName()
là String
, thì
thư viện sẽ tìm phương thức setText()
chấp nhận đối số String
. Nếu
biểu thức trả về int
, thư viện sẽ tìm kiếm phương thức setText()
chấp nhận đối số int
. Biểu thức phải trả về đúng loại. Bạn có thể
truyền giá trị trả về nếu cần.
Liên kết dữ liệu hoạt động ngay cả khi không có thuộc tính nào có tên đã cho. Bạn có thể
tạo thuộc tính cho bất kỳ phương thức setter nào bằng cách sử dụng liên kết dữ liệu. Ví dụ: hỗ trợ
lớp
DrawerLayout
không có thuộc tính nào nhưng có nhiều phương thức setter. Bố cục sau đây
tự động sử dụng
setScrimColor(int)
và
addDrawerListener(DrawerListener)
làm phương thức setter cho app:scrimColor
và app:drawerListener
các thuộc tính tương ứng:
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
Chỉ định tên phương thức tuỳ chỉnh
Một số thuộc tính có phương thức setter không khớp theo tên. Trong những trường hợp này, việc
có thể được liên kết với phương thức setter bằng cách sử dụng
BindingMethods
của bạn. Chú giải này được dùng với một lớp và có thể chứa nhiều
BindingMethod
các chú giải, một chú giải cho mỗi phương thức đã đổi tên. Phương thức liên kết là các chú giải
bạn có thể thêm vào bất kỳ lớp học nào trong ứng dụng của mình.
Trong ví dụ sau, thuộc tính android:tint
được liên kết với
setImageTintList(ColorStateList)
chứ không phải bằng phương thức 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"), })
Thông thường, bạn không cần đổi tên phương thức setter trong các lớp khung Android. Chiến lược phát hành đĩa đơn các thuộc tính đã được triển khai bằng cách sử dụng quy ước tên để tự động tìm phương thức so khớp.
Cung cấp logic tuỳ chỉnh
Một số thuộc tính cần logic liên kết tuỳ chỉnh. Ví dụ: không có
phương thức setter cho thuộc tính android:paddingLeft
. Thay vào đó, phương thức setPadding(left,
top, right, bottom)
được cung cấp. Phương thức bộ chuyển đổi liên kết tĩnh với
BindingAdapter
chú giải này cho phép bạn tuỳ chỉnh cách gọi phương thức setter cho một thuộc tính.
Các thuộc tính của các lớp trong khung Android đã có BindingAdapter
các chú thích. Ví dụ sau đây minh hoạ phương thức điều hợp liên kết (binding adapter) cho
Thuộc tính 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()); }
Các loại thông số rất quan trọng. Tham số đầu tiên xác định loại thành phần hiển thị đã liên kết với thuộc tính đó. Tham số thứ hai xác định loại được chấp nhận trong biểu thức liên kết của thuộc tính đã cho.
Bộ chuyển đổi liên kết cũng hữu ích cho các loại tuỳ chỉnh khác. Ví dụ: bạn có thể gọi một trình tải tuỳ chỉnh từ một luồng worker để tải hình ảnh.
Bạn cũng có thể có các bộ chuyển đổi nhận nhiều thuộc tính, như minh hoạ trong ví dụ sau:
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); }
Bạn có thể sử dụng trình chuyển đổi trong bố cục, như trong ví dụ sau. Ghi chú
@drawable/venueError
tham chiếu đến một tài nguyên trong ứng dụng của bạn. Xung quanh
có @{}
sẽ làm cho nó trở thành một biểu thức liên kết hợp lệ.
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
Bộ chuyển đổi sẽ được gọi nếu imageUrl
và error
được dùng cho một
Đối tượng ImageView
, imageUrl
là một
và error
là một
Drawable
. Nếu bạn muốn
bộ chuyển đổi sẽ được gọi khi bất kỳ thuộc tính nào được thiết lập, hãy đặt thuộc tính
requireAll
cờ của bộ chuyển đổi sang false
, như trong ví dụ sau:
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); } }
Phương thức bộ chuyển đổi liên kết có thể lấy các giá trị cũ trong trình xử lý. Một phương thức việc lấy các giá trị cũ và giá trị mới trước tiên phải khai báo tất cả các giá trị cũ cho các thuộc tính. tiếp theo là các giá trị mới như trong ví dụ sau:
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()); } }
Bạn chỉ có thể dùng trình xử lý sự kiện với các giao diện hoặc lớp trừu tượng có một như trong ví dụ sau:
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); } } }
Sử dụng trình xử lý sự kiện này trong bố cục của bạn như sau:
<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
Khi một trình nghe có nhiều phương thức, bạn phải chia trình nghe đó thành nhiều trình nghe.
Ví dụ:
View.OnAttachStateChangeListener
có hai phương thức:
onViewAttachedToWindow(View)
và
onViewDetachedFromWindow(View)
.
Thư viện này cung cấp 2 giao diện để phân biệt các thuộc tính và trình xử lý
cho họ:
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); }
Vì việc thay đổi một trình nghe có thể ảnh hưởng đến trình nghe kia, nên bạn cần có một trình chuyển đổi
phù hợp cho một trong hai thuộc tính hoặc cho cả hai. Bạn có thể đặt requireAll
thành false
trong
chú thích để chỉ định rằng không phải mọi thuộc tính đều phải được gán một liên kết
như được trình bày trong ví dụ sau:
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); } } }
Ví dụ ở trên hơi phức tạp vì
Lớp View
sử dụng phương thức
addOnAttachStateChangeListener()
và
removeOnAttachStateChangeListener()
thay vì phương thức setter cho
OnAttachStateChangeListener
.
Lớp android.databinding.adapters.ListenerUtil
giúp theo dõi các dữ liệu này
trình nghe để có thể xoá chúng trong bộ chuyển đổi liên kết.
Lượt chuyển đổi đối tượng
Tự động chuyển đổi đối tượng
Khi một Object
được trả về từ một liên kết
thì thư viện sẽ chọn phương thức dùng để đặt giá trị của
thuộc tính này. Object
được truyền đến một loại tham số của phương thức đã chọn. Chiến dịch này
trong các ứng dụng sử dụng
Lớp ObservableMap
đến
lưu trữ dữ liệu, như được thể hiện trong ví dụ sau:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Đối tượng userMap
trong biểu thức trả về một giá trị, giá trị này sẽ tự động
truyền tới loại tham số có trong phương thức setText(CharSequence)
dùng để
đặt giá trị của thuộc tính android:text
. Nếu loại thông số là
không rõ ràng, hãy truyền loại dữ liệu trả về trong biểu thức.
Lượt chuyển đổi tuỳ chỉnh
Trong một số trường hợp, bạn phải chuyển đổi tuỳ chỉnh giữa các loại cụ thể. Cho
Ví dụ: thuộc tính android:background
của khung hiển thị mong đợi có Drawable
, nhưng
giá trị color
được chỉ định là một số nguyên. Ví dụ sau đây cho thấy một
thuộc tính dự kiến có Drawable
, nhưng thay vào đó, một số nguyên lại được cung cấp:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Mỗi khi dự kiến có Drawable
và kết quả trả về là một số nguyên, hãy chuyển đổi int
thành ColorDrawable
.
Để thực hiện chuyển đổi, hãy sử dụng phương thức tĩnh với
BindingConversion
như sau:
Kotlin
@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)
Java
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
Tuy nhiên, các loại giá trị được cung cấp trong biểu thức liên kết phải nhất quán. Bạn không thể sử dụng các loại khác nhau trong cùng một biểu thức, như được minh hoạ sau đây ví dụ:
// 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"/>
Tài nguyên khác
Để tìm hiểu thêm về liên kết dữ liệu, hãy xem các tài nguyên sau.
Mẫu
Lớp học lập trình
Bài đăng trên blog
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Thư viện liên kết dữ liệu
- Bố cục và biểu thức liên kết
- Liên kết thành phần hiển thị