Język wyrażeń umożliwia pisanie wyrażeń, które obsługują wysyłane zdarzenia. według liczby wyświetleń. Biblioteka wiązań danych automatycznie generuje wymagane klasy , by powiązać widoki danych w układzie z obiektami danych.
Pliki układu wiązań danych są nieco inne i zaczynają się od tagu głównego
layout
, a po nim element data
i element główny view
. Ten widok
to element główny w niewiążącym pliku układu. Następujący kod:
pokazuje przykładowy plik układu:
<?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>
Zmienna user
w obrębie data
opisuje właściwość, której można użyć w obrębie
ten układ:
<variable name="user" type="com.example.User" />
Wyrażenia w układzie wpisuje się we właściwościach atrybutu za pomocą atrybutu
Składnia @{}
. W poniższym przykładzie para klucz-wartość
Tekst TextView
jest ustawiony na
Właściwość firstName
zmiennej user
:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
Obiekty danych
Załóżmy, że masz zwykły obiekt opisujący element 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; } }
Obiekt tego typu zawiera dane, które nigdy się nie zmieniają. Aplikacje mają często danych, które są odczytywane raz i nie zmieniają się później. Możesz też użyć funkcji obiektu zgodnego z określonymi konwencjami, na przykład przy użyciu metod akcesora w w języku Java, jak w tym przykładzie:
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; } }
Z punktu widzenia wiązania danych te 2 klasy są równoważne.
wyrażenie @{user.firstName}
używane jako
android:text
.
ma dostęp do pola firstName
w poprzedniej klasie, a atrybut
getFirstName()
w drugiej klasie. Ma także na celu
firstName()
, o ile ta metoda istnieje.
Powiązanie danych
Dla każdego pliku układu generowana jest klasa powiązania. Domyślnie jest to nazwa
jest oparta na nazwie pliku układu, przekonwertowanej na literę Pascal
dodany sufiks Binding (Wiązanie). Na przykład poprzedzająca nazwa pliku układu to
activity_main.xml
, więc odpowiednią wygenerowaną klasą powiązania jest
ActivityMainBinding
Ta klasa zawiera wszystkie powiązania z właściwości układu, na przykład
zmiennej user
– do widoków układu i wie, jak przypisywać wartości
dla wyrażeń wiążących. Zalecamy utworzenie powiązań podczas rozszerzania
zgodnie z poniższym przykładem:
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); }
W czasie działania aplikacji aplikacja wyświetla w interfejsie użytkownika Test. Ewentualnie możesz
uzyskać widok za pomocą
LayoutInflater
, jak widać w tabeli
następujący przykład:
Kotlin
val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
Java
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
Jeśli używasz elementów wiązania danych wewnątrz
Fragment
,
ListView
lub
RecyclerView
adaptera, lepiej użyć
inflate()
metod klas powiązań lub
DataBindingUtil
,
w tym przykładowym kodzie:
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);
Język wyrażania
Wspólne funkcje
Język wyrażeń wygląda bardzo podobnie do wyrażeń w kodzie zarządzanym. Ty mogą używać następujących operatorów i słów kluczowych w języku wyrażeń:
- Matematyczne:
+ - / * %
- Połączenie ciągu znaków:
+
- Wartość logiczna:
&& ||
- Wartość binarna:
& | ^
- Jednoargumentowy:
+ - ! ~
- Zmiana:
>> >>> <<
- Porównanie:
== > < >= <=
(<
musi mieć zmianę znaczenia jako<
) instanceof
- Grupowanie:
()
- Literały, np. znak, ciąg, liczbowy,
null
- Prześlij
- Wywołania metod
- Dostęp do pól
- Dostęp do tablicy:
[]
- Operator potrójny:
?:
Oto przykłady:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
Brakujące operacje
W składni wyrażenia, której możesz użyć, brakuje poniższych operacji w kodzie zarządzanym:
this
super
new
- Bezpośrednie wywołanie ogólne
Operator scalania o wartości null
Operator scalania o wartości null (??
) wybiera lewy operand, jeśli jest inny niż null
lub praw, jeśli poprzednia wartość to null
:
android:text="@{user.displayName ?? user.lastName}"
Pod względem funkcjonalnym jest to równoważne z tym:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
Odwołania do właściwości
Wyrażenie może odwoływać się do właściwości w klasie przy użyciu poniższego formatu:
jest taki sam dla pól, getterów i
ObservableField
obiekty:
android:text="@{user.lastName}"
Unikaj wyjątków ze wskaźnikiem null
Wygenerowany kod powiązania danych automatycznie sprawdza wartości null
i unika
wyjątki wskaźnika null. Na przykład w wyrażeniu @{user.name}
, jeśli
user
ma wartość null, a user.name
ma przypisaną wartość domyślną null
. Jeśli
odwołania do user.age
, gdzie wiek jest typu int
, to wiązanie danych wykorzystuje
wartość domyślna to 0
.
Zobacz odniesienia
Wyrażenie może odwoływać się do innych widoków w układzie za pomocą identyfikatora, korzystając z następującego kodu składnia:
android:text="@{exampleText.text}"
W poniższym przykładzie widok TextView
odwołuje się do widoku EditText
w:
ten sam układ:
<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}"/>
Kolekcje
Możesz uzyskać dostęp do wspólnych kolekcji, takich jak tablice, listy, listy rozproszone i
map, używając dla wygody operatora []
.
<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]}"
Możesz też odwołać się do wartości na mapie, korzystając z notacji object.key
. Dla:
możesz zastąpić @{map[key]}
w poprzednim przykładzie wartością
@{map.key}
Literały łańcuchowe
W cudzysłowie możesz ująć wartość atrybutu, co pozwala na korzystanie z podwójne cudzysłowy w wyrażeniu, jak w tym przykładzie:
android:text='@{map["firstName"]}'
Możesz też ująć wartość atrybutu w cudzysłów. Podczas wykonywania tych czynności:
Literały łańcuchowe muszą być otoczone grawisami `
, jak pokazano na ilustracji
tutaj:
android:text="@{map[`firstName`]}"
Materiały
Wyrażenie może odwoływać się do zasobów aplikacji o takiej składni:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
Ciągi tekstowe i formy liczby mnogiej mogą być oceniane, podając parametry:
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
Możesz przekazywać odwołania do właściwości i wyświetlać odwołań jako parametrów zasobów:
android:text="@{@string/example_resource(user.lastName, exampleText.text)}"
Jeśli liczba mnoga ma kilka parametrów, przekaż wszystkie parametry:
Have an orange
Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
Niektóre zasoby wymagają bezpośredniej oceny typu, jak w tym przykładzie tabela:
Typ | Normalne odniesienie | Odniesienie do wyrażenia |
---|---|---|
String[] |
@array |
@stringArray |
int[] |
@array |
@intArray |
TypedArray |
@array |
@typedArray |
Animator |
@animator |
@animator |
StateListAnimator |
@animator |
@stateListAnimator |
color int |
@color |
@color |
ColorStateList |
@color |
@colorStateList |
Obsługa zdarzeń
Powiązanie danych umożliwia pisanie zdarzeń obsługi wyrażeń, które są wysyłane z
widoków, na przykład
onClick()
. Nazwy atrybutów zdarzeń są określane przez nazwę metody detektora,
z kilkoma wyjątkami. Przykład:
View.OnClickListener
ma
metoda onClick()
, więc atrybut tego zdarzenia ma wartość android:onClick
.
Istnieją specjalne moduły obsługi zdarzeń kliknięcia, które wymagają parametru
inny niż android:onClick
, by uniknąć konfliktu. Za pomocą
następujące atrybuty, aby uniknąć tego rodzaju konfliktów:
Kategoria | Konfigurujący detektor | Atrybut |
---|---|---|
SearchView |
setOnSearchClickListener(View.OnClickListener) |
android:onSearchClick |
ZoomControls |
setOnZoomInClickListener(View.OnClickListener) |
android:onZoomIn |
ZoomControls |
setOnZoomOutClickListener(View.OnClickListener) |
android:onZoomOut |
Możesz używać tych 2 mechanizmów. Zostały one szczegółowo opisane w sekcjach, które śledzić, aby obsłużyć zdarzenie:
- Odwołania do metod: w wyrażeniach możesz
zgodnie z podpisami metody detektora. Kiedy
wyrażenie zwraca wartość odwołania do metody, a wiązanie danych opakowuje metodę;
i odwołania do obiektu właściciela w detektorze oraz ustawia ten odbiornik
docelowym. Jeśli wyrażenie zwraca wartość
null
, powiązanie danych nie powoduje utworzysz detektor i ustawi zamiast niego detektornull
. - Powiązania detektora: to wyrażenia lambda, które są oceniane, gdy wystąpi zdarzenie. Powiązanie danych zawsze tworzy który ustawia dla widoku. Po wysłaniu zdarzenia detektor ocenia wyrażenie lambda.
Odwołania do metod
Zdarzenia możesz wiązać bezpośrednio z metodami obsługi, podobnie jak w przypadku
przypisać
android:onClick
na
w ramach aktywności. Jedna z zalet
Atrybut View
onClick
oznacza, że
jest przetwarzane podczas kompilacji. Jeśli więc dana metoda nie istnieje lub
podpis jest nieprawidłowy, pojawia się błąd czasu kompilacji.
Główna różnica między odwołaniami do metod a powiązaniami detektora polega na tym, że rzeczywista implementacja detektora jest tworzona, gdy dane są powiązane, a nie gdy . Jeśli wolisz oceniać wyrażenie, gdy zdarzenie użyj powiązań detektora.
Aby przypisać zdarzenie do jego modułu obsługi, użyj normalnego wyrażenia wiązania z jest nazwą metody do wywołania. Przeanalizujmy następujący przykład: obiekt danych układu:
Kotlin
class MyHandlers { fun onClickFriend(view: View) { ... } }
Java
public class MyHandlers { public void onClickFriend(View view) { ... } }
Wyrażenie powiązania może przypisać odbiornik kliknięć do widoku danych
onClickFriend()
w następujący sposób:
<?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>
Powiązania detektora
Powiązania detektora to wyrażenia wiążące, które są uruchamiane po wystąpieniu zdarzenia. Ta są podobne do odwołań do metod, ale umożliwiają uruchamianie dowolnych wiązań danych wyrażeń. Ta funkcja jest dostępna we wtyczce Androida do obsługi Gradle do obsługi Gradle wersji 2.0 i nowszych.
W odwołaniach do metod parametry metody muszą odpowiadać parametrom
detektor zdarzeń. W powiązaniach detektora tylko zwracana wartość musi pasować do argumentu
oczekiwana wartość zwracana detektora, chyba że oczekuje on void
. Dla:
rozważ tę klasę prezentera, która ma onSaveClick()
:
Kotlin
class Presenter { fun onSaveClick(task: Task){} }
Java
public class Presenter { public void onSaveClick(Task task){} }
Zdarzenie kliknięcia możesz powiązać z metodą onSaveClick()
w ten sposób:
<?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>
Gdy w wyrażeniu używane jest wywołanie zwrotne, powiązanie danych automatycznie tworzy i rejestruje go dla zdarzenia. Po uruchomieniu widoku danych zdarzenia, powiązanie danych ocenia dane wyrażenie. Tak jak w przypadku zwykłego powiązania wyrażenia, bezpieczeństwo wiąże się z wartością null i wątkami, podczas gdy są sprawdzane wyrażenia detektora.
W poprzednim przykładzie parametr view
przekazywany do funkcji onClick(View)
nie jest zdefiniowany. Powiązania detektora udostępniają 2 parametry detektora:
możesz zignorować wszystkie parametry tej metody lub nazwać je wszystkie. Jeśli wolisz
nadając im nazwy, możesz ich używać w wyrażeniu. Na przykład:
może zapisać poprzednie wyrażenie w następujący sposób:
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
Jeśli chcesz użyć parametru w wyrażeniu, możesz to zrobić w ten sposób:
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)}"
Możesz też użyć wyrażenia lambda z więcej niż 1 parametrem:
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)}" />
Jeśli zdarzenie, którego słuchasz, zwraca wartość, której typ nie jest wartością void
,
również wyrażenia muszą zwracać ten sam typ wartości. Na przykład:
aby nasłuchiwać dotknięcia zdarzenie Hold (długie kliknięcie), wyrażenie musi zwracać
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)}"
Jeśli nie można ocenić wyrażenia ze względu na obiekty null
, zostanie zwrócone powiązanie danych
domyślną wartość dla danego typu, na przykład null
dla typów odwołań, 0
dla
int
lub false
za boolean
.
Jeśli musisz użyć wyrażenia z predykatem, na przykład
trójwartościowy – możesz użyć void
jako symbolu:
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
Unikaj złożonych detektorów
Wyrażenia detektora dają wiele możliwości i mogą ułatwić czytanie kodu. Dzień z kolei detektory zawierające złożone wyrażenia sprawiają, że układy są trudniejsze. do odczytywania i utrzymywania. Wyrażenia powinny być tak proste, jak przekazywanie dostępnych danych z interfejsu do metody wywołania zwrotnego. Zaimplementuj dowolną logikę biznesową wewnątrz metody wywołania zwrotnego, która jest wywoływana z wyrażenia odbiornika.
Importy, zmienne i uwzględnione
Biblioteka wiązań danych udostępnia takie funkcje, jak importy, zmienne zawiera. Dzięki importowaniu możesz łatwo odwołać się do klas w plikach układu. Zmienne umożliwiają opisywanie właściwości, których można używać w wyrażeniach wiążących. m.in. umożliwiają ponowne używanie złożonych układów w całej aplikacji.
Importy
Dzięki importowaniu możesz odwoływać się do klas w pliku układu, np. w kodzie zarządzanym.
Wewnątrz elementu data
możesz użyć 0 lub więcej elementów import
.
ten przykładowy kod importuje klasę View
do pliku układu:
<data>
<import type="android.view.View"/>
</data>
Zaimportowanie klasy View
umożliwia odwoływanie się do niej z wyrażeń powiązania.
Ten przykład pokazuje, jak odwołać się do parametru
VISIBLE
i
Stałe GONE
klasy View
:
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
Aliasy typu
W przypadku konfliktów nazw zajęć możesz zmienić nazwę jednej z nich na
alias. W tym przykładzie zmieniamy nazwę klasy View
w
Przesyłka com.example.real.estate
do: Vista
:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
Następnie możesz użyć Vista
, aby odwołać się do elementów com.example.real.estate.View
i View
by odwołać się do elementu android.view.View
w pliku układu.
Importuj inne zajęcia
Zaimportowanych typów możesz używać jako odwołań do typów w zmiennych i wyrażeniach.
ten przykład pokazuje typy zmiennej User
i List
:
<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
Możesz użyć zaimportowanych typów, aby rzutować część wyrażenia. Poniżej
przykład przekazuje właściwość connection
na typ User
:
<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Możesz też użyć importowanych typów przy odwoływaniu się do pól statycznych i metod w
wyrażeń. Ten kod importuje klasę MyStringUtils
i odwołania
jego metoda capitalize
:
<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"/>
Podobnie jak w przypadku kodu zarządzanego, tag java.lang.*
jest importowany automatycznie.
Zmienne
Wewnątrz elementu data
można używać wielu elementów variable
. Każdy
Element variable
opisuje właściwość, którą można ustawić w układzie, którego chcesz użyć
w wyrażeniach powiązań w pliku układu. W przykładzie poniżej deklarujesz,
zmienne user
, image
i 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>
Typy zmiennych są sprawdzane podczas kompilowania, więc jeśli zmienna implementuje
Observable
lub jest
obserwowalną kolekcję,
które musi znaleźć się w typie. Jeśli zmienna jest klasą bazową lub interfejsem
w którym nie jest zaimplementowany interfejs Observable
, zmienne nie są
zaobserwowane.
Gdy istnieją różne pliki układu dla różnych konfiguracji (na przykład w orientacji poziomej lub pionowej), zmienne są łączone. Nie może występować sprzecznych definicji zmiennych w tych plikach układu.
Wygenerowana klasa powiązania ma mechanizm ustawiający i pobierający dla każdego z opisanych
zmiennych. Zmienne przyjmują domyślne wartości kodu zarządzanego do momentu ustawienia
jest wywoływane – null
w przypadku typów plików, 0
dla int
, false
dla
boolean
itp.
Wygenerowana jest specjalna zmienna o nazwie context
do użycia w wyrażeniach wiążących
w razie potrzeby. Wartość parametru context
to
Obiekt Context
z widoku głównego
Metoda getContext()
.
Zmienna context
została zastąpiona przez jawną deklarację zmiennej zawierającą
imię i nazwisko.
Zawiera
Zmienne możesz przekazywać do powiązania uwzględnionego układu z poziomu zawierającego
z użyciem przestrzeni nazw aplikacji i nazwy zmiennej w atrybucie.
przykład poniżej pokazuje uwzględnione zmienne user
z name.xml
i
Pliki układu (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>
Powiązanie danych nie obsługuje uwzględnienia jako bezpośredniego elementu podrzędnego elementu scalonego. Na przykład ten układ nie jest obsługiwany:
<?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>
Dodatkowe materiały
Aby dowiedzieć się więcej o wiązaniu danych, zapoznaj się z tymi dodatkowymi materiałami.