Układy i wyrażenia powiązania

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 &lt;)
  • 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&lt;String>"/>
    <variable name="sparse" type="SparseArray&lt;String>"/>
    <variable name="map" type="Map&lt;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 detektor null.
  • 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&lt;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.

Próbki

Ćwiczenia z programowania

Posty na blogu