Los adaptadores de vinculación se encargan de realizar las llamadas de framework apropiadas a
establecer valores. Un ejemplo es establecer un valor de propiedad, como llamar a
setText()
. Otro
ejemplo es configurar un objeto de escucha de eventos, como llamar al
setOnClickListener()
.
La biblioteca de vinculación de datos te permite especificar el método llamado para establecer un valor, proporcionar tu propia lógica de vinculación y especificar el tipo de objeto que se devuelve con adaptadores.
Configura los valores de atributos
Cada vez que cambia un valor vinculado, la clase de vinculación generada debe llamar a un método set en la vista con la expresión de vinculación. Puedes permitir que la vinculación de datos La biblioteca determina automáticamente el método, o puedes declarar explícitamente o proporcionar una lógica personalizada para seleccionar un método.
Selección automática de métodos
Para un atributo llamado example
, la biblioteca encuentra automáticamente el método
setExample(arg)
, que acepta tipos compatibles como argumento. El espacio de nombres
del atributo. Solo se usan el nombre y el tipo de atributo
cuando se busca un método.
Por ejemplo, con la expresión android:text="@{user.name}"
, la biblioteca
busca un método setText(arg)
que acepte el tipo que muestra
user.getName()
Si el tipo de datos que se muestra de user.getName()
es String
, la
La biblioteca busca un método setText()
que acepte un argumento String
. Si el botón
expresión muestra un int
, la biblioteca busca un método setText()
que
acepta un argumento int
. La expresión debe mostrar el tipo correcto. Puedes
y convertir el valor de muestra si es necesario.
La vinculación de datos funciona incluso si no existe un atributo con el nombre dado. Puedes
crear atributos para cualquier método set con vinculación de datos Por ejemplo, la asistencia
clase
DrawerLayout
no tiene atributos, pero tiene muchos métodos set. El siguiente diseño
utiliza automáticamente el
setScrimColor(int)
y
addDrawerListener(DrawerListener)
métodos como el método set para app:scrimColor
y app:drawerListener
.
de atributos, respectivamente:
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
Especifica un nombre de método personalizado
Algunos atributos tienen métodos set que no coinciden por nombre. En estas situaciones, un
pueden asociarse al método set a través del
BindingMethods
. La anotación se usa con una clase y puede contener múltiples
BindingMethod
anotaciones, una para cada método renombrado. Los métodos de vinculación son anotaciones que
que puedes agregar a cualquier clase en tu app.
En el siguiente ejemplo, el atributo android:tint
está asociado con el
setImageTintList(ColorStateList)
no con el método setTint()
:
Kotlin
@BindingMethods(value = [ BindingMethod( type = android.widget.ImageView::class, attribute = "android:tint", method = "setImageTintList")])
Java
@BindingMethods({ @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"), })
Por lo general, no es necesario cambiar el nombre de los métodos set en las clases del framework de Android. El ya se implementaron usando la convención de nombres para buscar métodos coincidentes.
Proporciona lógica personalizada
Algunos atributos requieren una lógica de vinculación personalizada. Por ejemplo, no hay datos asociados
método set para el atributo android:paddingLeft
. En su lugar, se proporciona el método setPadding(left,
top, right, bottom)
. Un método de adaptador de vinculación estático con
el BindingAdapter
te permite personalizar el nombre de un método set para un atributo.
Los atributos de las clases del framework de Android ya tienen BindingAdapter
anotaciones. En el siguiente ejemplo, se muestra el adaptador de vinculación para la
Atributo paddingLeft
:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, padding: Int) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) }
Java
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int padding) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); }
Los tipos de parámetros son importantes. El primer parámetro determina el tipo de la vista asociada con el atributo. El segundo parámetro determina el tipo aceptado en la expresión de vinculación para el atributo especificado.
Los adaptadores de vinculación también son útiles para otros tipos de personalización. Por ejemplo: se puede llamar a un cargador personalizado desde un subproceso de trabajo para cargar una imagen.
También puedes tener adaptadores que reciban múltiples atributos, como se muestra en el siguiente ejemplo:
Kotlin
@BindingAdapter("imageUrl", "error") fun loadImage(view: ImageView, url: String, error: Drawable) { Picasso.get().load(url).error(error).into(view) }
Java
@BindingAdapter({"imageUrl", "error"}) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.get().load(url).error(error).into(view); }
Puedes usar el adaptador en tu diseño, como se muestra en el siguiente ejemplo. Nota
que @drawable/venueError
hace referencia a un recurso de tu app. Alrededor de
con @{}
la convierte en una expresión de vinculación válida.
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
Se llama al adaptador si se usan imageUrl
y error
para un
ImageView
, imageUrl
es un
string y error
es una
Drawable
. Si quieres
el adaptador al que se llamará cuando se establezca cualquiera de los atributos, establece el estado
requireAll
marca del adaptador a false
, como se muestra en el siguiente ejemplo:
Kotlin
@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false) fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } }
Java
@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false) public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } }
Los métodos de adaptador de vinculación pueden tomar los valores anteriores en sus controladores. Un método Para tomar los valores antiguos y nuevos, primero se deben declarar todos los valores anteriores para los atributos. seguido de los valores nuevos, como se muestra en el siguiente ejemplo:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) { if (oldPadding != newPadding) { view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) } }
Java
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int oldPadding, int newPadding) { if (oldPadding != newPadding) { view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); } }
Los controladores de eventos solo se pueden usar con interfaces o clases abstractas, con una abstracto, como se muestra en el siguiente ejemplo:
Kotlin
@BindingAdapter("android:onLayoutChange") fun setOnLayoutChangeListener( view: View, oldValue: View.OnLayoutChangeListener?, newValue: View.OnLayoutChangeListener? ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (oldValue != null) { view.removeOnLayoutChangeListener(oldValue) } if (newValue != null) { view.addOnLayoutChangeListener(newValue) } } }
Java
@BindingAdapter("android:onLayoutChange") public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue, View.OnLayoutChangeListener newValue) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (oldValue != null) { view.removeOnLayoutChangeListener(oldValue); } if (newValue != null) { view.addOnLayoutChangeListener(newValue); } } }
Usa este controlador de eventos en el diseño de la siguiente manera:
<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
Cuando un objeto de escucha tiene varios métodos, debe dividirse en múltiples objetos de escucha.
Por ejemplo:
View.OnAttachStateChangeListener
tiene dos métodos:
onViewAttachedToWindow(View)
y
onViewDetachedFromWindow(View)
La biblioteca proporciona dos interfaces para diferenciar los atributos y los controladores
para ellos:
Kotlin
// Translation from provided interfaces in Java: @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewDetachedFromWindow { fun onViewDetachedFromWindow(v: View) } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewAttachedToWindow { fun onViewAttachedToWindow(v: View) }
Java
@TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewDetachedFromWindow { void onViewDetachedFromWindow(View v); } @TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewAttachedToWindow { void onViewAttachedToWindow(View v); }
Como cambiar un objeto de escucha puede afectar al otro, necesitas un adaptador
funciona con un atributo o con ambos. Puedes configurar requireAll
como false
en
la anotación para especificar que no a todos los atributos se les debe asignar una vinculación
expresión, como se muestra en el siguiente ejemplo:
Kotlin
@BindingAdapter( "android:onViewDetachedFromWindow", "android:onViewAttachedToWindow", requireAll = false ) fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { val newListener: View.OnAttachStateChangeListener? newListener = if (detach == null && attach == null) { null } else { object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { attach.onViewAttachedToWindow(v) } override fun onViewDetachedFromWindow(v: View) { detach.onViewDetachedFromWindow(v) } } } val oldListener: View.OnAttachStateChangeListener? = ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener) if (oldListener != null) { view.removeOnAttachStateChangeListener(oldListener) } if (newListener != null) { view.addOnAttachStateChangeListener(newListener) } } }
Java
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"}, requireAll=false) public static void setListener(View view, OnViewDetachedFromWindow detach, OnViewAttachedToWindow attach) { if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) { OnAttachStateChangeListener newListener; if (detach == null && attach == null) { newListener = null; } else { newListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { if (attach != null) { attach.onViewAttachedToWindow(v); } } @Override public void onViewDetachedFromWindow(View v) { if (detach != null) { detach.onViewDetachedFromWindow(v); } } }; } OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener); if (oldListener != null) { view.removeOnAttachStateChangeListener(oldListener); } if (newListener != null) { view.addOnAttachStateChangeListener(newListener); } } }
El ejemplo anterior es un poco complicado porque
View
usa el elemento
addOnAttachStateChangeListener()
y
removeOnAttachStateChangeListener()
en lugar de un método set para
OnAttachStateChangeListener
.
La clase android.databinding.adapters.ListenerUtil
ayuda a hacer un seguimiento de estos
objetos de escucha para que puedan quitarse en el adaptador de vinculación.
Conversiones de objetos
Conversión automática de objetos
Cuando se muestra un Object
desde una vinculación
expresión, la biblioteca selecciona el método utilizado para establecer el valor de la
propiedad. Object
se transmite a un tipo de parámetro del método elegido. Esta
el comportamiento de los usuarios es conveniente en las apps que usan
ObservableMap
para
almacenar datos, como se muestra en el siguiente ejemplo:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
El objeto userMap
de la expresión muestra un valor, que se
convertir al tipo de parámetro encontrado en el método setText(CharSequence)
que se usa para
configura el valor del atributo android:text
. Si el tipo de parámetro es
ambiguo, transmite el tipo de datos que se muestra en la expresión.
Conversiones personalizadas
En algunos casos, se requiere una conversión personalizada entre tipos específicos. Para
Por ejemplo, el atributo android:background
de una vista espera un Drawable
, pero
El valor color
especificado es un número entero. En el siguiente ejemplo, se muestra un
que espera un Drawable
, pero se proporciona un número entero en su lugar:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Cada vez que se espere un Drawable
y se muestre un número entero, convierte int
.
a un elemento ColorDrawable
.
Para realizar la conversión, usa un método estático con una
BindingConversion
anotación, como se muestra a continuación:
Kotlin
@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)
Java
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
Sin embargo, los tipos de valor proporcionados en la expresión de vinculación deben ser coherentes. No puedes usar tipos diferentes en la misma expresión, como se muestra a continuación ejemplo:
// The @drawable and @color represent different value types in the same
// expression, which causes a build error.
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Recursos adicionales
Para obtener más información sobre la vinculación de datos, consulta los siguientes recursos.
Ejemplos
Codelabs
Entradas de blog
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Biblioteca de vinculación de datos
- Diseños y expresiones vinculantes
- Vinculación de vistas