Android'de veri bağlamayı kullanarak ImageView için android: src'de çekilebilir kaynak kimliğini ayarlayın


91

Veri bağlamayı kullanarak ImageView'in android: src'sine çekilebilir kaynak kimliğini ayarlamaya çalışıyorum

İşte benim amacım:

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

    // ...
}

İşte düzenim:

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

Ve son olarak, aktivite sınıfı:

// ...

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

    // ...

}

Görüntüyü hiç göstermiyor. Neyi yanlış yapıyorum?

BTW, standart yolla mükemmel çalışıyordu:

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

}

Yanıtlar:


112

10 Kasım 2016 itibarıyla yanıt

Splash'in aşağıdaki yorumu, özel bir özellik türü (gibi imageResource) kullanmanın gerekli olmadığını vurguladı, bunun yerine bunun için birden fazla yöntem oluşturabiliriz 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);
    }
}

Eski Cevap

Her zaman bir adaptör kullanmayı deneyebilirsiniz:

public class DataBindingAdapters {

    @BindingAdapter("imageResource")
    public static void setImageResource(ImageView imageView, int resource){
        imageView.setImageResource(resource);
    }
}

Daha sonra bağdaştırıcıyı xml'nizde şu şekilde kullanabilirsiniz

<ImageView
    android:id="@+id/recipe_image_view"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:scaleType="centerCrop"
    imageResource="@{recipe.imageResource}" />

Xml içindeki adın BindingAdapter ek açıklamasıyla (imageResource) eşleştiğinden emin olun.

DataBindingAdapters sınıfının özellikle herhangi bir yerde bildirilmesine gerek yoktur, DataBinding mekaniği onu ne olursa olsun bulacaktır (inanıyorum)


Kullanarak çalışır @BindingAdapter. Ama, değer olmalıdır android:src, değil imageResource: @BindingAdapter("android:src"). Teşekkür ederim!
Yuriy Seredyuk

5
@YuriySeredyuk Nooooo! haha lütfen. Bunu yapmak, uygulamanızın tamamında xml içinde "android: src" nin her bir kullanımını geçersiz kılar ve bunu kesinlikle yapmak istemezsiniz. İmageResource'u çalıştırmak için, imageView için xml'yi yukarıda yaptığım gibi değiştirmeniz gerekir, veri bağlama oraya ne koyacağımı belirleyecektir
Joe Maher

1
Tamam, fikri anladım. Şimdi <ImageView imageResource="@{recipe.imageResource}" />ve kullanılıyor @BindingAdapter("imageResource"). Az imageResource="@{recipe.imageResource}"önce kodunuzdan alınan kısmı kaçırdım :)
Yuriy Seredyuk

1
Bunun olması gerekmiyor app:imageResourcemu?
NameSpace

1
"Bunu yapmak, uygulamanızın tamamında xml içinde" android: src "nin her bir kullanımını geçersiz kılacaktır" Veri bağlama, bu özniteliği yalnızca ImageView'a uygulamak için yeterince akıllı değil, çünkü işlevde tanımlanan şey bu mu? Sanırım "android: src" tercih edilir .... ImageView bağlama bağdaştırıcıları için Android'in kendisinin ne yaptığını düşünün: android.googlesource.com/platform/frameworks/data-binding/+/…
Splash

42

Hiç bir âdete gerek yok BindingAdapter. Sadece kullan

app:imageResource="@{yourResId}"

ve iyi çalışacak.

Kontrol bu nasıl çalıştığını için.


2
2020 dolaylarında harika bir cevap olduğu için bunun daha fazla olumlu oyu olmalı
JohnnyLambada

kesinlikle, en iyi ve en basit cevap
Luckyhandler

Bu, 2020'nin sonu itibariyle en iyi ve en uygun cevap gibi görünüyor
mcy

ImageViewSınıfa bir göz atıyorum ve setImageResourceyöntemi takip ediyorum , sonunda çözüldü resolveUrive sıfır değilse bile var. Bu işe Intyarardı, acaba ne olurdu Int?? Bağlamalar yürütüldüğünde, örneğin başka bir şey executePendingBindingsçağrılırsa null yapılamazsa varsayılan olarak sıfır, nullable'lar null olarak gerçekleştirilir.
cutiko

25

tanımlamak:

@BindingAdapter({"android:src"})
public static void setImageViewResource(ImageView imageView, int resource) {
    imageView.setImageResource(resource);
}

kullanım:

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

bu yöntemi nereye ekleyebilirim
myatmins

support herhangi bir sınıfa ekleyin, belki bir ImageDataBindingAdapter.class oluşturabilirsiniz.
qinmiao

12

Kendinizinkini oluştururken standart SDK özniteliklerini asla geçersiz kılmayın @BindingAdapter!

Bu, şu gibi birçok nedenden dolayı iyi bir yaklaşım değildir: Android SDK güncellemesinde bu öznitelikte yeni düzeltmelerin faydalarının elde edilmesini engelleyecektir. Ayrıca geliştiricilerin kafasını karıştırabilir ve yeniden kullanılabilirlik açısından kesinlikle yanıltıcı olabilir (çünkü geçersiz kılınması beklenmemektedir)

aşağıdaki gibi farklı ad alanları kullanabilirsiniz:

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

veya

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

------ Güncellemeyi Başlat 2.Temmuz 2018

Ad alanının kullanılması tavsiye edilmez, bu nedenle aşağıdaki gibi ön ek veya farklı adlara güvenmek daha iyidir:

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

veya

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

------ Güncelleme 2.Temmuz 2018

Bununla birlikte, şu şekilde farklı bir çözüm öneririm:

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

bağlam görünümü her zaman bağlama ifadesi içinde kullanılabilir @{ ... }


1
Bence xml içindeki koddan olabildiğince kaçınılmalıdır - test edilemez, yığılabilir ve açık değildir. Ancak, standart özelliklerin aşırı yüklenmesinin kafa karıştırıcı olabileceğine katılıyorum. Bence en iyi yol, yeni özniteliği farklı bir şekilde adlandırmaktır, bu durumda "srcResId", ancak yine de bir BindingAdapter
Kirill Starostin

7

Maher Abuthraa'nın cevabına dayanarak, XML'de bunu kullanmaya başladım:

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

contextDeğişken kullanılabilir herhangi bir ithalat olmadan ifade bağlama. Ayrıca, özel bir şey BindingAdaptergerekmez. Yalnızca uyarı: yöntem getDrawableyalnızca API 21'den beri mevcuttur.


6

İçin Kotlin bir üst düzey utils dosyaya koymak, hiçbir statik / tamamlayıcı bağlam gerekli:

@BindingAdapter("android:src")
fun setImageViewResource(view: ImageView, resId : Int) {
    view.setImageResource(resId)
}

5

İle daha fazlasını yapabilirsin DataBindingAdapter

Şu türlerden herhangi birini ayarlayın:

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

Hata resmini / Yer tutucu resmini ayarla

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

Tüm türleri test etti

SC

Böylelikle tek ciltleme adaptörü ile mümkün olur. Sadece bu yöntem projesini kopyalayın.

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

Tüm nesneleri yüklemek için Glide kullanmamın nedeni

Bana neden çekilebilir / kaynak kimliğini yüklemek için Glide'ı kullandığımı sorarsanız, bunun yerine imageView.setImageBitmap();veya imageView.setImageResource();. Yani sebep şu ki

  • Glide, ortam kod çözme, bellek ve disk önbelleğe almayı tamamlayan verimli bir görüntü yükleme çerçevesidir. Bu nedenle, büyük boyutlu görüntüler ve önbellek konusunda endişelenmenize gerek yok.
  • Görüntü yüklerken tutarlılık sağlamak için. Artık her tür görüntü kaynağı Glide tarafından yüklenmektedir.

Piccaso, Fresso veya başka herhangi bir resim yükleme kitaplığı kullanıyorsanız, loadImageWithGlideyöntemde değişiklik yapabilirsiniz .


`errorImage =" @ {@ drawable / ic_launcher} "". Bu şey benim için
Vivek Mishra

@VivekMishra Belki ic_launcher'ınız mipmap'te mi ?, deneyin @ mipmap / ic_launcher.
Khemraj

@VivekMishra İlgili hata günlüğünüzü yapıştırabilir misiniz? Bu yöntemi bağlama kullanım sınıfınıza eklediniz mi?
Khemraj

**** / veri bağlama hatası **** msg: com.zuowei.circleimageview.CircleImageView üzerinde java.lang.String değer türüne sahip 'app: src' özniteliği için alıcı bulunamıyor. Hem android hem de uygulama ad alanıyla denedim ve ikisi de benim için çalışmadı. Parametrede varsayılan görüntü görünümünü circleImageView ile değiştirdim
Vivek Mishra

Ayrıca ayrı bir sınıfta bağlayıcı Adaptör oluşturdum
Vivek Mishra

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

3

aşağıdakileri yapabilirsin

android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"

2

Android konusunda uzman değilim ama mevcut çözümleri deşifre etmek için saatler harcadım. İşin iyi yanı, veri bağlama fikrinin tamamını kullanarakBindingAdapter biraz daha iyi kavradım. Bunun için en azından mevcut cevaplar için müteşekkirim (her ne kadar tamamlanmamış olsa da). İşte yaklaşımın tam bir dökümü:

BindingAdapterBu örnekte de kullanacağım . Hazırlanıyor 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>

Yani burada sadece önemli şeyleri saklıyorum:

  • SomeViewModelBenim ise ViewModelbağlama veri için ben kullanım. Ayrıca genişleyen BaseObservableve kullanan bir sınıf da kullanabilirsiniz @Bindable. Ancak, BindingAdapterbu örnekteki bir veya sınıfta olmak zorunda değil ! Sade bir sınıf yeter! Bu daha sonra açıklanacaktır.ViewModelBaseObservable
  • app:appIconDrawable="@{model.packageName}". Evet ... bu gerçekten başımı ağrıtıyordu! Hadi parçalayalım:
    • app:appIconDrawableBu herhangi bir şey olabilir: app:iCanBeAnything! Gerçekten mi. Ayrıca tutabilirsiniz"android:src" ! Ancak seçiminize bir not alın, daha sonra kullanacağız!
    • "@ {model.packageName}": Veri bağlamayla çalıştıysanız , bu tanıdık geliyor. Bunun nasıl kullanıldığını daha sonra göstereceğim.

Bu basit Observable sınıfını kullandığımızı varsayalım:

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

Söz verdiğiniz gibi, public static void setImageViewDrawable()başka bir sınıfa da taşıyabilirsiniz , örneğin aşağıdaki koleksiyonlara sahip bir sınıfa sahip olabilirsiniz BindingAdapters:

public class BindingAdapterCollection {
   @BindingAdapter({"appIconDrawable"})
   public static void setImageViewDrawable(ImageView imageView, String packageName) {
       imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName));
   }
}

Bir diğer önemli sözler benim de olmasıdır Observablesınıfın kullandığım String packageNameiçin ekstra bilgi geçmek setImageViewDrawable. Örneğin int resourceId, adaptörün şu hale geldiği ilgili alıcılar / ayarlayıcılar ile de seçim yapabilirsiniz :

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

Bu benim için çalışıyor. @ hqzxzwb cevabına yorum olarak eklerdim, ancak itibar sınırlamaları nedeniyle.

Bu benim görüşümde var Model

var passport = R.drawable.passport

Sonra xml'mde var

android:src="@{context.getDrawable(model.passort)}"

Ve bu kadar


Tamam ama bağlamı içe aktarmanız gerektiğini söylemeyi unuttunuz. Cevabınızı günceller misiniz?
deadfish

1

Fresco (facebook resim kitaplığı) kullanma

 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

Görünüm durumunuzda veya model sınıfınızda;

 fun getSource(context: Context): Drawable? {
        return ContextCompat.getDrawable(context, R.drawable.your_source)
    }

XML'nizde;

<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

böyle bir resim ayarla,

  <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" />
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.