Próbuję ustawić identyfikator zasobu do rysowania na android: src z ImageView przy użyciu powiązania danych
Oto mój obiekt:
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;
}
// ...
}
Oto mój układ:
<?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>
I wreszcie klasa aktywności:
// ...
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);
}
// ...
}
W ogóle nie wyświetla obrazu. Co ja robię źle?
Swoją drogą, działał doskonale w standardowy sposób:
@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);
}
źródło
@BindingAdapter
. Ale wartość powinna byćandroid:src
nieimageResource
:@BindingAdapter("android:src")
. Dziękuję Ci!<ImageView imageResource="@{recipe.imageResource}" />
i@BindingAdapter("imageResource")
. Właśnie przegapiłemimageResource="@{recipe.imageResource}"
fragment twojego kodu :)app:imageResource
?W ogóle nie jest potrzebny zwyczaj
BindingAdapter
. Po prostu użyjapp:imageResource="@{yourResId}"
i będzie działać dobrze.
Sprawdzić to na jak to działa.
źródło
ImageView
klasę i podążam zasetImageResource
metodą, wydaje się, że w końcu jest rozwiązanyresolveUri
i jest, jeśli nie zero, walidacja. Więc to zadziałałobyInt
, zastanawiam się, co by się stało, gdybyInt?
. Gdy powiązania są wykonywane, na przykład, jeśli wywoła coś innego,executePendingBindings
wówczas nie dopuszcza wartości null, domyślnie przyjmuje wartość zero, wartości null.definiować:
@BindingAdapter({"android:src"}) public static void setImageViewResource(ImageView imageView, int resource) { imageView.setImageResource(resource); }
posługiwać się:
<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}"/>
źródło
Nigdy nie zastępuj standardowych atrybutów SDK podczas tworzenia własnych
@BindingAdapter
!To nie jest dobre podejście z wielu powodów, takich jak: zapobiegnie uzyskaniu korzyści z nowych poprawek aktualizacji Android SDK dla tego atrybutu. Może to również dezorientować programistów i na pewno trudne do ponownego wykorzystania (ponieważ nie oczekuje się nadpisania)
możesz użyć innej przestrzeni nazw, na przykład:
custom:src="@{recipe.imageResource}"
lub
mybind:src="@{recipe.imageResource}"
------ Rozpocznij aktualizację 2 lipca 2018 r
Nie zaleca się używania przestrzeni nazw, więc lepiej polegać na prefiksie lub innej nazwie, na przykład:
app:custom_src="@{recipe.imageResource}"
lub
app:customSrc="@{recipe.imageResource}"
------ koniec Aktualizacja 2. lipca 2018
Poleciłbym jednak inne rozwiązanie, ponieważ:
android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"
widok kontekstu jest zawsze dostępny wewnątrz wyrażenia powiązania
@{ ... }
źródło
Opierając się na odpowiedzi Mahera Abuthraa, oto, czego ostatecznie użyłem w XML:
android:src="@{context.getDrawable(recipe.imageResource)}"
context
Zmienna jest dostępna w ekspresji wiązania bez importu. Nie jest teżBindingAdapter
potrzebny żaden zwyczaj . Jedyne zastrzeżenie: metodagetDrawable
jest dostępna dopiero od API 21.źródło
Na Kotlin umieścić to na najwyższym poziomie pliku utils, brak kontekstu static / towarzysz potrzebne:
@BindingAdapter("android:src") fun setImageViewResource(view: ImageView, resId : Int) { view.setImageResource(resId) }
źródło
Im więcej możesz z tym zrobić
DataBindingAdapter
Ustaw dowolny z tych typów:
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`}"
Ustaw obraz błędu / obraz zastępczy
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`}" />
Przetestowano wszystkie typy
Staje się to możliwe dzięki pojedynczemu adapterowi do wiązania. Po prostu skopiuj ten projekt metody.
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); } }
Powód, dla którego użyłem Glide do załadowania wszystkich obiektów
Jeśli zapytasz mnie, dlaczego użyłem Glide do załadowania identyfikatora do rysowania / zasobu, zamiast tego mogę użyć
imageView.setImageBitmap();
lubimageView.setImageResource();
. Więc powód jest takiJeśli używasz Piccaso, Fresso lub jakiejkolwiek innej biblioteki do ładowania obrazów, możesz wprowadzić zmiany w
loadImageWithGlide
metodzie .źródło
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}"/>
źródło
możesz wykonać następujące czynności
android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
źródło
Nie jestem ekspertem od Androida, ale godzinami próbowałem rozszyfrować istniejące rozwiązania. Dobrą rzeczą jest to, że
BindingAdapter
nieco lepiej uchwyciłem cały pomysł wiązania danych . Za to jestem przynajmniej wdzięczny za istniejące odpowiedzi (choć bardzo niekompletne). Oto kompletny podział podejścia:Będę również użyć
BindingAdapter
w tym przykładzie. Przygotowaniexml
:<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>
Więc tutaj zachowuję tylko ważne rzeczy:
SomeViewModel
jest moimViewModel
używanym do wiązania danych. Możesz także użyć klasy, która rozszerzaBaseObservable
i używać@Bindable
. JednakBindingAdapter
w tym przykładzie nie musi być w klasieViewModel
aniBaseObservable
! Zwykła klasa wystarczy! Zostanie to zilustrowane później.app:appIconDrawable="@{model.packageName}"
. Tak ... to naprawdę przyprawiało mnie o bóle głowy! Rozbijmy to:app:appIconDrawable
: To może być wszystkoapp:iCanBeAnything
:! Naprawdę. Możesz też zatrzymać"android:src"
! Zwróć jednak uwagę na swój wybór, użyjemy go później!Załóżmy, że używamy tej prostej klasy 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)); } }
Zgodnie z obietnicą możesz również przenieść
public static void setImageViewDrawable()
, do innej klasy, np. Możesz mieć klasę, która ma kolekcjęBindingAdapters
:public class BindingAdapterCollection { @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } }
Kolejną ważną uwagą jest to, że w mojej
Observable
klasieString packageName
przekazywałem dodatkowe informacje dosetImageViewDrawable
. Możesz też wybrać np.int resourceId
Z odpowiednimi pobierającymi / ustawiającymi parametrami, dla których adapter staje się: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); } }
źródło
Ta praca dla mnie. dodałbym to do odpowiedzi @hqzxzwb jako komentarz, ale z powodu ograniczeń reputacji.
Mam to w widoku Model
var passport = R.drawable.passport
Następnie w moim xml mam
android:src="@{context.getDrawable(model.passort)}"
I to wszystko
źródło
Korzystanie z Fresco (biblioteka obrazów na Facebooku)
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)); } } }
źródło
W Twoim stanie widoku lub widoku klasy modelu;
fun getSource(context: Context): Drawable? { return ContextCompat.getDrawable(context, R.drawable.your_source) }
W swoim XML;
<androidx.appcompat.widget.AppCompatImageButton . . . android:src="@{viewState.getSource(context)}"
źródło
<?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>
źródło
ustaw obraz w ten sposób,
<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" />
źródło