Установите идентификатор ресурса для рисования в 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);

}

person Yuriy Seredyuk    schedule 05.03.2016    source источник


Ответы (14)


Ответ от 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 найдет его независимо (я верю)

person Joe Maher    schedule 05.03.2016
comment
Он работает с использованием @BindingAdapter. Но значение должно быть android:src, а не imageResource: @BindingAdapter("android:src"). Благодарю вас! - person Yuriy Seredyuk; 05.03.2016
comment
@YuriySeredyuk Неееет! хаха пожалуйста. Это переопределит каждое отдельное использование android:src в xml во всем вашем всем приложении, что вы определенно НЕ хотите делать. Чтобы заставить imageResource работать, вам нужно изменить xml для imageView, как я сделал выше, привязка данных решит, что туда поместить. - person Joe Maher; 05.03.2016
comment
Хорошо, я понял идею. Теперь используем <ImageView imageResource="@{recipe.imageResource}" /> и @BindingAdapter("imageResource"). Я только что пропустил imageResource="@{recipe.imageResource}" часть вашего кода, вырезанного :) - person Yuriy Seredyuk; 05.03.2016
comment
Итак, эта строка здесь @BindingAdapter("imageResource") определяет новый ключ imageResource, который вы можете использовать в XML для ссылки на идентификатор ресурса. Мне потребовалось немного времени, чтобы установить эту связь.. Я думаю, что понял, спасибо! - person Gene Bo; 06.05.2016
comment
Разве это не должно быть app:imageResource? - person NameSpace; 17.08.2016
comment
@NameSpace, включая приложение, не будет иметь значения, единственная разница, которую я видел сам, заключается в том, что ide больше не будет помечать его как недействительный - person Joe Maher; 18.08.2016
comment
Это переопределит каждое отдельное использование android:src в xml во всем вашем приложении. Разве привязка данных недостаточно умна, чтобы применять этот атрибут только к ImageView, потому что это то, что определено в функции? Я думаю, что android:src был бы предпочтительнее... подумайте, что делает сам Android для адаптеров привязки ImageView: android.googlesource.com/platform/frameworks/data-binding/ +/ - person Splash; 31.10.2016
comment
О, да, извините, я имел в виду каждое использование android:src в imageview в приложении, но вы делаете хороший вывод, я не понимал, что привязка данных была достаточно умной, чтобы соответствовать типу, указанному в этом примере, который вы иметь ссылку. изменит мой ответ - person Joe Maher; 01.11.2016
comment
У меня похожая проблема. В некоторых случаях мне нужно показать один из 100 png, как это сделать с привязкой данных? - person svarog; 15.06.2017
comment
@hogar лично у меня не было бы моей логики для этого в адаптере, я бы обработал изображение 1/100 отдельно, а затем передал его - person Joe Maher; 16.06.2017
comment
@ Джо Махер, я не понимаю твоей точки зрения. Не могли бы вы немного пояснить это? Спасибо - person svarog; 16.06.2017
comment
что такое imageView передается в качестве параметра в методе setImageDrawable - person Manish Butola; 27.02.2018
comment
Использование @BindingAdapter("android:src"), которое принимает int, так как параметр конфликтует с android:src="@{aBoolean ? @color/red : @color/green}". Цвет также передается этому адаптеру как целое число, поэтому ресурс для imageView.setImageResource(...) не может быть разрешен. - person Jakob Ulbrich; 05.04.2018
comment
Нет необходимости в BindingAdapter. Метод ImageView.setImageResource имеет имя стиля установки. Привязка данных связывает сеттеры автоматически. - person hqzxzwb; 07.11.2019
comment
Это элегантное решение - person Leo Mrko; 26.01.2021
comment
можно конвертировать в Kotlin как расширения (немного кода) - person user924; 27.01.2021

Пользовательский BindingAdapter вообще не нужен. Просто используйте

app:imageResource="@{yourResId}"

и это будет работать нормально.

Проверьте это, чтобы узнать, как это работает.

person hqzxzwb    schedule 07.11.2019
comment
у этого должно быть больше голосов, так как это отличный ответ примерно в 2020 году - person JohnnyLambada; 07.02.2020
comment
определенно, лучший и самый простой ответ - person luckyhandler; 18.09.2020
comment
Это выглядит как лучший и наиболее подходящий ответ на конец 2020 года. - person mcy; 22.09.2020
comment
Я смотрю на класс ImageView и следую методу setImageResource, кажется, что в конечном итоге это решается на resolveUri, и есть, если не нулевая проверка. Так что это сработает для Int Интересно, что может случиться, если Int?. Когда привязки выполняются, например, если что-то еще вызывает executePendingBindings, то не обнуляемое значение по умолчанию равно нулю, обнуляемое значение равно нулю. - person cutiko; 20.10.2020
comment
определенно работает ;) большое спасибо - person naitbrahim; 09.06.2021

определять:

@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}"/>
person qinmiao    schedule 29.04.2016
comment
где добавить этот метод - person myatmins; 09.01.2017
comment
поддержка добавить его в любой класс, возможно, вы можете создать ImageDataBindingAdapter.class. - person qinmiao; 09.01.2017

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

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

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

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

or

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

------ начать обновление 2 июля 2018 г.

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

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

or

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

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

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

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

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

person Maher Abuthraa    schedule 15.11.2017
comment
Я думаю, что кода внутри xml нужно избегать по возможности - он не тестируемый, может нагромождаться и не очевиден. Но я согласен, что перегрузка стандартных атрибутов может сбивать с толку. Я думаю, что лучший способ — назвать новый атрибут по-другому, в данном случае srcResId, но все же использовать BindingAdapter. - person Kirill Starostin; 14.06.2019

Основываясь на ответе Махера Абутраа, это то, что я использовал в XML:

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

Переменная context доступна в выражении привязки без какого-либо импорта. Кроме того, нет необходимости в пользовательском BindingAdapter. Только предостережение: метод getDrawable доступен только с API 21.

person Tom Ladek    schedule 25.10.2019
comment
Используя <import type="androidx.core.content.ContextCompat"/> и android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}", вы можете достичь еще более низкого уровня API. - person Bruno Bieri; 14.04.2021

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

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

Чем больше вы можете сделать с DataBindingAdapter

  • Вы можете установить URL-адрес изображения, Файл, Растровое изображение, Массив байтов, Рисуемый, Drawable Id что угодно путем привязки данных.
  • Вы также можете установить изображение ошибки / изображения-заполнители с помощью передачи нескольких параметров адаптеру привязки< /а>.

Установите любой из этих типов:

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

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

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

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

person Khemraj Sharma    schedule 25.10.2018
comment
` errorImage=@{@drawable/ic_launcher}`. Эта штука у меня даже не компилируется - person Vivek Mishra; 07.12.2018
comment
@VivekMishra Возможно, ваш ic_launcher находится в mipmap? Попробуйте @mipmap/ic_launcher. - person Khemraj Sharma; 07.12.2018
comment
@VivekMishra Можете ли вы вставить соответствующий журнал ошибок? Вы добавили этот метод в свой класс использования привязки? - person Khemraj Sharma; 07.12.2018
comment
****/ошибка привязки данных ****msg: Не удается найти получатель для атрибута «приложение: src» с типом значения java.lang.String на com.zuowei.circleimageview.CircleImageView. Я пробовал как с Android, так и с пространством имен приложений, и оба они у меня не работали. Я также заменил изображение по умолчанию на circleImageView в параметре - person Vivek Mishra; 07.12.2018
comment
Также я создал адаптер привязки в отдельном классе - person Vivek Mishra; 07.12.2018
comment
@VivekMishra это происходит только после добавления атрибута errorImage? Также CIV унаследован от ImageView, поэтому вам не нужно изменять параметры адаптера привязки. - person Khemraj Sharma; 07.12.2018
comment
Я использую атрибут placeHolderImage и src из адаптера привязки. - person Vivek Mishra; 07.12.2018
comment
@VivekMishra Это placeholderImage в приведенном выше методе, вы сделали ошибку в типе? - person Khemraj Sharma; 07.12.2018
comment
@BindingAdapter (value = * arrayOf (android: src, placeholderImage, errorImage), requireAll = false) - person Vivek Mishra; 07.12.2018

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

android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
person Fahad Alotaibi    schedule 04.03.2019

Я не эксперт в 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}". Yes... this was really causing me headaches! Let's break it down:
    • app:appIconDrawable: This can be anything: app:iCanBeAnything! Really. You can also keep "android:src"! However, take a note on your choice, we will use it later!
    • "@{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'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:

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);
   }
}
person Tanasis    schedule 23.11.2018

Эта работа для меня. я бы добавил его в ответ @hqzxzwb в качестве комментария, но из-за ограничений репутации.

У меня есть это, на мой взгляд, модель

var passport = R.drawable.passport

Затем в моем xml у меня есть

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

И это все

person Raines    schedule 29.04.2020
comment
Хорошо, но вы забыли упомянуть, что вы должны импортировать контекст. Не могли бы вы обновить свой ответ? - person deadfish; 29.10.2020

Использование 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));
        }
    }
}
person 최봉재    schedule 14.03.2017

Пользовательский 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}" />
person luibel    schedule 02.02.2021

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

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

В вашем XML;

<androidx.appcompat.widget.AppCompatImageButton
   .
   .
   .
   android:src="@{viewState.getSource(context)}"
person Cafer Mert Ceyhan    schedule 07.10.2019

установить изображение, как это,

  <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" />
person luttu android    schedule 20.05.2020