データバインディングを使用して、ImageViewのAndroid:srcに描画可能なリソースIDを設定しようとしています
これが私のオブジェクトです:
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);
}
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}"/>
独自の@BindingAdapter
を作成するときに、標準のSDK属性をオーバーライドしないでください!
これは、次のような多くの理由で適切なアプローチではありません。その属性のAndroid SDK更新の新しい修正の利点が得られなくなるからです。また、開発者を混乱させ、再利用性のために確かにトリッキーかもしれません(オーバーライドされることは予期されていないため)
次のような異なる名前空間を使用できます。
custom:src="@{recipe.imageResource}"
または
mybind:src="@{recipe.imageResource}"
------アップデート2を開始2018年7月
名前空間の使用は推奨されていません。したがって、次のようにプレフィックスまたは別の名前に依存することをお勧めします。
app:custom_src="@{recipe.imageResource}"
または
app:customSrc="@{recipe.imageResource}"
------アップデート2終了2018年7月
ただし、次のような別のソリューションをお勧めします。
Android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"
コンテキストビューは、バインディング式@{ ... }
内で常に使用可能です
Kotlinの場合、これをトップレベルのutilsファイルに配置します。静的/コンパニオンコンテキストは不要です。
@BindingAdapter("Android:src")
fun setImageViewResource(view: ImageView, resId : Int) {
view.setImageResource(resId)
}
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}"/>
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`}"
/>
そのため、単一のバインディングアダプタで可能になります。このメソッドプロジェクトをコピーするだけです。
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を使用してドロアブル/リソースIDをロードしたのかと聞かれたら、代わりにimageView.setImageBitmap();
またはimageView.setImageResource();
を使用できます。その理由は
Piccaso、Fresso、またはその他の画像読み込みライブラリを使用する場合、loadImageWithGlide
methodで変更を加えることができます。
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));
}
}
}
次のことができます
Android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
私は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
は、がViewModel
またはBaseObservable
クラスにある必要はありません !普通のクラスで十分です!これについては後で説明します。app:appIconDrawable="@{model.packageName}"
。はい...これは本当に頭痛の種でした!分解しましょう:app:appIconDrawable
:これは何でも構いません:app:iCanBeAnything
!本当に。 "Android:src"
を保持することもできます!ただし、選択したことに注意してください、後で使用します!この単純な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 aligned, that's it! :)
//
// The name of the fuction, 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);
}
}