Добро пожаловать в мою серию блогов о решении проблем! Я опишу здесь общие препятствия, с которыми вы можете столкнуться при разработке приложений для Android, и постараюсь дать вам простые решения для преодоления этих трудностей.

Проблема

Допустим, мы хотим создать простое приложение, отображающее список цветов. Каждый элемент в этом списке содержит имя слева и маленький кружок с этим цветом справа. Чтобы немного подправить его, мы хотим, чтобы наш цвет отображался как градиент. Звучит просто, правда? Итак, мы начинаем с создания RecyclerView, Adapter, затем ViewHolder, который будет связывать нашу сущность, например. ColorItem. У нас может получиться такой код в нашем ViewHolder:

А наш фоновый ресурс с возможностью рисования будет выглядеть так:

Что возможно могло пойти не так? Построим и запустим.

Подождите, что-то не так! Все чертежи имеют цвет фона последнего привязанного элемента, хотя я привязываю правильный цвет для каждого элемента в ViewHolder.

Решение

Чтобы понять, что здесь произошло, нам нужно знать одну вещь о Drawable на Android. Каждый раз, когда мы создаем View, для которого задан фон для ресурса Drawable, он будет создавать новый Drawable экземпляр, но все они имеют общее состояние! Это связано с оптимизацией, чтобы не создавать, например, растровое изображение для каждого вида, использующего этот ресурс. Если вы хотите узнать об этом больше, здесь - отличный пост в блоге, объясняющий все.

Итак, мы уже знаем, в чем проблема. Что мы можем сделать, чтобы решить эту проблему? К счастью для нас, есть простой метод, который мы можем использовать, чтобы указать Drawable иметь собственное состояние и не передавать его другим Drawable экземплярам. Он называется Drawable.mutate(), и вы можете узнать о нем подробнее в документации.

Вот обновленный код:

Давайте построим и запустим.

Успех! Это тот результат, которого мы ждали. Поскольку здесь мы используем Kotlin, мы можем создать красивый DSL для Drawable мутаций. Итак, окончательная версия нашего кода будет выглядеть так:

Заключение

Мы должны помнить об этих подводных камнях оптимизации платформы Android при программном изменении наших Drawable объектов. В противном случае это может привести к непредсказуемому поведению, подобному тому, что мы видели выше. Надеюсь, я сэкономил вам время на поиске в Google и размышлениях о том, что не так с вашим кодом. Я с нетерпением жду ваших отзывов и терпеливо жду следующих сообщений в моей серии блогов о решении проблем! :)