Подтвердить что ты не робот

Установите идентификатор ресурса в android: src для ImageView с использованием привязки данных в Android

Я пытаюсь установить идентификатор ресурса ресурса для android: src ImageView с использованием привязки данных

Вот мой объект:

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

}
4b9b3361

Ответ 1

Ответа на этот вопрос от 10 ноября 2016

Комментарий 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 найдет его неважно (я полагаю)

Ответ 2

определить:

@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, [email protected]/guide_1}"/>

Ответ 3

Никогда не переопределяйте стандартные атрибуты SDK, когда создаете свой собственный @BindingAdapter !

Это не очень хороший подход по многим причинам, таким как: он предотвратит получение преимуществ от новых исправлений в обновлении Android SDK для этого атрибута. Кроме того, это может сбить с толку разработчиков и, конечно, сложно использовать повторно (потому что его нельзя перезаписать).

Вы можете использовать другое пространство имен, например:

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

или же

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

------ начать обновление 2.июл .198

Пространство имен не рекомендуется использовать, поэтому лучше использовать префикс или другое имя, например:

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

или же

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

------ конец Обновление 2. июл .2018

Тем не менее, я бы порекомендовал другое решение, как:

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

контекстное представление всегда доступно внутри выражения привязки @{... }

Ответ 4

Для Kotlin поместите это в файл utils верхнего уровня, не требуется статический/вспомогательный контекст:

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

Ответ 5

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

Ответ 6

Чем больше вы можете сделать с 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 для загрузки всех объектов

Если вы спросите меня, почему я использовал Glide для загрузки drawable/идентификатора ресурса, я мог бы использовать imageView.setImageBitmap(); или imageView.setImageResource(); , Так что причина в том, что

  • Glide - это эффективный фреймворк для загрузки изображений, который охватывает декодирование медиа, память и кеширование диска. Так что вам не нужно беспокоиться о больших размерах изображений и кэше.
  • Для обеспечения согласованности при загрузке изображения. Теперь все типы ресурсов изображений загружаются Glide.

Если вы используете Piccaso, Fresso или любую другую библиотеку загрузки изображений, вы можете внести изменения в метод loadImageWithGlide.

Ответ 7

Вы можете сделать следующее

android:src="@{[email protected]/ic_collapse:@drawable/ic_expand}"

Ответ 8

Использование 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));
        }
    }
}

Ответ 9

Я не эксперт по 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"! Тем не менее, сделайте заметку на ваш выбор, мы будем использовать ее позже!
    • "@{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 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));
   }
}

Еще одно важное замечание: в моем классе 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);
   }
}

Ответ 10

В вашем состоянии просмотра или классе модели представления;

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

в вашем XML;

<androidx.appcompat.widget.AppCompatImageButton
   .
   .
   .
   android:src="@{viewState.getSource(context)}"