Diseños y expresiones vinculantes

El lenguaje de expresiones te permite escribir expresiones que controlen eventos enviados por vistas. La biblioteca de vinculación de datos genera automáticamente las clases requeridas para vincular las vistas en el diseño con tus objetos de datos.

Los archivos de diseño de vinculación de datos son ligeramente diferentes y comienzan con una etiqueta raíz de layout, seguido de un elemento data y un elemento raíz view. Esta vista es cuál es tu raíz en un archivo de diseño no vinculante. El siguiente código muestra un archivo de diseño de muestra:

<?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>

La variable user dentro de data describe una propiedad que se puede usar en este diseño:

<variable name="user" type="com.example.User" />

Las expresiones del diseño se escriben en las propiedades del atributo con el elemento @{}. En el siguiente ejemplo, el El texto TextView se establece en Propiedad firstName de la variable user:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}" />

Objetos de datos

Supongamos que tienes un objeto sin formato para describir la entidad 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;
  }
}

Este tipo de objeto tiene datos que nunca cambian. Es común en las apps tener datos que se leen una vez y nunca cambian. También puedes usar un objeto que sigue un conjunto de convenciones, como el uso de métodos de acceso en el lenguaje de programación Java, como se muestra en el siguiente ejemplo:

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;
  }
}

Desde la perspectiva de la vinculación de datos, esas dos clases son equivalentes. El la expresión @{user.firstName} que se usa para android:text accede al campo firstName en la clase anterior y el getFirstName() en la última clase. También se resuelve firstName(), si ese método existe

Cómo vincular datos

Se genera una clase de vinculación para cada archivo de diseño. De forma predeterminada, el nombre del se basa en el nombre del archivo de diseño, convertido a mayúsculas y minúsculas, con el sufijo Binding agregado. Por ejemplo, el nombre de archivo de diseño anterior es activity_main.xml, por lo que la clase de vinculación generada correspondiente es ActivityMainBinding

Esta clase contiene todas las vinculaciones de las propiedades de diseño, por ejemplo, la variable user, a las vistas del diseño y sabe cómo asignar valores para las expresiones de vinculación. Recomendamos crear las vinculaciones mientras se aumenta el diseño, como se muestra en el siguiente ejemplo:

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);
}

Durante el tiempo de ejecución, la app muestra el usuario de prueba en la IU. Como alternativa, puedes obtener la vista con un LayoutInflater, como se muestra en la siguiente ejemplo:

Kotlin

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())

Java

ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());

Si usas elementos de vinculación de datos dentro de Fragment: ListView o RecyclerView adaptador, tal vez prefieras usar el inflate() de las clases de vinculación DataBindingUtil, como como se muestra en el siguiente ejemplo de código:

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);

Lenguaje de expresiones

Funciones comunes

El lenguaje de expresiones se parece mucho a las expresiones que se encuentran en el código administrado. Tú puedes usar los siguientes operadores y palabras clave en el lenguaje de expresiones:

  • Matemático: + - / * %
  • Concatenación de cadenas: +
  • Lógico: && ||
  • Objeto binario: & | ^
  • Unario: + - ! ~
  • Mayúsculas: >> >>> <<
  • Comparación: == > < >= <= (< debe tener el formato de escape &lt;)
  • instanceof
  • Agrupamiento: ()
  • Literales, como caracteres, strings, números, null
  • Transmitir
  • de llamadas a métodos
  • de acceso de campo
  • Acceso al array: []
  • Operador ternario: ?:

Estos son algunos ejemplos:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

Operaciones faltantes

Faltan las siguientes operaciones de la sintaxis de expresiones que puedes usar en código administrado:

  • this
  • super
  • new
  • invocación genérica explícita

Operador coalescente nulo

El operador coalescente nulo (??) elige el operando izquierdo si no es null o la derecha si el anterior es null:

android:text="@{user.displayName ?? user.lastName}"

Esto es funcionalmente equivalente a lo siguiente:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

Referencias de propiedades

Una expresión puede hacer referencia a una propiedad en una clase con el siguiente formato: que es lo mismo para campos, métodos get y ObservableField objetos:

android:text="@{user.lastName}"

Cómo evitar excepciones de puntero nulo

El código de vinculación de datos generado verifica automáticamente si hay valores null y evita excepciones de puntero nulo. Por ejemplo, en la expresión @{user.name}, si user es nulo, a user.name se le asigna su valor predeterminado de null. Si user.age de referencia, donde la edad es del tipo int, la vinculación de datos usa el el valor predeterminado es 0.

Referencias de vistas

Una expresión puede hacer referencia a otras vistas en el diseño por ID, mediante el siguiente sintaxis:

android:text="@{exampleText.text}"

En el siguiente ejemplo, la vista TextView hace referencia a una vista EditText en el mismo diseño:

<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}"/>

Colecciones

Puedes acceder a colecciones comunes, como matrices, listas, listas dispersas y Maps, con el operador [] para mayor comodidad.

<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]}"

También puedes hacer referencia a un valor en el mapa con la notación object.key. Para Puedes reemplazar @{map[key]} en el ejemplo anterior por @{map.key}

Literales de string

Puede usar comillas simples para rodear el valor del atributo, lo que le permite usar comillas dobles en la expresión, como se muestra en el siguiente ejemplo:

android:text='@{map["firstName"]}'

También puedes usar comillas dobles para rodear el valor del atributo. Cuando lo hagas, Los literales de cadena deben estar entre acentos graves `, como se muestra. aquí:

android:text="@{map[`firstName`]}"

Recursos

Una expresión puede hacer referencia a los recursos de la app con la siguiente sintaxis:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

Puedes evaluar las strings de formato y los valores plurales proporcionando parámetros:

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

Puedes pasar referencias de propiedades y ver referencias como parámetros de recursos:

android:text="@{@string/example_resource(user.lastName, exampleText.text)}"

Cuando un plural toma varios parámetros, pasa todos los parámetros:


  Have an orange
  Have %d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"

Algunos recursos requieren una evaluación de tipo explícito, como se muestra en el siguiente tabla:

Tipo Referencia normal Referencia de expresiones
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

Manejo de eventos

La vinculación de datos te permite escribir eventos de manejo de expresiones que se envían desde las vistas, por ejemplo, onClick() . Los nombres de los atributos de eventos están determinados por el nombre del método del objeto de escucha con algunas excepciones. Por ejemplo: View.OnClickListener tiene un método onClick(), por lo que el atributo de este evento es android:onClick.

Hay algunos controladores de eventos especializados para el evento de clic que necesitan un distinto de android:onClick para evitar conflictos. Puedes usar la los siguientes atributos para evitar este tipo de conflictos:

Clase Método set de objetos de escucha Atributo
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

Puedes utilizar estos dos mecanismos, descritos en detalle en las secciones que seguir, para manejar un evento:

  • Referencias de métodos: En tus expresiones, puedes métodos de referencia que se ajusten a la firma del método del objeto de escucha. Cuándo se evalúa como una referencia de método, la vinculación de datos une el método referencia y de propietario en un objeto de escucha y establece ese objeto de escucha en la vista de destino. Si la expresión se evalúa como null, la vinculación de datos no crea un objeto de escucha y, en su lugar, establece un objeto de escucha null.
  • Vinculaciones de objetos de escucha: son expresiones lambda que y se evalúan cuando ocurre el evento. La vinculación de datos siempre crea de objetos, que se configura en la vista. Cuando se envía el evento, el del objeto de escucha evalúa la expresión lambda.

Referencias de métodos

Puedes vincular eventos a métodos de controlador directamente, similar a como lo haces con asignar android:onClick a una en una actividad. Una ventaja en comparación con el El atributo View onClick indica que el elemento la expresión estándar se procesa en el momento de la compilación. Por lo tanto, si el método no existe o no firma es incorrecta, recibes un error de tiempo de compilación.

La principal diferencia entre las referencias de métodos y las vinculaciones de objetos de escucha es que una implementación real de objetos de escucha se crea cuando los datos están vinculados, no cuando evento activado. Si prefieres evaluar la expresión cuando el evento usa las vinculaciones de objetos de escucha.

Para asignar un evento a su controlador, usa una expresión de vinculación normal, con el value es el nombre del método a llamar. Por ejemplo, considera el siguiente ejemplo objeto de datos de diseño:

Kotlin

class MyHandlers {
    fun onClickFriend(view: View) { ... }
}

Java

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

La expresión de vinculación puede asignar el objeto de escucha de clics de una vista al onClickFriend() de la siguiente manera:

<?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>

Vinculaciones de objetos de escucha

Las vinculaciones de los objetos de escucha son expresiones de vinculación que se ejecutan cuando ocurre un evento. Ellas son similares a las referencias de métodos, pero te permiten ejecutar vinculaciones de datos arbitrarias con expresiones regulares. Esta función está disponible con el complemento de Android para Gradle para Gradle versión 2.0 y posteriores.

En las referencias de métodos, los parámetros del método deben coincidir con los parámetros del el objeto de escucha de eventos. En las vinculaciones de objetos de escucha, solo el valor de retorno debe coincidir con el Es el valor de retorno esperado del objeto de escucha, a menos que espere void. Para ejemplo, considera la siguiente clase de presentador que tiene un elemento onSaveClick(). método:

Kotlin

class Presenter {
    fun onSaveClick(task: Task){}
}

Java

public class Presenter {
    public void onSaveClick(Task task){}
}

Puedes vincular el evento de clic al método onSaveClick() de la siguiente manera:

<?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>

Cuando se usa una devolución de llamada en una expresión, la vinculación de datos crea automáticamente el el objeto de escucha necesario y lo registra para el evento. Cuando la vista activa evento, la vinculación de datos evalúa la expresión dada. Como con la vinculación regular obtienes la seguridad nula y del subproceso de la vinculación de datos, mientras que estas de objetos de escucha.

En el ejemplo anterior, el parámetro view que se pasa a onClick(View) no está definido. Las vinculaciones de objetos de escucha proporcionan dos opciones para los parámetros de objetos de escucha: puedes ignorar todos los parámetros del método o nombrarlos a todos. Si lo prefieres para nombrar los parámetros, puedes usarlos en tu expresión. Por ejemplo, puede escribir la expresión anterior de la siguiente manera:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

Si deseas usar el parámetro en la expresión, puedes hacerlo de la siguiente manera:

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)}"

Además, puedes usar una expresión lambda con más de un parámetro:

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)}" />

Si el evento que estás escuchando muestra un valor cuyo tipo no es void, tu las expresiones deben mostrar el mismo tipo de valor. Por ejemplo, si quieres para escuchar el tacto y retención (clic largo), tu expresión debe mostrar un de lista y booleanas.

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)}"

Si la expresión no se puede evaluar debido a objetos null, la vinculación de datos muestra el valor predeterminado para ese tipo, como null para los tipos de referencia, 0 para int o false para boolean.

Si necesitas usar una expresión con un predicado, por ejemplo, un ternario; puedes usar void como símbolo:

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

Evita usar objetos de escucha complejos

Las expresiones de los objetos de escucha son potentes y pueden facilitar la lectura de tu código. En la los objetos de escucha que contienen expresiones complejas hacen que tus diseños sean más difíciles de leer y mantener. Haz que tus expresiones sean tan simples como pasar datos disponibles de la IU a tu método de devolución de llamada. Implementar cualquier lógica empresarial el método de devolución de llamada que invocas desde la expresión del objeto de escucha.

Importaciones, variables e inclusiones

La biblioteca de vinculación de datos proporciona funciones como importaciones, variables y incluye. Las importaciones hacen que las clases sean fáciles de consultar dentro de tus archivos de diseño. Las variables te permiten describir una propiedad que se puede usar en expresiones de vinculación. Las inclusiones permiten reutilizar diseños complejos en tu app.

Importaciones

Las importaciones te permiten hacer referencia a clases dentro de tu archivo de diseño, como en el código administrado. Puedes usar cero o más elementos import dentro del elemento data. El En el siguiente ejemplo de código, se importa la clase View al archivo de diseño:

<data>
    <import type="android.view.View"/>
</data>

Importar la clase View te permite hacer referencia a ella desde las expresiones de vinculación. En el siguiente ejemplo, se muestra cómo hacer referencia al VISIBLE y Constantes de GONE de la clase View:

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

Escribe alias

Cuando hay conflictos de nombres de clases, puedes cambiar el nombre de una de las clases por uno alias. En el siguiente ejemplo, se cambia el nombre de la clase View en Paquete com.example.real.estate a Vista:

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

Luego, puedes usar Vista para hacer referencia a com.example.real.estate.View y View. para hacer referencia a android.view.View en el archivo de diseño.

Importa otras clases

Puedes usar tipos importados como referencias de tipos en variables y expresiones. El El siguiente ejemplo muestra User y List usados como el tipo de una variable:

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List&lt;User>"/>
</data>

Puedes usar los tipos importados para convertir parte de una expresión. Lo siguiente En el ejemplo, se convierte la propiedad connection a un tipo de User:

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

También puedes usar tipos importados cuando hagas referencia a campos y métodos estáticos en con expresiones regulares. Con el siguiente código, se importa la clase MyStringUtils y las referencias. su método 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"/>

Al igual que en el código administrado, se importa automáticamente java.lang.*.

Variables

Puedes usar varios elementos variable dentro del elemento data. Cada El elemento variable describe una propiedad que se puede establecer en el diseño que se usará. en expresiones de vinculación dentro del archivo de diseño. En el siguiente ejemplo, se declara las variables user, image y 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>

Los tipos de variables se inspeccionan en el tiempo de compilación, por lo que, si una variable implementa Observable o es un recopilación observable, que debe reflejarse en el tipo. Si la variable es una interfaz o clase base que no implementa la interfaz Observable, las variables no observadas.

Cuando hay diferentes archivos de diseño para varias configuraciones (por ejemplo, horizontal o vertical), se combinan las variables. No debe haber definiciones de variables conflictivas entre estos archivos de diseño.

La clase Binding generada tiene un método set y un método get para cada uno de los variables. Las variables toman los valores predeterminados de código administrado hasta que el método set se llama: null para los tipos de referencia, 0 para int, false para los tipos de referencia. boolean, etcétera

Se genera una variable especial llamada context para usarla en expresiones de vinculación. según sea necesario. El valor de context es el Context desde la vista raíz getContext(). El La variable context se anula mediante una declaración de variable explícita con esa de la fuente de datos.

Inclusiones

Puedes pasar variables a la vinculación de un diseño incluido desde la biblioteca usando el espacio de nombres de la app y el nombre de la variable en un atributo. El En el siguiente ejemplo, se muestran las variables user incluidas de name.xml y Archivos de diseño 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>

La vinculación de datos no admite una inclusión como elemento secundario directo de un elemento de combinación. Por ejemplo, el siguiente diseño no es compatible:

<?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>

Recursos adicionales

Para obtener más información sobre la vinculación de datos, consulta los siguientes recursos adicionales.

Ejemplos

Codelabs

Entradas de blog