[英]Set drawable resource ID in android:src for ImageView using data binding in Android
我正在嘗試使用數據綁定將可繪制資源 ID 設置為 android:src of ImageView
這是我的 object:
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>
最后,活動 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 類不需要在任何地方特別聲明,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}"
------ 2018 年 7 月 2 日開始更新
不建議使用命名空間,因此最好依賴前綴或不同的名稱:
app:custom_src="@{recipe.imageResource}"
或者
app:customSrc="@{recipe.imageResource}"
------ 2018 年 7 月 2 日更新結束
但是,我會推薦不同的解決方案:
android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"
上下文視圖在綁定表達式@{ ... }
始終可用
基於 Maher Abuthraa 的回答,這就是我最終在 XML 中使用的內容:
android:src="@{context.getDrawable(recipe.imageResource)}"
context
變量可在沒有任何導入的綁定表達式中使用。 此外,不需要自定義BindingAdapter
。 唯一警告:方法getDrawable
僅自 API 21 起可用。
###使用DataBindingAdapter
可以做的更多
###Set 任何這些類型:
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);
}
}
###Reason 我使用 Glide 加載所有對象
如果你問我為什么使用 Glide 來加載 drawable/resource id,我可以使用imageView.setImageBitmap();
或imageView.setImageResource();
. 所以原因是
如果您使用 Piccaso、Fresso 或任何其他圖像加載庫,您可以在loadImageWithGlide
方法中進行更改。
對於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}"/>
您可以執行以下操作
android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
根本不需要自定義 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}" />
我不是 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 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
集合的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);
}
}
這對我有用。 我會把它作為評論添加到@hqzxzwb 答案中,但由於聲譽限制。
我在我看來有這個模型
var passport = R.drawable.passport
然后在我的 xml 中,我有
android:src="@{context.getDrawable(model.passort)}"
就是這樣
使用 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));
}
}
}
在您的視圖狀態或視圖模型類中;
fun getSource(context: Context): Drawable? {
return ContextCompat.getDrawable(context, R.drawable.your_source)
}
在您的 XML 中;
<androidx.appcompat.widget.AppCompatImageButton
.
.
.
android:src="@{viewState.getSource(context)}"
<?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>
像這樣設置圖像,
<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" />
解決方案的關鍵點是我們需要將類型指定為type="android.graphics.drawable.Drawable"
解釋在下面
假設我們有 2 個布局first_layout.xml
和second_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>
您可以像這樣使用這些數據:
<ImageView
android:id="@+id/left_icon"
android:layout_width="@dimen/_25"
android:layout_height="@dimen/_25"
android:src="@{myCustomImage}"/>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.