Androidのデータバインディングを使用して、ImageViewのandroid:srcに描画可能なリソース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);

}

回答:


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 2016年

5
@YuriySeredyuk Nooooo!ハハお願いします。あなたの全体のxml内:「SRCアンドロイド」のことを行うとのひとつひとつの使用よりも優先されます全体のアプリケーションあなたは間違いないでくださいやっているしたいの。imageResourceを機能させるには、上記のようにimageViewのxmlを変更する必要があります。データバインディングは、そこに何を配置するかを決定します
Joe Maher

1
はい、わかりました。現在とを使用<ImageView imageResource="@{recipe.imageResource}" />してい@BindingAdapter("imageResource")ます。imageResource="@{recipe.imageResource}"切り取ったコードの一部を見逃しました:)
Yuriy Seredyuk 2016年

1
これは必要ではありませんapp:imageResourceか?
名前空間2016

1
「これを行うと、アプリケーション全体でxml内の「android:src」のすべての使用がオーバーライドされます」データバインディングは、関数で定義されているものであるため、その属性をImageViewにのみ適用するほどスマートではありませんか?「android: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れた場合、nullableはデフォルトでゼロになり、nullableは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 2017年

サポートはそれを任意のクラスに追加します。おそらく、ImageDataBindingAdapter.classを作成できます。
qinmiao 2017年

12

独自の属性を作成するときは、標準のSDK属性を上書きしないでください@BindingAdapter

これは、次のような多くの理由から適切なアプローチではありません。その属性のAndroidSDKアップデートで新しい修正のメリットを得ることができなくなります。また、開発者を混乱させ、再利用性を確実にトリッキーにする可能性があります(オーバーライドされることが予期されていないため)

次のような別の名前空間を使用できます。

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

または

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

------アップデート2を開始します。2018年7月2日

名前空間の使用はお勧めしません。そのため、次のようにプレフィックスまたは別の名前を使用することをお勧めします。

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

または

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

------アップデート2.Jul.2018を終了します

ただし、次のような別のソリューションをお勧めします。

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

コンテキストビューは、バインディング式内で常に使用できます @{ ... }


1
xml内のコードは可能な限り避けるべきだと思います-それはテスト可能ではなく、積み重なる可能性があり、明白ではありません。しかし、私は、標準属性のオーバーロードが混乱を招く可能性があることに同意します。私は最善の方法は、「srcResId」この場合には、違った新しい属性に名前を付けることだと思いますが、それでもBindingAdapter使用
キリルStarostin

7

Maher Abuthraaからの回答に基づいて、これは私がXMLで使用することになったものです。

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

context変数が利用可能である任意の輸入せずに式を結合して。また、カスタムはBindingAdapter必要ありません。警告のみ:このメソッドgetDrawableはAPI21以降でのみ使用できます。


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を使用してdrawable / resource idをロードしたのかと聞かれたら、代わりにimageView.setImageBitmap();またはを使用できますimageView.setImageResource();。だから理由は

  • Glideは、メディアのデコード、メモリ、ディスクのキャッシュをラップする効率的な画像読み込みフレームワークです。したがって、大きなサイズの画像やキャッシュについて心配する必要はありません。
  • 画像の読み込み中に一貫性を保つため。これで、すべてのタイプの画像リソースがGlideによって読み込まれます。

Piccaso、Fresso、またはその他の画像読み込みライブラリを使用している場合は、loadImageWithGlideメソッドを変更できます


`errorImage =" @ {@ drawable / ic_launcher} "`。このことは私のためにコンパイルすらしません
Vivek Mishra

@VivekMishraおそらくあなたのic_launcherはmipmapにありますか?、@ mipmap / ic_launcherを試してください。
ケムラジ

@VivekMishra関連するエラーログを貼り付けることができますか?このメソッドをバインディングutilクラスに追加しましたか?
Khemraj 2018

**** /データバインディングエラー**** msg:com.zuowei.circleimageview.CircleImageViewで値タイプjava.lang.Stringの属性 'app:src'のゲッターが見つかりません。Androidとアプリの名前空間の両方で試しましたが、どちらも機能しませんでした。また、パラメーターのデフォルトのimageviewをcircleImageViewに置き換えました
Vivek Mishra

また、別のクラスでバインディングアダプターを作成しました
Vivek Mishra 2018

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>

だからここで私は重要なものだけを保持しています:

  • SomeViewModelViewModelはデータバインディングに使用しています。を拡張して使用するクラスを使用することもできBaseObservableます@Bindable。ただし、BindingAdapterこの例でViewModelまたはBaseObservableクラスに属している必要はありません。普通のクラスで十分です!これについては後で説明します。
  • 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));
   }
}

もう1つの重要な注意点は、私のObservableクラスでは、にString packageName追加情報を渡すために使用したことsetImageViewDrawableです。たとえばint resourceId、対応するゲッター/セッターを使用して、アダプターが次のようになるように選択することもできます。

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

以上です


OKですが、コンテキストをインポートする必要があるという言及を忘れています。あなたの答えを更新してもらえますか?
デッドフィッシュ

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

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" />
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.