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

117
我正在尝试使用数据绑定将可绘制资源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);

}
18个回答

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

1
解决方案的关键是我们需要将类型设置为type="android.graphics.drawable.Drawable" 下面是解释:
假设我们有两个布局文件first_layout.xmlsecond_layout.xml,我们将从第一个布局传递drawable到第二个布局。
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>

您可以这样使用这些数据:
    <ImageView
        android:id="@+id/left_icon"
        android:layout_width="@dimen/_25"
        android:layout_height="@dimen/_25"
        android:src="@{myCustomImage}"/>

0
在你的视图状态或视图模型类中;
 fun getSource(context: Context): Drawable? {
        return ContextCompat.getDrawable(context, R.drawable.your_source)
    }

在您的XML中;
<androidx.appcompat.widget.AppCompatImageButton
   .
   .
   .
   android:src="@{viewState.getSource(context)}"

什么编程语言?Kotlin? - undefined

0
使用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));
        }
    }
}

-1

还有一个使用@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) {
}

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

-1

像这样设置图像:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接