tutorial studio example español ejemplo databinding data android android-layout android-databinding

android - studio - data binding español



Establecer ID de recurso dibujable en android: src para ImageView usando el enlace de datos en Android (9)

Estoy tratando de configurar la identificación de recursos dibujables para Android: src de ImageView usando el enlace de datos

Aquí está mi objeto:

public class Recipe implements Parcelable { public final int imageResource; // resource ID (e.g. R.drawable.some_image) public final String title; // ... public Recipe(int imageResource, String title /* ... */) { this.imageResource = imageResource; this.title = title; } // ... }

Aquí está mi diseño:

<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="recipe" type="com.example.android.fivewaystocookeggs.Recipe" /> </data> <!-- ... --> <ImageView android:id="@+id/recipe_image_view" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop" android:src="@{recipe.imageResource}" /> <!-- ... --> </layout>

Y finalmente, clase de actividad:

// ... public class RecipeActivity extends AppCompatActivity { public static final String RECIPE_PARCELABLE = "recipe_parcelable"; private Recipe mRecipe; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRecipe = getIntent().getParcelableExtra(RECIPE_PARCELABLE); ActivityRecipeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_recipe); binding.setRecipe(mRecipe); } // ... }

No muestra imagen en absoluto. ¿Qué estoy haciendo mal?

Por cierto, funcionaba perfectamente con la forma estándar:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recipe); final ImageView recipeImageView = (ImageView) findViewById(R.id.recipe_image_view); recipeImageView.setImageResource(mRecipe.imageResource); }


Cuanto más puedas hacer con DataBindingAdapter

  • Puede configurar la URL de la imagen , el archivo , el mapa de bits , la matriz de bytes , el Drawable , el Drawable Id cualquier cosa mediante el enlace de datos.
  • También puede establecer imágenes de error / imágenes de marcador de posición pasando varios parámetros al adaptador de enlace .

Establezca cualquiera de estos tipos:

android:src="@{model.profileImage}" android:src="@{roundIcon ? @drawable/ic_launcher_round : @drawable/ic_launcher_round}" android:src="@{bitmap}" android:src="@{model.drawableId}" android:src="@{@drawable/ic_launcher}" android:src="@{file}" android:src="@{`https://placekitten.com/200/200`}"

Establecer imagen de error / imagen de marcador de posición

placeholderImage="@{@drawable/img_placeholder}" errorImage="@{@drawable/img_error}" <ImageView placeholderImage="@{@drawable/ic_launcher}" errorImage="@{@drawable/ic_launcher}" android:layout_width="100dp" android:layout_height="100dp" android:src="@{`https://placekitten.com/2000/2000`}" />

Probado todos los tipos

Entonces eso es posible con un adaptador de enlace único. Simplemente copie este proyecto de método.

public class BindingAdapters { @BindingAdapter(value = {"android:src", "placeholderImage", "errorImage"}, requireAll = false) public static void loadImageWithGlide(ImageView imageView, Object obj, Object placeholder, Object errorImage) { RequestOptions options = new RequestOptions(); if (placeholder instanceof Drawable) options.placeholder((Drawable) placeholder); if (placeholder instanceof Integer) options.placeholder((Integer) placeholder); if (errorImage instanceof Drawable) options.error((Drawable) errorImage); if (errorImage instanceof Integer) options.error((Integer) errorImage); RequestManager manager = Glide.with(App.getInstance()). applyDefaultRequestOptions(options); RequestBuilder<Drawable> builder; if (obj instanceof String) { builder = manager.load((String) obj); } else if (obj instanceof Uri) builder = manager.load((Uri) obj); else if (obj instanceof Drawable) builder = manager.load((Drawable) obj); else if (obj instanceof Bitmap) builder = manager.load((Bitmap) obj); else if (obj instanceof Integer) builder = manager.load((Integer) obj); else if (obj instanceof File) builder = manager.load((File) obj); else if (obj instanceof Byte[]) builder = manager.load((Byte[]) obj); else builder = manager.load(obj); builder.into(imageView); } }

Razón por la que usé Glide para cargar todos los objetos

Si me pregunta por qué usé Glide para cargar la identificación de recursos / imageView.setImageBitmap(); , en su lugar podría usar imageView.setImageBitmap(); o imageView.setImageResource(); . Entonces la razón es que

  • Glide es un marco de carga de imágenes eficiente que envuelve la decodificación de medios, la memoria y el almacenamiento en caché de disco. Por lo tanto, no debe preocuparse por las imágenes de gran tamaño y la memoria caché.
  • Para hacer coherencia al cargar la imagen. Ahora, Glide carga todos los tipos de recursos de imágenes.

Si utiliza Piccaso, Fresso o cualquier otra biblioteca de carga de imágenes, puede realizar cambios en el método loadImageWithGlide .


¡Nunca anule los atributos estándar del SDK cuando cree su propio @BindingAdapter !

Este no es un buen enfoque por muchas razones como: va a evitar obtener beneficios de nuevas correcciones en la actualización del SDK de Android en ese atributo. También puede confundir a los desarrolladores y seguramente complicado para la reutilización (porque no se detecta que se anule)

puede usar diferentes espacios de nombres como:

custom:src="@{recipe.imageResource}"

o

mybind:src="@{recipe.imageResource}"

------ inicie la Actualización 2.Jul.2018

No se recomienda utilizar el espacio de nombres, por lo que es mejor confiar en un prefijo o un nombre diferente como:

app:custom_src="@{recipe.imageResource}"

o

app:customSrc="@{recipe.imageResource}"

------ finalice la actualización 2.jul.2018

Sin embargo, recomendaría una solución diferente como:

android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"

la vista de contexto siempre está disponible dentro de la expresión de enlace @{ ... }


No soy un experto en Android, pero pasé horas tratando de descifrar las soluciones existentes. Lo bueno es que BindingAdapter un poco mejor la idea del enlace de datos usando BindingAdapter . Por eso, al menos estoy agradecido por las respuestas existentes (aunque muy incompletas). Aquí un desglose completo del enfoque:

También BindingAdapter en este ejemplo. Preparando el xml :

<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="model" type="blahblah.SomeViewModel"/> </data> <!-- blah blah --> <ImageView android:id="@+id/ImageView" app:appIconDrawable="@{model.packageName}"/> <!-- blah blah --> </layout>

Así que aquí guardo solo las cosas importantes:

  • SomeViewModel es mi ViewModel que uso para el enlace de datos. También puede usar una clase que amplíe BaseObservable y use @Bindable . Sin embargo, el BindingAdapter en este ejemplo, ¡ no tiene que estar en una clase ViewModel o BaseObservable ! ¡Una clase simple servirá! Esto se ilustrará más adelante.
  • app:appIconDrawable="@{model.packageName}" . Sí ... ¡esto realmente me estaba causando dolores de cabeza! Vamos a desglosarlo:
    • app:appIconDrawable : Esto puede ser cualquier cosa: app:iCanBeAnything ! De Verdad. ¡También puedes mantener "android:src" ! Sin embargo, tome nota de su elección, ¡la usaremos más tarde!
    • "@ {model.packageName}": si trabajó con el enlace de datos , esto le resulta familiar. Mostraré cómo se usa esto más adelante.

Supongamos que usamos esta simple clase Observable:

public class SomeViewModel extends BaseObservable { private String packageName; // this is what @{model.packageName} // access via the getPackageName() !!! // Of course this needs to be set at some // point in your program, before it makes // sense to use it in the BindingAdapter. @Bindable public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; notifyPropertyChanged(BR.packageName); } // The "appIconDrawable" is what we defined above! // Remember, they have to align!! As we said, we can choose whatever "app:WHATEVER". // The BindingAdapter and the xml need to aligned, that''s it! :) // // The name of the fuction, i.e. setImageViewDrawable, can also be // whatever we want! Doesn''t matter. @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } }

Como se prometió, también puede mover el public static void setImageViewDrawable() , a otra clase, por ejemplo, tal vez pueda una clase que tenga una colección de BindingAdapters :

public class BindingAdapterCollection { @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } }

Otra observación importante es que en mi clase Observable usé String packageName para pasar información adicional a setImageViewDrawable . También puede elegir, por ejemplo, int resourceId , con los getters / setters correspondientes, para lo cual el adaptador se convierte en:

public class SomeViewModel extends BaseObservable { private String packageName; // this is what @{model.packageName} // access via the getPackageName() !!! private int resourceId; // if you use this, don''t forget to update // your xml with: @{model.resourceId} @Bindable public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; notifyPropertyChanged(BR.packageName); } @Bindable public int getResourceId() { return packageName; } public void setResourceId(int resourceId) { this.resourceId = resourceId; notifyPropertyChanged(BR.resourceId); } // For this you use: app:appIconDrawable="@{model.packageName}" (passes String) @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } // for this you use: app:appIconResourceId="@{model.resourceId}" (passes int) @BindingAdapter({"appIconResourceId"}) public static void setImageViewResourceId(ImageView imageView, int resource) { imageView.setImageResource(resource); } }


Para Kotlin, coloque esto en un archivo de utilidades de nivel superior, no se necesita contexto estático / complementario:

@BindingAdapter("android:src") fun setImageViewResource(view: ImageView, resId : Int) { view.setImageResource(resId) }


Usando Fresco (biblioteca de imágenes de Facebook)

android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"


definir:

@BindingAdapter({"android:src"}) public static void setImageViewResource(ImageView imageView, int resource) { imageView.setImageResource(resource); }

utilizar:

<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:scaleType="center" android:src="@{viewModel.imageRes, default=@drawable/guide_1}"/>


puedes hacer lo siguiente

public class YourCustomBindingAdapters { //app:imageUrl="@{data.imgUri}" @BindingAdapter("bind:imageUrl") public static void loadImage(SimpleDraweeView imageView, String url) { if (url == null) { imageView.setImageURI(Uri.EMPTY); } else { if (url.length() == 0) imageView.setImageURI(Uri.EMPTY); else imageView.setImageURI(Uri.parse(url)); } } }


Respuesta a partir del 10 de noviembre de 2016

El comentario de Splash a continuación ha resaltado que no es necesario usar un tipo de propiedad personalizada (como imageResource ), en su lugar podemos crear múltiples métodos para android:src manera:

public class DataBindingAdapters { @BindingAdapter("android:src") public static void setImageUri(ImageView view, String imageUri) { if (imageUri == null) { view.setImageURI(null); } else { view.setImageURI(Uri.parse(imageUri)); } } @BindingAdapter("android:src") public static void setImageUri(ImageView view, Uri imageUri) { view.setImageURI(imageUri); } @BindingAdapter("android:src") public static void setImageDrawable(ImageView view, Drawable drawable) { view.setImageDrawable(drawable); } @BindingAdapter("android:src") public static void setImageResource(ImageView imageView, int resource){ imageView.setImageResource(resource); } }

Vieja respuesta

Siempre puedes intentar usar un adaptador:

public class DataBindingAdapters { @BindingAdapter("imageResource") public static void setImageResource(ImageView imageView, int resource){ imageView.setImageResource(resource); } }

Luego puede usar el adaptador en su xml así

<ImageView android:id="@+id/recipe_image_view" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop" imageResource="@{recipe.imageResource}" />

Asegúrese de notar que el nombre dentro del xml coincide con la anotación BindingAdapter (imageResource)

La clase DataBindingAdapters no necesita declararse en ningún lugar en particular, la mecánica de DataBinding lo encontrará sin importar (creo)


public Drawable getImageRes() { return mContext.getResources().getDrawable(R.drawable.icon); } <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="center" android:src="@{viewModel.imageRes}"/>