Estoy tratando de establecer el ID de recurso dibujable en 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 ninguna imagen. ¿Qué estoy haciendo mal?
Por cierto, estaba funcionando 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);
}
fuente
@BindingAdapter
. Sin embargo, el valor debe serandroid:src
, noimageResource
:@BindingAdapter("android:src")
. ¡Gracias!<ImageView imageResource="@{recipe.imageResource}" />
y@BindingAdapter("imageResource")
. Me perdíimageResource="@{recipe.imageResource}"
parte de tu código recortado :)app:imageResource
?No hay necesidad de una costumbre
BindingAdapter
en absoluto. Solo usaapp:imageResource="@{yourResId}"
y funcionará bien.
Compruebe esto para saber cómo funciona.
fuente
ImageView
clase y siguiendo elsetImageResource
método, parece que finalmente se resuelveresolveUri
y hay, si no cero, validación. Así que eso funcionaría porqueInt
me pregunto qué podría pasar siInt?
. Cuando se ejecutan los enlaces, por ejemplo, si otra llamada,executePendingBindings
entonces los que no aceptan valores nulos se establecen por defecto en cero, los valores nulos en nulos.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}"/>
fuente
¡Nunca anule los atributos estándar del SDK cuando cree los suyos propios
@BindingAdapter
!Este no es un buen enfoque por muchas razones como: 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 es complicado para la reutilización (porque no se espera que se anule)
puede usar diferentes espacios de nombres como:
custom:src="@{recipe.imageResource}"
o
mybind:src="@{recipe.imageResource}"
------ iniciar actualización 2.Jul.2018
No se recomienda el uso del espacio de nombres, por lo que es mejor confiar en el prefijo o en un nombre diferente como:
app:custom_src="@{recipe.imageResource}"
o
app:customSrc="@{recipe.imageResource}"
------ Finalizar 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
@{ ... }
fuente
Sobre la base de la respuesta de Maher Abuthraa, esto es lo que terminé usando en el XML:
android:src="@{context.getDrawable(recipe.imageResource)}"
La
context
variable está disponible en expresión de enlace sin ninguna importación. Además, no esBindingAdapter
necesario personalizarlo . Solo advertencia: el métodogetDrawable
solo está disponible desde API 21.fuente
Para Kotlin, ponga esto en un archivo de utils de nivel superior, no se necesita contexto estático / complementario:
@BindingAdapter("android:src") fun setImageViewResource(view: ImageView, resId : Int) { view.setImageResource(resId) }
fuente
Cuanto más puedas hacer con
DataBindingAdapter
Configure 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
Eso es posible con un solo adaptador de enlace. 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 preguntas por qué usé Glide para cargar la identificación del recurso / dibujable, en su lugar podría usar
imageView.setImageBitmap();
oimageView.setImageResource();
. Entonces la razón es queSi usa Piccaso, Fresso o cualquier otra biblioteca de carga de imágenes, puede realizar cambios en el
loadImageWithGlide
método .fuente
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}"/>
fuente
puedes hacer lo siguiente
android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
fuente
No soy un experto en Android pero pasé horas intentando descifrar las soluciones existentes. Lo bueno es que comprendí
BindingAdapter
un poco mejor la idea de vinculación de datos . Por eso, estoy al menos agradecido por las respuestas existentes (aunque muy incompletas). Aquí un desglose completo del enfoque:También usaré
BindingAdapter
en este ejemplo. Preparando elxml
:<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í estoy guardando solo las cosas importantes:
SomeViewModel
es miViewModel
uso para el enlace de datos. También puede usar una clase que amplíeBaseObservable
y use@Bindable
. Sin embargo,BindingAdapter
en este ejemplo, ¡ no tiene que estar en una claseViewModel
oBaseObservable
! ¡Una clase sencilla servirá! Esto se ilustrará más adelante.app:appIconDrawable="@{model.packageName}"
. Sí ... ¡esto realmente me estaba causando dolores de cabeza! Vamos a analizarlo:app:appIconDrawable
: Esto puede ser cualquier cosaapp:iCanBeAnything
:! De Verdad. ¡También puedes quedarte"android:src"
! Sin embargo, tome nota de su elección, ¡la usaremos más adelante!Supongamos que usamos esta clase observable simple:
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 be aligned, that's it! :) // // The name of the function, 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 tener una clase que tenga una colección deBindingAdapters
:public class BindingAdapterCollection { @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } }
Otro comentario importante es que en mi
Observable
clase solíaString packageName
pasar información adicional alsetImageViewDrawable
. También puede elegir, por ejemploint 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); } }
fuente
Esto funciona para mí. Lo habría agregado a la respuesta de @hqzxzwb como comentario, pero debido a limitaciones de reputación.
Tengo esto en mi modelo de vista
var passport = R.drawable.passport
Luego, en mi xml, tengo
android:src="@{context.getDrawable(model.passort)}"
Y eso es
fuente
Usando Fresco (biblioteca de imágenes de Facebook)
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)); } } }
fuente
En su estado de vista o clase de modelo de vista;
fun getSource(context: Context): Drawable? { return ContextCompat.getDrawable(context, R.drawable.your_source) }
En su XML;
<androidx.appcompat.widget.AppCompatImageButton . . . android:src="@{viewState.getSource(context)}"
fuente
<?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" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="model" type="YourViewModel"/> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:paddingStart="@dimen/dp16" android:paddingTop="@dimen/dp8" android:paddingEnd="@dimen/dp8" android:paddingBottom="@dimen/dp8"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@{model.selected ? @drawable/check_fill : @drawable/check_empty}"/> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
fuente
establecer una imagen como esta,
<ImageView android:layout_width="28dp" android:layout_height="28dp" android:src="@{model.isActive ? @drawable/white_activated_icon :@drawable/activated_icon}" tools:src="@mipmap/white_activated_icon" />
fuente