web-dev-qa-db-fra.com

Définir un ID de ressource pouvant être dessiné dans Android: src pour ImageView à l'aide de la liaison de données dans Android

J'essaie de définir l'ID de ressource pouvant être dessiné sur Android: src de ImageView en utilisant la liaison de données

Voici mon objet:

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;
    }

    // ...
}

Voici ma mise en page:

<?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>

Et enfin, classe d’activité:

// ...

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);
    }

    // ...

}

Il n'affiche pas d'image du tout. Qu'est-ce que je fais mal?

BTW, cela fonctionnait parfaitement avec la méthode standard:

@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);

}
57
Yuriy Seredyuk

Réponse en date du 10 nov 2016

Le commentaire de Splash ci-dessous a mis en évidence qu'il n'est pas nécessaire d'utiliser un type de propriété personnalisé (comme imageResource), nous pouvons créer plusieurs méthodes pour Android:src comme suit:

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);
    }
}

Old Answer

Vous pouvez toujours essayer d'utiliser un adaptateur:

public class DataBindingAdapters {

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

Vous pouvez ensuite utiliser l'adaptateur dans votre fichier xml comme si

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

Assurez-vous de noter que le nom dans le fichier xml correspond à l'annotation BindingAdapter (imageResource)

La classe DataBindingAdapters n'a pas besoin d'être déclarée nulle part en particulier, les mécaniciens de DataBinding le trouveront peu importe (je crois)

72
Joe Maher

définir:

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

utilisation:

<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}"/>
19
qinmiao

Ne remplacez jamais les attributs SDK standard lorsque vous créez votre propre @BindingAdapter!

Ce n'est pas une bonne approche pour de nombreuses raisons, telles que: En outre, cela pourrait dérouter les développeurs et rendre la possibilité de réutilisation plus difficile (car il n’est pas exclu qu’il soit remplacé) 

vous pouvez utiliser différents espaces de noms comme:

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

ou

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

------ démarrer la mise à jour 2.juil.2018

L’utilisation de l’espace de noms n’est pas recommandée. Il est donc préférable de s’appuyer sur un préfixe ou un nom différent, comme suit:

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

ou

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

------ fin de la mise à jour 2.juillet.2018

Cependant, je recommanderais une solution différente comme:

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

La vue contextuelle est toujours disponible dans l'expression de liaison @{ ... }

7
Maher Abuthraa

Pour Kotlin mettez ceci dans un fichier utils de niveau supérieur, aucun contexte statique/compagnon n'est nécessaire:

@BindingAdapter("Android:src")
fun setImageViewResource(view: ImageView, resId : Int) {
    view.setImageResource(resId)
}
4
joecks
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}"/>
3
mustafasevgi

Plus vous pouvez faire avec DataBindingAdapter

Définissez l'un de ces types:

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`}"

Set Error image/Placeholder image

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`}"
    />

Testé tous les types

 SC

Cela devient donc possible avec un adaptateur de liaison unique. Il suffit de copier ce projet de méthode.

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);
    }
}

Raison pour laquelle j'ai utilisé Glide pour charger tous les objets

Si vous me demandez pourquoi j'ai utilisé Glide pour charger l'identifiant de ressource/dessinable, je pourrais plutôt utiliser imageView.setImageBitmap(); ou imageView.setImageResource();. Donc la raison est que 

  • Glide est une structure efficace de chargement d’images qui englobe le décodage des supports, la mémoire et la mise en cache du disque. Vous n'avez donc pas à vous soucier des images et du cache de grande taille.
  • Pour assurer la cohérence lors du chargement de l'image. Maintenant, tous les types de ressources d’image sont chargés par Glide.

Si vous utilisez Piccaso, Fresso ou toute autre bibliothèque de chargement d'images, vous pouvez modifier la méthode loadImageWithGlide.

2
Khemraj

Utilisation de Fresco (bibliothèque d'images 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));
        }
    }
}
1
bong jae choe

Je ne suis pas un expert sur Android, mais j'ai passé des heures à essayer de déchiffrer les solutions existantes. La bonne chose est que j'ai saisi l'idée de la liaison de données en utilisant BindingAdapter un peu mieux. Pour cela, je suis au moins reconnaissant des réponses existantes (bien que très incomplètes). Voici un aperçu complet de l'approche:

Je vais aussi utiliser la BindingAdapter dans cet exemple. Préparation de la 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>

Donc ici je ne garde que les choses importantes:

  • SomeViewModel est ma ViewModel que j'utilise pour la liaison de données. Vous pouvez également utiliser une classe qui étend la variable BaseObservable et utiliser @Bindable. Cependant, la variable BindingAdapter dans cet exemple, ne doit pas nécessairement être être dans une classe ViewModel ou BaseObservable! Une classe ordinaire fera l'affaire! Ceci sera illustré plus tard.
  • app:appIconDrawable="@{model.packageName}". Oui ... ça me causait vraiment des maux de tête! Décomposons-le:
    • app:appIconDrawable: Cela peut être n'importe quoi: app:iCanBeAnything! Vraiment. Vous pouvez aussi garder "Android:src"! Cependant, prenez une note sur votre choix, nous l'utiliserons plus tard!
    • "@ {model.packageName}": Si vous avez travaillé avec data binding , cela vous est familier. Je vais montrer comment cela est utilisé plus tard.

Supposons que nous utilisions cette simple classe 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));
   }
}

Comme promis, vous pouvez également déplacer la public static void setImageViewDrawable(), dans une autre classe, par exemple. vous pouvez peut-être une classe qui a une collection de BindingAdapters:

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

Une autre remarque importante est que, dans ma classe Observable, j'ai utilisé String packageName pour transmettre des informations supplémentaires à la setImageViewDrawable. Vous pouvez également choisir par exemple int resourceId, avec les getters/setters correspondants, pour lequel l'adaptateur devient:

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);
   }
}
0
Tanasis

vous pouvez faire ce qui suit

Android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
0
FAHD Alotaibi