Legen Sie die Zeichnungs-Ressourcen-ID in android: src für ImageView mithilfe der Datenbindung in Android fest


90

Ich versuche, die Drawable-Ressourcen-ID mithilfe der Datenbindung auf android: src von ImageView zu setzen

Hier ist mein Objekt:

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

    // ...
}

Hier ist mein 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>

Und schließlich Aktivitätsklasse:

// ...

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

    // ...

}

Es wird überhaupt kein Bild angezeigt. Was mache ich falsch?

Übrigens funktionierte es perfekt mit 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);

}

Antworten:


110

Antwort ab dem 10. November 2016

Der Kommentar von Splash unten hat hervorgehoben, dass es nicht erforderlich ist, einen benutzerdefinierten Eigenschaftstyp (wie imageResource) zu verwenden. Stattdessen können wir mehrere Methoden für das Gleiche erstellen android:src:

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

Alte Antwort

Sie können jederzeit versuchen, einen Adapter zu verwenden:

public class DataBindingAdapters {

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

Sie können dann den Adapter in Ihrer XML wie folgt verwenden

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

Beachten Sie, dass der Name in der XML-Datei mit der BindingAdapter-Annotation (imageResource) übereinstimmt.

Die DataBindingAdapters-Klasse muss nirgendwo deklariert werden, die DataBinding-Mechanik wird sie finden, egal (glaube ich)


Es funktioniert mit @BindingAdapter. Wert sollte aber android:srcnicht sein imageResource: @BindingAdapter("android:src"). Danke dir!
Yuriy Seredyuk

5
@YuriySeredyuk Nooooo! haha bitte Dadurch wird jede einzelne Verwendung von "android: src" in der XML-Datei in Ihrer gesamten Anwendung außer Kraft gesetzt, was Sie definitiv NICHT möchten. Damit imageResource funktioniert, müssen Sie die XML-Datei für die imageView wie oben beschrieben ändern. Die Datenbindung wird herausfinden, was dort abgelegt werden soll
Joe Maher,

1
OK, ich habe die Idee verstanden. Jetzt mit <ImageView imageResource="@{recipe.imageResource}" />und @BindingAdapter("imageResource"). Ich habe gerade einen imageResource="@{recipe.imageResource}"Teil von Ihrem Code verpasst.
Yuriy Seredyuk

1
Muss das nicht sein app:imageResource?
NameSpace

1
"Dadurch wird jede einzelne Verwendung von" android: src "in der XML-Datei in Ihrer gesamten Anwendung überschrieben." Ist die Datenbindung nicht intelligent genug, um dieses Attribut nur auf ImageView anzuwenden, da dies in der Funktion definiert ist? Ich denke, "android: src" wäre vorzuziehen ... Überlegen Sie, was Android selbst für ImageView-Bindungsadapter tut
Splash

34

Es ist überhaupt kein Brauch erforderlich BindingAdapter. Benutz einfach

app:imageResource="@{yourResId}"

und es wird gut funktionieren.

Überprüfen Sie dies , wie es funktioniert.


2
Dies sollte mehr positive Stimmen haben, da es eine großartige Antwort um 2020 ist
JohnnyLambada

25

definieren:

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

verwenden:

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

Wo füge ich diese Methode hinzu
Myatmins

Unterstützung Hinzufügen in jeder Klasse, vielleicht können Sie eine ImageDataBindingAdapter.class erstellen.
Qinmiao

12

Überschreiben Sie niemals Standard-SDK-Attribute, wenn Sie Ihre eigenen erstellen @BindingAdapter!

Dies ist aus vielen Gründen kein guter Ansatz: Es wird verhindert, dass neue Fixes für das Android SDK-Update für dieses Attribut verwendet werden. Es könnte auch Entwickler verwirren und sicherlich schwierig für die Wiederverwendbarkeit sein (weil es nicht erwartet wird, überschrieben zu werden)

Sie können verschiedene Namespace verwenden wie:

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

oder

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

------ Update 2.Jul.2018 starten

Es wird nicht empfohlen, den Namespace zu verwenden. Verwenden Sie daher besser das Präfix oder einen anderen Namen als:

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

oder

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

------ Ende Update 2.Jul.2018

Ich würde jedoch eine andere Lösung empfehlen als:

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

Die Kontextansicht ist immer innerhalb des Bindungsausdrucks verfügbar @{ ... }


1
Ich denke, dass Code in XML so weit wie möglich vermieden werden sollte - er ist nicht testbar, kann sich häufen und ist nicht offensichtlich. Aber ich stimme zu, dass das Überladen von Standardattributen verwirrend sein kann. Ich denke, der beste Weg ist, ein neues Attribut anders zu benennen, in diesem Fall "srcResId", aber immer noch einen BindingAdapter zu verwenden
Kirill Starostin

6

Damit Kotlin dies in eine Utils-Datei der obersten Ebene einfügt, ist kein statischer / begleitender Kontext erforderlich:

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

6

Aufbauend auf der Antwort von Maher Abuthraa habe ich Folgendes im XML verwendet:

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

Die contextVariable ist im Bindungsausdruck ohne Importe verfügbar . Auch kein Brauch BindingAdapternotwendig. Einzige Einschränkung: Die Methode getDrawableist erst seit API 21 verfügbar.


5

Je mehr Sie damit machen können DataBindingAdapter

Stellen Sie einen dieser Typen ein:

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

Fehlerbild / Platzhalterbild einstellen

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

Alle Typen getestet

SC

So wird das mit Single Binding Adapter möglich. Kopieren Sie einfach dieses Methodenprojekt.

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

Grund, warum ich Glide verwendet habe, um alle Objekte zu laden

Wenn Sie mich fragen, warum ich Glide zum Laden der Zeichen- / Ressourcen-ID verwendet habe, könnte ich stattdessen imageView.setImageBitmap();oder verwenden imageView.setImageResource();. Der Grund ist also der

  • Glide ist ein effizientes Framework zum Laden von Bildern, das die Dekodierung von Medien, den Speicher und das Zwischenspeichern von Datenträgern umfasst. Sie müssen sich also keine Gedanken über große Bilder und den Cache machen.
  • Konsistenz beim Laden des Bildes herstellen. Jetzt werden alle Arten von Bildressourcen von Glide geladen.

Wenn Sie Piccaso, Fresso oder eine andere Bildladebibliothek verwenden, können Sie die loadImageWithGlideMethode ändern .


`errorImage =" @ {@ drawable / ic_launcher} "`. Dieses Ding kompiliert nicht einmal für mich
Vivek Mishra

@VivekMishra Vielleicht befindet sich Ihr ic_launcher in der Mipmap? Versuchen Sie es mit @ mipmap / ic_launcher.
Khemraj

@VivekMishra Können Sie Ihr relevantes Fehlerprotokoll einfügen? Haben Sie diese Methode in Ihre Bindungs-Util-Klasse aufgenommen?
Khemraj

**** / Datenbindungsfehler **** msg: Der Getter für das Attribut 'app: src' mit dem Werttyp java.lang.String in com.zuowei.circleimageview.CircleImageView wurde nicht gefunden. Ich habe es sowohl mit Android als auch mit App-Namespace versucht und beide haben bei mir nicht funktioniert. Ich habe auch die Standardbildansicht durch circleImageView im Parameter
Vivek Mishra

Außerdem habe ich einen Bindungsadapter in einer separaten Klasse erstellt
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

Sie können Folgendes tun

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

2

Ich bin kein Experte für Android, aber ich habe stundenlang versucht, die vorhandenen Lösungen zu entschlüsseln. Das Gute ist, dass ich die ganze Idee der Datenbindung mit verstanden habeBindingAdapter etwas besser verstanden habe. Dafür bin ich zumindest dankbar für die vorhandenen Antworten (obwohl stark unvollständig). Hier eine vollständige Aufschlüsselung des Ansatzes:

Ich werde das auch BindingAdapterin diesem Beispiel verwenden. Vorbereiten der 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>

Also behalte ich hier nur das Wichtige:

  • SomeViewModelist mein ViewModelich benutze für die Datenbindung. Sie können auch eine Klasse verwenden, die erweitert BaseObservableund verwendet wird @Bindable. Das BindingAdapterin diesem Beispiel muss jedoch nicht in einer ViewModeloder BaseObservableKlasse sein! Eine einfache Klasse wird es tun! Dies wird später veranschaulicht.
  • app:appIconDrawable="@{model.packageName}". Ja ... das hat mir wirklich Kopfschmerzen bereitet! Lassen Sie es uns zusammenfassen:
    • app:appIconDrawable: Das kann alles sein : app:iCanBeAnything! Ja wirklich. Sie können auch behalten "android:src"! Notieren Sie sich jedoch Ihre Wahl, wir werden sie später verwenden!
    • "@ {model.packageName}": Wenn Sie mit Datenbindung gearbeitet haben , ist dies bekannt. Ich werde später zeigen, wie dies verwendet wird.

Nehmen wir an, wir verwenden diese einfache Observable-Klasse:

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

Wie versprochen können Sie die public static void setImageViewDrawable()Klasse auch in eine andere Klasse verschieben, z. B. können Sie eine Klasse mit einer Sammlung von BindingAdapters:

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

Eine weitere wichtige Bemerkung ist, dass ich in meiner ObservableKlasse String packageNamezusätzliche Informationen an die weitergegeben habe setImageViewDrawable. Sie können beispielsweise int resourceIdauch mit den entsprechenden Gettern / Setzern auswählen , für die der Adapter wird:

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

Diese Arbeit für mich. Ich hätte es als Kommentar zu @hqzxzwb Antwort hinzugefügt, aber aufgrund von Reputationsbeschränkungen.

Ich habe dies aus meiner Sicht Modell

var passport = R.drawable.passport

Dann habe ich in meiner XML

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

Und das ist es


1

Verwenden von Fresco (Facebook-Bildbibliothek)

 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

In Ihrem Ansichtsstatus oder Ihrer Ansichtsmodellklasse;

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

In Ihrem 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

Bild wie folgt einstellen,

  <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" />
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.