在 Android 中使用数据绑定为 ImageView 在 android:src 中设置可绘制资源 ID
Set drawable resource ID in android:src for ImageView using data binding in Android
我正在尝试使用数据绑定将可绘制资源 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>
最后,activity class:
// ...
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);
}
截至 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 class 不需要在任何地方特别声明,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}"/>
对于 Kotlin 将其放入顶级 utils 文件,不需要静态/伴随上下文:
@BindingAdapter("android:src")
fun setImageViewResource(view: ImageView, resId : Int) {
view.setImageResource(resId)
}
在创建自己的 @BindingAdapter
!
时,切勿覆盖标准 SDK 属性
这不是一个好方法,原因有很多,例如:
它会阻止获得 Android 该属性的 SDK 更新的新修复的好处。此外,它可能会使开发人员感到困惑,并且在可重用性方面肯定很棘手(因为它不希望被覆盖)
您可以使用不同的命名空间,例如:
custom:src="@{recipe.imageResource}"
或
mybind:src="@{recipe.imageResource}"
------开始更新2.Jul.2018
不建议使用命名空间,因此最好依赖前缀或不同的名称:
app:custom_src="@{recipe.imageResource}"
或
app:customSrc="@{recipe.imageResource}"
------ 结束更新2.Jul.2018
但是,我会推荐不同的解决方案:
android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"
上下文视图在绑定表达式中始终可用 @{ ... }
###DataBindingAdapter
可以做的更多
- 你可以设置Image Url, File, Bitmap, Byte Array, Drawable, Drawable Id 任意数据绑定
- 您也可以使用 passing multiple parameters to binding adapter 设置错误图像/占位符图像。
###设置以下任何类型:
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`}"
以及 mipmap 资源
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`}"
/>
###测试了所有类型
因此,使用单一绑定适配器就可以做到这一点。直接复制这个方法工程即可。
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 是一个高效的图像加载框架,它封装了媒体解码、内存和磁盘缓存。因此您无需担心大尺寸图像和缓存。
- 加载图像时保持一致性。现在所有类型的图片资源都由Glide加载。
如果您使用 Piccaso、Fresso 或任何其他图像加载库,您可以在 loadImageWithGlide
方法中进行更改。
我不是 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
的 class 并使用 @Bindable
。但是,本例中的 BindingAdapter
, 不必 在 ViewModel
或 BaseObservable
class 中!一个普通的 class 就可以了!稍后会对此进行说明。
app:appIconDrawable="@{model.packageName}"
。是的……这真是让我很头疼!让我们分解一下:
app:appIconDrawable
:这可以是任何东西:app:iCanBeAnything
!真的。你也可以保持"android:src"
!但是,请注意您的选择,我们稍后会用到它!
- "@{model.packageName}":如果您使用过 data binding,这对您来说很熟悉。稍后我会展示它是如何使用的。
假设我们使用这个简单的 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));
}
}
正如承诺的那样,您还可以将 public static void setImageViewDrawable()
移动到其他 class,例如也许你可以有一个 class 集合 BindingAdapters
:
public class BindingAdapterCollection {
@BindingAdapter({"appIconDrawable"})
public static void setImageViewDrawable(ImageView imageView, String packageName) {
imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName));
}
}
另一个重要的评论是,在我的 Observable
class 中,我使用 String packageName
将额外信息传递给 setImageViewDrawable
。您还可以选择例如 int resourceId
,对应的 getters/setters,其适配器变为:
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}"
在您的视图状态或视图模型中class;
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
变量 is available 没有任何导入。此外,无需自定义 BindingAdapter
。唯一需要注意的是:方法 getDrawable
仅在 API 21.
之后可用
根本不需要自定义 BindingAdapter
。
只需使用
app:imageResource="@{yourResId}"
它会正常工作。
检查 this 它是如何工作的。
<?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 的回答中,但由于声誉限制。
我有这个模型
var passport = R.drawable.passport
然后在我的 xml 中,我有
android:src="@{context.getDrawable(model.passort)}"
就是这样
像这样设置图像,
<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>
<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}" />
我正在尝试使用数据绑定将可绘制资源 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>
最后,activity class:
// ...
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);
}
截至 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 class 不需要在任何地方特别声明,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}"/>
对于 Kotlin 将其放入顶级 utils 文件,不需要静态/伴随上下文:
@BindingAdapter("android:src")
fun setImageViewResource(view: ImageView, resId : Int) {
view.setImageResource(resId)
}
在创建自己的 @BindingAdapter
!
这不是一个好方法,原因有很多,例如: 它会阻止获得 Android 该属性的 SDK 更新的新修复的好处。此外,它可能会使开发人员感到困惑,并且在可重用性方面肯定很棘手(因为它不希望被覆盖)
您可以使用不同的命名空间,例如:
custom:src="@{recipe.imageResource}"
或
mybind:src="@{recipe.imageResource}"
------开始更新2.Jul.2018
不建议使用命名空间,因此最好依赖前缀或不同的名称:
app:custom_src="@{recipe.imageResource}"
或
app:customSrc="@{recipe.imageResource}"
------ 结束更新2.Jul.2018
但是,我会推荐不同的解决方案:
android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"
上下文视图在绑定表达式中始终可用 @{ ... }
###DataBindingAdapter
- 你可以设置Image Url, File, Bitmap, Byte Array, Drawable, Drawable Id 任意数据绑定
- 您也可以使用 passing multiple parameters to binding adapter 设置错误图像/占位符图像。
###设置以下任何类型:
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`}"
以及 mipmap 资源
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`}"
/>
###测试了所有类型
因此,使用单一绑定适配器就可以做到这一点。直接复制这个方法工程即可。
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 是一个高效的图像加载框架,它封装了媒体解码、内存和磁盘缓存。因此您无需担心大尺寸图像和缓存。
- 加载图像时保持一致性。现在所有类型的图片资源都由Glide加载。
如果您使用 Piccaso、Fresso 或任何其他图像加载库,您可以在 loadImageWithGlide
方法中进行更改。
我不是 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
的 class 并使用@Bindable
。但是,本例中的BindingAdapter
, 不必 在ViewModel
或BaseObservable
class 中!一个普通的 class 就可以了!稍后会对此进行说明。app:appIconDrawable="@{model.packageName}"
。是的……这真是让我很头疼!让我们分解一下:app:appIconDrawable
:这可以是任何东西:app:iCanBeAnything
!真的。你也可以保持"android:src"
!但是,请注意您的选择,我们稍后会用到它!- "@{model.packageName}":如果您使用过 data binding,这对您来说很熟悉。稍后我会展示它是如何使用的。
假设我们使用这个简单的 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));
}
}
正如承诺的那样,您还可以将 public static void setImageViewDrawable()
移动到其他 class,例如也许你可以有一个 class 集合 BindingAdapters
:
public class BindingAdapterCollection {
@BindingAdapter({"appIconDrawable"})
public static void setImageViewDrawable(ImageView imageView, String packageName) {
imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName));
}
}
另一个重要的评论是,在我的 Observable
class 中,我使用 String packageName
将额外信息传递给 setImageViewDrawable
。您还可以选择例如 int resourceId
,对应的 getters/setters,其适配器变为:
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}"
在您的视图状态或视图模型中class;
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
变量 is available 没有任何导入。此外,无需自定义 BindingAdapter
。唯一需要注意的是:方法 getDrawable
仅在 API 21.
根本不需要自定义 BindingAdapter
。
只需使用
app:imageResource="@{yourResId}"
它会正常工作。
检查 this 它是如何工作的。
<?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 的回答中,但由于声誉限制。
我有这个模型
var passport = R.drawable.passport
然后在我的 xml 中,我有
android:src="@{context.getDrawable(model.passort)}"
就是这样
像这样设置图像,
<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>
<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}" />