Imposta l'ID risorsa disegnabile in Android: src per ImageView utilizzando l'associazione dati in Android


94

Sto cercando di impostare l'ID risorsa disegnabile su android: src di ImageView utilizzando l'associazione dati

Ecco il mio oggetto:

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

    // ...
}

Ecco il mio layout:

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

E infine, lezione di attività:

// ...

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

    // ...

}

Non mostra affatto l'immagine. Che cosa sto facendo di sbagliato?

BTW, funzionava perfettamente con il modo 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);

}

Risposte:


114

Risposta del 10 novembre 2016

Il commento di Splash di seguito ha evidenziato che non è necessario utilizzare un tipo di proprietà personalizzato (come imageResource), possiamo invece creare più metodi per android:srccosì:

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

Vecchia risposta

Puoi sempre provare a usare un adattatore:

public class DataBindingAdapters {

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

Puoi quindi usare l'adattatore nel tuo xml in questo modo

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

Assicurati di notare che il nome all'interno dell'xml corrisponde all'annotazione BindingAdapter (imageResource)

La classe DataBindingAdapters non ha bisogno di essere dichiarata da nessuna parte in particolare, la meccanica di DataBinding la troverà indipendentemente (credo)


Funziona usando @BindingAdapter. Ma, il valore dovrebbe essere android:src, no imageResource: @BindingAdapter("android:src"). Grazie!
Yuriy Seredyuk

5
@YuriySeredyuk Nooooo! haha per favore. Ciò sovrascriverà ogni singolo utilizzo di "android: src" all'interno dell'xml nell'intera applicazione, cosa che sicuramente NON vorresti fare. Per far funzionare imageResource devi cambiare l'xml per imageView come ho fatto sopra, l'associazione dati risolverà cosa mettere lì
Joe Maher

1
OK, ho capito l'idea. Ora usando <ImageView imageResource="@{recipe.imageResource}" />e @BindingAdapter("imageResource"). Ho appena perso una imageResource="@{recipe.imageResource}"parte del tuo codice tagliato :)
Yuriy Seredyuk

1
Non è necessario che lo sia app:imageResource?
NameSpace

1
"In questo modo si sovrascriverà ogni singolo utilizzo di" android: src "all'interno dell'xml nell'intera applicazione" L'associazione dati non è abbastanza intelligente da applicare solo quell'attributo a ImageView, perché è quello che è definito nella funzione? Penso che "android: src" sarebbe preferibile ... considera cosa fa Android stesso per gli adattatori di binding ImageView: android.googlesource.com/platform/frameworks/data-binding/+/…
Splash

45

Non c'è affatto bisogno di un'usanza BindingAdapter. Basta usare

app:imageResource="@{yourResId}"

e funzionerà bene.

Controlla questo per come funziona.


2
questo dovrebbe avere più voti positivi in ​​quanto è un'ottima risposta intorno al 2020
JohnnyLambada

sicuramente, la risposta migliore e più semplice
luckyhandler

Questa sembra la risposta migliore e più appropriata alla fine del 2020
dal

Sto dando un'occhiata alla ImageViewclasse e seguendo il setImageResourcemetodo, sembra che alla fine sia risolto resolveUrie non ci sia se non zero convalida. Quindi potrebbe funzionare per Intmi chiedo cosa potrebbe accadere se Int?. Quando vengono eseguiti i collegamenti, ad esempio se viene chiamato qualcos'altro, executePendingBindingsnon annullabile viene impostato automaticamente a zero, nullable a null.
cutiko

25

definire:

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

uso:

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

dove aggiungo questo metodo
myatmins

supportalo aggiungilo in qualsiasi classe, forse puoi creare un ImageDataBindingAdapter.class.
qinmiao

12

Non sovrascrivere mai gli attributi SDK standard quando ne crei uno tuo @BindingAdapter!

Questo non è un buon approccio per molte ragioni come: impedirà di ottenere i vantaggi delle nuove correzioni sull'aggiornamento di Android SDK su quell'attributo. Inoltre potrebbe confondere gli sviluppatori e sicuramente complicato per la riusabilità (perché non è previsto che venga sovrascritto)

puoi usare diversi spazi dei nomi come:

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

o

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

------ avvia l'aggiornamento 2 luglio 2018

Non è consigliabile utilizzare lo spazio dei nomi, quindi è meglio fare affidamento sul prefisso o su un nome diverso come:

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

o

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

------ terminare l'aggiornamento 2 luglio 2018

Tuttavia, consiglierei una soluzione diversa come:

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

la vista contesto è sempre disponibile all'interno dell'espressione di associazione @{ ... }


1
Penso che il codice all'interno di xml dovrebbe essere evitato il più possibile: non è testabile, può accumularsi e non è ovvio. Ma sono d'accordo che il sovraccarico degli attributi standard possa creare confusione. Penso che il modo migliore sia nominare il nuovo attributo in modo diverso, in questo caso "srcResId", ma utilizzare comunque un BindingAdapter
Kirill Starostin

8

Basandomi sulla risposta di Maher Abuthraa, questo è ciò che ho finito per usare nell'XML:

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

La contextvariabile è disponibile nell'espressione di associazione senza alcuna importazione. Inoltre, nessuna personalizzazione BindingAdapternecessaria. Unica avvertenza: il metodo getDrawableè disponibile solo dall'API 21.


6

Per Kotlin mettilo in un file utils di primo livello, non è necessario alcun contesto statico / companion:

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

5

Più puoi fare con DataBindingAdapter

Imposta uno di questi tipi:

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

Imposta immagine errore / immagine segnaposto

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

Testato tutti i tipi

SC

In questo modo diventa possibile con un singolo adattatore di rilegatura. Basta copiare questo progetto di metodo.

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

Motivo per cui ho usato Glide per caricare tutti gli oggetti

Se mi chiedi perché ho usato Glide per caricare drawable / resource id, invece potrei usare imageView.setImageBitmap();o imageView.setImageResource();. Quindi il motivo è quello

  • Glide è un efficiente framework di caricamento delle immagini che avvolge la decodifica multimediale, la memoria e il caching del disco. Quindi non devi preoccuparti di immagini di grandi dimensioni e cache.
  • Per rendere la coerenza durante il caricamento dell'immagine. Ora tutti i tipi di risorse immagine vengono caricati da Glide.

Se utilizzi Piccaso, Fresso o qualsiasi altra libreria di caricamento immagini, puoi apportare modifiche al loadImageWithGlidemetodo .


`errorImage =" @ {@ drawable / ic_launcher} "". Questa cosa non si compila nemmeno per me
Vivek Mishra

@VivekMishra Forse il tuo ic_launcher è in mipmap ?, prova @ mipmap / ic_launcher.
Khemraj

@VivekMishra Puoi incollare il registro degli errori pertinente? Hai aggiunto questo metodo nella tua classe di utilità di associazione?
Khemraj

**** / errore di associazione dati **** msg: Impossibile trovare il getter per l'attributo "app: src" con tipo di valore java.lang.String su com.zuowei.circleimageview.CircleImageView. Ho provato sia con Android che con lo spazio dei nomi delle app ed entrambi non hanno funzionato per me. Ho anche sostituito la visualizzazione immagine predefinita con circleImageView nel parametro
Vivek Mishra

Inoltre ho creato l'adattatore di rilegatura in una classe separata
Vivek Mishra

3
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

puoi fare quanto segue

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

2

Non sono un esperto di Android ma ho passato ore a cercare di decifrare le soluzioni esistenti. La cosa buona è che ho afferrato l'intera idea del data binding usando BindingAdapterun po 'meglio. Per questo, sono almeno grato per le risposte esistenti (sebbene pesantemente incomplete). Ecco una ripartizione completa dell'approccio:

Userò anche BindingAdapterin questo esempio. Preparare 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>

Quindi eccomi qui a tenere solo le cose importanti:

  • SomeViewModelè il mio ViewModelutilizzo per il data binding. Puoi anche usare una classe che estende BaseObservablee usa @Bindable. Tuttavia, BindingAdapterin questo esempio, non deve essere in una classe ViewModelo BaseObservable! Una semplice lezione andrà bene! Questo verrà illustrato più avanti.
  • app:appIconDrawable="@{model.packageName}". Sì ... questo mi stava davvero causando mal di testa! Analizziamolo:
    • app:appIconDrawable: Può essere qualsiasi cosa app:iCanBeAnything:! Veramente. Puoi anche tenerlo "android:src"! Tuttavia, prendi nota della tua scelta, la useremo in seguito!
    • "@ {model.packageName}": se hai lavorato con il data binding , questo ti è familiare. Mostrerò in seguito come viene utilizzato.

Supponiamo di utilizzare questa semplice 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 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));
   }
}

Come promesso, puoi anche spostare il public static void setImageViewDrawable(), in qualche altra classe, ad esempio potresti avere una classe che ha una raccolta di BindingAdapters:

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

Un'altra osservazione importante è che nella mia Observableclasse String packageNamepassavo informazioni extra al file setImageViewDrawable. Puoi anche scegliere ad esempio int resourceId, con i corrispondenti getter / setter, per i quali l'adattatore diventa:

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

2

Questo lavoro per me. Lo avrei aggiunto alla risposta @hqzxzwb come commento ma a causa dei limiti di reputazione.

Ho questo a mio avviso Modello

var passport = R.drawable.passport

Quindi nel mio xml, ho

android:src="@{context.getDrawable(model.passort)}"

E questo è tutto


OK ma dimentichi di menzionare che devi importare il contesto. Potresti aggiornare la tua risposta?
deadfish

1

Utilizzo di Fresco (libreria di immagini di 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));
        }
    }
}

0

Nello stato di visualizzazione o nella classe del modello di visualizzazione;

 fun getSource(context: Context): Drawable? {
        return ContextCompat.getDrawable(context, R.drawable.your_source)
    }

Nel tuo XML;

<androidx.appcompat.widget.AppCompatImageButton
   .
   .
   .
   android:src="@{viewState.getSource(context)}"

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

0

imposta un'immagine in questo modo,

  <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" />
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.