Set drawable resource ID in android:src for ImageView using data binding in Android

后端 未结 15 2005
陌清茗
陌清茗 2020-12-04 09:27

I\'m trying to set drawable resource ID to android:src of ImageView using data binding

Here is my object:

public class Recipe implem         


        
相关标签:
15条回答
  • 2020-12-04 10:21

    I am not an expert in Android but I spent hours trying to decipher the existing solutions. The good thing is that I grasped the whole idea of data binding using BindingAdapter a bit better. For that, I am at least thankful for the existing answers (although heavily incomplete). Here a complete breakdown of the approach:

    I will also use the BindingAdapter in this example. Preparing the 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>
    

    So here I am keeping only the important stuff:

    • SomeViewModel is my ViewModel I use for data binding. You can also use a class that extends BaseObservable and use @Bindable. However, the BindingAdapter in this example, doesn't have to be in a ViewModel or BaseObservable class! A plain class will do! This will be illustrated later.
    • app:appIconDrawable="@{model.packageName}". Yes... this was really causing me headaches! Let's break it down:
      • app:appIconDrawable: This can be anything: app:iCanBeAnything! Really. You can also keep "android:src"! However, take a note on your choice, we will use it later!
      • "@{model.packageName}": If you worked with data binding, this is familiar. I'll show how this is used later.

    Let's assume we use this simple Observable class:

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

    As promised, you can also move the public static void setImageViewDrawable(), to some other class, e.g. maybe you can have a class that has a collection of BindingAdapters:

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

    Another important remark is that in my Observable class I used String packageName to pass extra info to the setImageViewDrawable. You can also choose for example int resourceId, with the corresponding getters/setters, for which the adapter becomes:

    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 讨论(0)
  • 2020-12-04 10:22

    Answer as of Nov 10 2016

    Splash's comment below has highlighted that it is not necessary to use a custom property type (like imageResource), we can instead create multiple methods for android:src like so:

    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

    You could always try to use an adapter:

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

    You can then use the adapter in your xml like so

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

    Be sure to notice that the name within the xml matches the BindingAdapter annotation (imageResource)

    The DataBindingAdapters class doesn't need to be declared anywhere in particular, the DataBinding mechanics will find it no matter (i believe)

    0 讨论(0)
  • 2020-12-04 10:22

    define:

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

    use:

    <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}"/>
    
    0 讨论(0)
提交回复
热议问题