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

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

我的目标是:

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


}
95851 次浏览

截至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"})
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}"/>

使用 Fresco (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));
}
}
}
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}"/>

对于 科特林,把它放到一个顶级的 utils 文件中,不需要静态/伴随上下文:

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

Never override standard SDK attributes when you create your own @BindingAdapter!

这不是一个好的方法,原因有很多,比如: 它将阻止获得对该属性的 Android SDK 更新的新补丁的好处。此外,它可能会使开发人员感到困惑,并且肯定会对可重用性造成麻烦(因为它不会被覆盖)

您可以使用不同的名称空间,如:

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

or

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

——开始更新2018年7月2日

不建议使用命名空间,因此最好使用前缀或不同的名称:

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

或者

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

——更新2.2018年7月

不过,我建议采取以下不同的解决方案:

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

在绑定表达式 @{ ... }中始终可以使用上下文视图

你能做的就越多

  • 您可以通过数据绑定设置 Image Url档案Bitmap字节数组可以画可绘制的身份证任何内容。
  • 也可以用 将多个参数传递给绑定适配器设置错误图像/占位符图像。

设置以下类型:

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

还有地图资源

android:src="@{@mipmap/ic_launcher}" <!--This will show Token recognition error at '@mipmap -->




android:src="@{R.mipmap.ic_launcher}" <!-- correct with improt R class -->

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

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 加载可绘制/资源 id,我可以使用 imageView.setImageBitmap();imageView.setImageResource();。原因就是

  • Glide is an efficient image loading framework that wraps media decoding, memory and disk caching. So you need not to worry about large size images and cache.
  • 为了在加载图像时保持一致性,现在所有类型的图像资源都由 Glide 加载。

If you use Piccaso, Fresso or any other image loading library, you can make changes in loadImageWithGlide method.

我不是 Android 方面的专家,但我花了几个小时试图破译现有的解决方案。好的方面是,我更好地理解了使用 BindingAdapter进行数据绑定的整体思想。对此,我至少要感谢现有的答案(尽管非常不完整)。下面是这种方法的完整分类:

在这个例子中,我还将使用 BindingAdapter:

<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:iCanBeAnything!真的。你也可以保留 "android:src"!不过,请注意您的选择,我们将使用它以后!
    • “@{ model. packageName }”: 如果您使用的是 数据绑定,那么这是很熟悉的。稍后我将展示如何使用它。

假设我们使用这个简单的可观察类:

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 packageNamesetImageViewDrawable传递额外的信息。您还可以选择例如 int resourceId,以及相应的 getters/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);
}
}

您可以执行以下操作

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

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

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

在你的 XML 中;

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

基于 Maher Abuthraa 的答案,我最后在 XML 中使用了以下内容:

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

context变量 是可用的在绑定表达式中没有任何导入。此外,没有自定义 BindingAdapter的必要。只需注意: 方法 getDrawable只能在 API 21之后使用。

根本不需要自定义 BindingAdapter。 用就是了

app:imageResource="@{yourResId}"

一切都会好起来的。

Check 这个 for how it works.

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

这个工作为我。我会把它添加到@hqzxzwb 回答作为评论,但由于声誉限制。

I have this in my view Model

var passport = R.drawable.passport

然后在 xml 中,我有

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

And thats it

set image like this,

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

根本不需要自定义 BindingAdapter。

data:

<data>
<import type="com.example.R"/>
:
</data>

图片视图:

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:imageResource="@{gender == 0 ? R.drawable.male : R.drawable.female}" />

Key point of solution is we need to give the type as type="android.graphics.drawable.Drawable"

解释如下

让我们说,我们有2个布局 first_layout.xmlsecond_layout.xml,我们将发送从第一到第二绘图。

first_layout.xml

<include
android:id="@+id/home_last_trip"
layout="@layout/second_layout.xml"
app:myCustomImage="@{someCondition == 1 ? @drawable/your_image_1 :@drawable/your_image_1 }"/>

second_layout.xml

<data>
<variable
name="myCustomImage"
type="android.graphics.drawable.Drawable" />
</data>

You can use this data like this:

    <ImageView
android:id="@+id/left_icon"
android:layout_width="@dimen/_25"
android:layout_height="@dimen/_25"
android:src="@{myCustomImage}"/>

再举一个使用 @IdRes@BindingAdapter的例子

<?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>
<import type="hello.R" />
</data>


<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:show_image="@{R.drawable.image}" />
</layout>
@BindingAdapter("show_image")
public static void loadImage(ImageView view, @IdRes int imageId) {
}