web-dev-qa-db-de.com

Legen Sie die Zeichnungsressourcen-ID in Android fest: src für ImageView mit Datenbindung in Android

Ich versuche, eine zeichnungsfähige Ressourcen-ID für Android festzulegen: src von ImageView mithilfe von Datenbindung

Hier ist mein Gegenstand:

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 die 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 zeigt überhaupt kein Bild an. Was mache ich falsch?

Übrigens, es hat perfekt mit der Standardmethode gearbeitet:

@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

Antwort vom 10.11.2016

Der folgende Kommentar von Splash hat gezeigt, dass es nicht erforderlich ist, einen benutzerdefinierten Eigenschaftstyp (wie imageResource) zu verwenden. Stattdessen können wir mehrere Methoden für Android:src wie folgt erstellen:

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 immer versuchen, einen Adapter zu verwenden:

public class DataBindingAdapters {

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

Sie können den Adapter dann in Ihrer XML-Datei verwenden

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

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

Die DataBindingAdapters-Klasse muss an keiner bestimmten Stelle deklariert werden. Die DataBinding-Mechanik findet sie (egal wie ich glaube).

72
Joe Maher

definieren:

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

benutzen:

<ImageView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_centerInParent="true"
    Android:scaleType="center"
    Android:src="@{viewModel.imageRes, [email protected]/guide_1}"/>
19
qinmiao

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

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

sie können verschiedene Namespaces verwenden:

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

oder

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

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

Es wird nicht empfohlen, einen Namespace zu verwenden. Setzen Sie sich daher lieber auf 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)}"

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

7
Maher Abuthraa

Für Kotlin setze dies in eine Utils-Datei der obersten Ebene, kein statischer/begleitender Kontext erforderlich:

@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

Je mehr Sie mit DataBindingAdapter machen können

Legen Sie einen dieser Typen fest:

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 setzen/Platzhalterbild

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

Das wird mit einem einzigen Bindungsadapter 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 benutzt habe, um alle Objekte zu laden

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

  • Glide ist ein effizientes Rahmenprogramm zum Laden von Medien, das Datenträgerdekodierung, Speicher und Zwischenspeicherung von Festplatten einschließt. Sie müssen sich also nicht um große Bilder und Cache kümmern.
  • Konsistenz beim Laden des Bildes Nun werden alle Arten von Bildressourcen von Glide geladen.

Wenn Sie Piccaso, Fresso oder eine andere Bildladebibliothek verwenden, können Sie Änderungen an der loadImageWithGlide-Methode vornehmen.

2
Khemraj

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));
        }
    }
}
1
bong jae choe

Ich bin kein Experte für Android, aber ich habe Stunden damit verbracht, die vorhandenen Lösungen zu entschlüsseln. Das Gute ist, dass ich die Idee der Datenbindung mit BindingAdapter etwas besser verstanden habe. Dafür bin ich zumindest dankbar (wenn auch sehr unvollständig) für die vorhandenen Antworten. Hier eine vollständige Aufschlüsselung des Ansatzes:

Ich werde in diesem Beispiel auch die BindingAdapter verwenden. xml vorbereiten:

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

Hier bewahre ich nur das Wichtige auf:

  • SomeViewModel ist meine ViewModel, die ich zur Datenbindung verwende. Sie können auch eine Klasse verwenden, die BaseObservable erweitert und @Bindable verwendet. Die BindingAdapter in diesem Beispiel, muss jedoch nicht in einer ViewModel- oder BaseObservable-Klasse sein! Eine einfache Klasse reicht aus! Dies wird später erläutert.
  • app:appIconDrawable="@{model.packageName}". Ja ... das hat mir wirklich Kopfschmerzen bereitet! Lassen Sie uns es aufschlüsseln:
    • app:appIconDrawable: Dies kann alles sein: app:iCanBeAnything! Ja wirklich. Sie können auch "Android:src" behalten! 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 das genutzt wird.

Nehmen wir an, wir verwenden diese einfache Klasse "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));
   }
}

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

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 Observable-Klasse String packageName verwendet habe, um zusätzliche Informationen an die setImageViewDrawable zu übergeben. Sie können beispielsweise auch int resourceId mit den entsprechenden Getter/Setter wählen, für die der Adapter gilt:

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

sie können Folgendes tun

Android:src="@{[email protected]/ic_collapse:@drawable/ic_expand}"
0
FAHD Alotaibi