使用Android中的数据绑定在android:src中为ImageView设置可绘制资源ID


91

我正在尝试使用数据绑定将可绘制资源ID设置为ImageView的android:src

这是我的对象:

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

    // ...
}

这是我的布局:

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

最后,活动课:

// ...

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

    // ...

}

它根本不显示图像。我究竟做错了什么?

顺便说一句,它与标准方式完美配合:

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

}

Answers:


112

截至2016年11月10日的答案

Splash在下面的评论中强调指出,不必使用自定义属性类型(如imageResource),我们可以这样创建多个方法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);
    }
}

旧答案

您总是可以尝试使用适配器:

public class DataBindingAdapters {

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

然后,您可以像这样使用xml中的适配器

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

确保注意xml中的名称与BindingAdapter批注(imageResource)匹配

不需要在任何地方特别声明DataBindingAdapters类,DataBinding机制可以找到它(我相信)


通过使用它可以工作@BindingAdapter。但是,价值应该是android:src,而不是imageResource@BindingAdapter("android:src")。谢谢!
Yuriy Seredyuk

5
@YuriySeredyuk Nooooo!哈哈 这样做将覆盖整个应用程序中xml中XML内对“ android:src”的每一次使用,您肯定希望这样做。要使imageResource正常工作,您必须像上面所做的那样更改imageView的xml,数据绑定将确定要放置的内容
Joe Maher

1
好,我明白了主意。现在使用<ImageView imageResource="@{recipe.imageResource}" />@BindingAdapter("imageResource")。我只是错过了imageResource="@{recipe.imageResource}"部分代码
而已

1
这不是必须的app:imageResource吗?
NameSpace

1
“这样做将覆盖整个应用程序中xml中对android:src的每次使用”,数据绑定不够聪明,仅将该属性应用于ImageView,因为这就是函数中定义的内容吗?我认为,“机器人:SRC”将是可取....考虑一下自身的Android能源部ImageView的结合适配器:android.googlesource.com/platform/frameworks/data-binding/+/...
飞溅

40

完全不需要自定义BindingAdapter。只需使用

app:imageResource="@{yourResId}"

它将正常工作。

检查如何工作。


2
这应该有更多的投票,因为这是大约2020
JohnnyLambada

肯定,最好和最简单的答案
luckyhandler

这看起来像是2020年末的最佳和最合适的答案
mcy

我正在看一看ImageView该类并遵循该setImageResource方法,似乎最终解决了该问题,resolveUri并且即使不是零验证也是如此。因此,这对于Int我不知道如果会发生会起作用Int?。例如,执行绑定时,如果其他调用executePendingBindings然后将不可为空的值默认为零,则将可为空的值默认为null。
Cutiko

25

定义:

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

使用:

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

我在哪里添加该方法
myatmins

支持将其添加到任何类中,也许您可​​以创建一个ImageDataBindingAdapter.class。
qinmiao

12

创建自己的属性时,切勿覆盖标准的SDK属性@BindingAdapter

这不是一个好方法,原因有很多,例如:它将阻止从该属性的Android SDK更新中获得新修复的好处。此外,它可能会使开发人员感到困惑,并且肯定会引起重用性的棘手(因为它是意外的被覆盖)

您可以使用其他名称空间,例如:

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

要么

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

------开始更新2.七月2018

不建议使用命名空间,因此最好依赖前缀或其他名称,例如:

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

要么

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

------结束更新2018年7月2日

但是,我建议使用以下不同的解决方案:

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

绑定表达式内部始终提供上下文视图 @{ ... }


1
我认为应尽可能避免xml中的代码-这是不可测试的,可能会堆积并且不明显。但是我同意重载标准属性可能会造成混淆。我认为最好的方法是用不同的方式命名新属性,在本例中为“ srcResId”,但仍使用BindingAdapter
Kirill Starostin

7

基于Maher Abuthraa的回答,这就是我最终在XML中使用的内容:

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

context变量在绑定表达式中使用,而无需任何导入。另外,也不BindingAdapter需要自定义。仅警告:此方法getDrawable仅在API 21之后可用。


6

对于Kotlin,将其放置在顶层utils文件中,不需要静态/伴随上下文:

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

5

您可以做的越多 DataBindingAdapter

设置以下任何一种类型:

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

设置错误图片/占位符图片

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

测试了所有类型

SC

这样就可以使用单绑定适配器。只需复制此方法项目。

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

我使用Glide加载所有对象的原因

如果您问我为什么我使用Glide加载drawable / resource id,我可以使用imageView.setImageBitmap();imageView.setImageResource();。所以原因是

  • Glide是一个有效的图像加载框架,它包装了媒体解码,内存和磁盘缓存。因此,您不必担心大型图像和缓存。
  • 在加载图像时保持一致。现在,所有类型的图像资源均由Glide加载。

如果您使用Piccaso,Fresso或任何其他图像加载库,则可以在loadImageWithGlidemethod中进行更改


`errorImage =“ @ {@ drawable / ic_launcher}”`。这个东西对我来说甚至都不编译
Vivek Mishra

@VivekMishra也许您的​​ic_launcher在mipmap中?,请尝试@ mipmap / ic_launcher。
Khemraj

@VivekMishra您可以粘贴相关的错误日志吗?您是否在绑定util类中添加了此方法?
Khemraj

**** /数据绑定错误**** msg:在com.zuowei.circleimageview.CircleImageView上找不到属性类型为java.lang.String的属性“ app:src”的吸气剂。我尝试使用android以及应用程序名称空间,但两者都对我不起作用。我还用参数中的circleImageView替换了默认的imageview
Vivek Mishra,

我还创建了一个单独的类中的绑定适配器
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}"/>


2

我不是Android方面的专家,但我花费了数小时试图破解现有解决方案。好消息是我BindingAdapter更好地掌握了数据绑定的整个想法。为此,我至少要感谢现有的答案(尽管严重不完整)。这里是该方法的完整分解:

BindingAdapter在本示例中,我还将使用。准备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>

所以这里我只保留重要的内容:

  • SomeViewModel是我ViewModel用于数据绑定的。您还可以使用扩展BaseObservable和使用的类@Bindable。但是,BindingAdapter在此示例中,不必位于ViewModelBaseObservable类中!普通班就可以!稍后将对此进行说明。
  • app:appIconDrawable="@{model.packageName}"。是的...这真的让我头疼!让我们分解一下:
    • app:appIconDrawable:这可以是任何东西:app:iCanBeAnything!真。您还可以保留"android:src"!但是,请注意您的选择,我们将在以后使用!
    • “ @ {model.packageName}”:如果您使用数据绑定,这是很熟悉的。我将在稍后展示如何使用它。

假设我们使用这个简单的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));
   }
}

如所承诺的,您也可以将移到public static void setImageViewDrawable()其他类,例如,也许您可​​以拥有一个包含以下集合的类BindingAdapters

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

另一个重要的说明是,在我的Observable课堂上,我曾经String packageName将额外的信息传递给setImageViewDrawable。例如int resourceId,您还可以选择,以及相应的getter / setter,适配器将变为:

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

这项工作对我来说。我会把它添加到@hqzxzwb答案中作为评论,但是由于声誉限制。

我认为这个模型

var passport = R.drawable.passport

然后在我的xml中,

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

就是这样


可以,但是您忘记提到必须导入上下文。您能更新您的答案吗?
死鱼

1

使用壁画(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

在您的视图状态或视图模型类中;

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

在您的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

这样设置图像

  <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" />
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.