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 miViewModel
que uso para el enlace de datos. También puede usar una clase que amplíeBaseObservable
y use@Bindable
. Sin embargo, elBindingAdapter
en este ejemplo, ¡ no tiene que estar en una claseViewModel
oBaseObservable
! ¡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}"/>