Adaptery wiążące odpowiadają za wywoływanie odpowiednich wywołań platformy
ustaw wartości. Jednym z przykładów jest ustawienie wartości właściwości, np. wywoływanie funkcji
setText()
. Inny
jest na przykład ustawienie detektora zdarzeń, np. wywoływanie funkcji
setOnClickListener()
.
Biblioteka wiązań danych umożliwia określenie metody wywoływanej w celu ustawienia wartości, podać własną logikę wiązania i określić typ zwróconego obiektu, za pomocą przejściówek.
Ustawianie wartości atrybutów
Po każdej zmianie wartości granicznej wygenerowana klasa powiązania musi wywołać metodę ustawiającą za pomocą wyrażenia wiążącego. Funkcja wiązania danych Biblioteka określa metodę automatycznie. Można też jawnie zadeklarować lub podaj własną logikę wyboru metody.
Automatyczny wybór metody
W przypadku atrybutu o nazwie example
biblioteka automatycznie znajduje metodę
setExample(arg)
, który jako argument akceptuje zgodne typy. Przestrzeń nazw
atrybutu nie jest brany pod uwagę. Używana jest tylko nazwa i typ atrybutu
przy wyszukiwaniu metody.
Na przykład biblioteka w ramach wyrażenia android:text="@{user.name}"
szuka metody setText(arg)
, która akceptuje typ zwracany przez
user.getName()
Jeśli zwracany typ user.getName()
to String
, to
biblioteka szuka metody setText()
, która akceptuje argument String
. Jeśli
zwraca wartość int
, biblioteka wyszukuje metodę setText()
, która
akceptuje argument int
. Wyrażenie musi zwracać poprawny typ. Dostępne opcje
w razie potrzeby rzutować wartość zwrotną.
Powiązanie danych działa nawet wtedy, gdy nie ma atrybutu o podanej nazwie. Dostępne opcje
tworzyć atrybuty dla dowolnej metody ustawiającej za pomocą powiązania danych. Na przykład zespół pomocy
zajęcia
DrawerLayout
nie ma atrybutów, ale ma mnóstwo ustalających. Następujący układ
automatycznie używa
setScrimColor(int)
oraz
addDrawerListener(DrawerListener)
jako metody ustawiania dla metod app:scrimColor
i app:drawerListener
odpowiednio:
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
Podaj nazwę metody niestandardowej
Niektóre atrybuty mają elementy ustawiające, które nie pasują do nazwy. W takich sytuacjach
można powiązać z metodą ustawiającą za pomocą atrybutu
BindingMethods
adnotacja. Adnotacja jest używana z klasą i może zawierać wiele
BindingMethod
adnotacji, po 1 dla każdej ze zmienionych metod. Metody powiązań to adnotacje, które
które możesz dodawać do dowolnych zajęć w swojej aplikacji.
W poniższym przykładzie atrybut android:tint
jest powiązany z parametrem
setImageTintList(ColorStateList)
.
– nie przy użyciu metody 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"), })
Zwykle nie musisz zmieniać nazw ustawień w klasach platformy Androida. są już zaimplementowane z użyciem konwencji nazw, aby automatycznie i metody dopasowywania.
Dodaj niestandardowe funkcje logiczne
Niektóre atrybuty wymagają niestandardowej logiki wiązania. Na przykład nie ma powiązanych
ustawiającą atrybut android:paddingLeft
. Zamiast tego udostępniana jest metoda setPadding(left,
top, right, bottom)
. Metoda adaptera wiązania statycznego z
BindingAdapter
umożliwia dostosowanie sposobu wywoływania funkcji ustawiającej dla atrybutu.
Atrybuty klas platformy Android mają już obiekt BindingAdapter
adnotacji. Poniższy przykład pokazuje adapter wiązania dla
Atrybut 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()); }
Typy parametrów są ważne. Pierwszy parametr określa typ do widoku danych powiązanego z atrybutem. Drugi parametr określa typ akceptowany w wyrażeniu wiązania dla danego atrybutu.
Adaptery wiązań są też przydatne do innych rodzajów dostosowywania. Przykład: w celu wczytania obrazu można wywołać niestandardowy program wczytujący z wątku instancji roboczej.
Możesz też używać przejściówek, które otrzymują wiele atrybutów, jak pokazano w następujący przykład:
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); }
Możesz użyć adaptera w swoim układzie, tak jak w przykładzie poniżej. Notatka
które @drawable/venueError
odnosi się do zasobu w Twojej aplikacji. Otoczenie
zasób z @{}
sprawia, że jest to prawidłowe wyrażenie powiązania.
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
Przejściówka jest wywoływana, jeśli interfejsy imageUrl
i error
są używane przez
Obiekt ImageView
, imageUrl
to
ciąg znaków, a error
to
Drawable
Jeśli chcesz
adapter, który ma być wywoływany po ustawieniu dowolnego z atrybutów, ustaw opcjonalny
requireAll
flagę adaptera na false
, jak w poniższym przykładzie:
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); } }
Metody powiązań mogą przyjmować stare wartości w modułach obsługi. Metoda Przyjęcie starych i nowych wartości musi najpierw zadeklarować wszystkie stare wartości dla atrybutów, z nowymi wartościami, tak jak w tym przykładzie:
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()); } }
Modułów obsługi zdarzeń można używać tylko z interfejsami lub klasami abstrakcyjnymi z jednym metody abstrakcyjnej, jak w tym przykładzie:
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); } } }
Użyj tego modułu obsługi zdarzeń w układzie:
<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
Jeśli odbiornik ma wiele metod, musisz podzielić go na kilku odbiorników.
Przykład:
View.OnAttachStateChangeListener
udostępnia 2 metody:
onViewAttachedToWindow(View)
oraz
onViewDetachedFromWindow(View)
.
Biblioteka ma dwa interfejsy do rozróżniania atrybutów i modułów obsługi
dla nich:
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); }
Zmiana jednego detektora może oddziaływać na resztę, dlatego potrzebujesz przejściówki,
działa w przypadku jednego z tych atrybutów lub obu tych atrybutów. Możesz ustawić: requireAll
na false
w:
adnotacja wskazująca, że nie do każdego atrybutu trzeba przypisać powiązanie
zgodnie z poniższym przykładem:
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); } } }
Powyższy przykład jest nieco skomplikowany, ponieważ
Klasa View
używa parametru
addOnAttachStateChangeListener()
oraz
removeOnAttachStateChangeListener()
zamiast metody wyznaczającej
OnAttachStateChangeListener
.
Klasa android.databinding.adapters.ListenerUtil
pomaga je śledzić
z detektorami, aby można je było usunąć w adapterze wiązania.
Konwersje obiektów
Automatyczna konwersja obiektów
Kiedy Object
jest zwracane z powiązania
, biblioteka wybiera metodę używaną do ustawienia wartości argumentu
usłudze. Pole Object
jest rzutowane na typ parametru wybranej metody. Ten
jest wygodne w aplikacjach, które używają
ObservableMap
zajęcia na
jak w tym przykładzie:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Obiekt userMap
w wyrażeniu zwraca wartość, która jest automatycznie
rzutować na typ parametru znaleziony w metodzie setText(CharSequence)
używanej do
ustaw wartość atrybutu android:text
. Jeśli typ parametru to
niejednoznaczne, rzutować zwracany typ w wyrażeniu.
Konwersje niestandardowe
W niektórych sytuacjach wymagana jest konwersja niestandardowa między określonymi typami. Dla:
np. atrybut android:background
widoku oczekuje Drawable
, ale
określona wartość color
jest liczbą całkowitą. Poniższy przykład pokazuje
który oczekuje wartości Drawable
, ale jest podana liczba całkowita:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Gdy wartość Drawable
jest oczekiwana i zwracana jest liczba całkowita, przekonwertuj int
do ColorDrawable
.
Aby dokonać konwersji, użyj metody statycznej ze
BindingConversion
w następujący sposób:
Kotlin
@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)
Java
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
Typy wartości podane w wyrażeniu powiązania muszą być jednak spójne. W jednym wyrażeniu nie można używać różnych typów, jak pokazano na ilustracji poniżej przykład:
// 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"/>
Dodatkowe materiały
Więcej informacji o powiązaniach danych znajdziesz w tych materiałach.
Próbki
Ćwiczenia z programowania
Posty na blogu
.Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Biblioteka wiązań danych
- Układy i wyrażenia wiążące
- Wyświetl powiązanie