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<
) 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<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]}"
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 a continuación 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 escuchanull
. - 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<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.