Реализуем Android Adaptive Icons.
Перевод https://medium.com/google-developers/implementing-adaptive-icons-1e4d1795470e
Это третья статья цикла. Рекомендуем ознакомиться с двумя предыдущими, если вы их ещё не читали. Первый пост раскрывает основные положения концепции Android Adaptive Icons:
Второй пост больше ориентирован на дизайнеров. В нём рассказывается о том, как сделать адаптивную иконку привлекательной, задействовав при этом максимум возможностей технологии:
Настало время задуматься о реализации.
Основы
Адаптивные иконки — это новый тип drawable-ресурсов type, за работу с которым отвечает класс AdaptiveIconDrawable. На данный момент, всё устроено так, что вам никогда не придётся работать с этим классом напрямую — вы объявляете его в XML-файле ресурса, а затем ссылаетесь на этот файл из манифеста приложения. Это будет выглядеть похожим образом:
<adaptive-icon>
<background android:drawable="@[drawable|mipmap|color]/bar"/>
<foreground android:drawable="@[drawable|mipmap|color]/foo"/></adaptive-icon>Каждый используемый drawable-ресурс должен иметь габариты 108dp*108dp. Фоновый drawable должен быть полностью непрозрачным, тогда как drawable переднего плана может содержать прозрачность.
minSDK 26
Адаптивные иконки поддерживаются только на API 26+, а это значит, что вы можете рассчитывать на VectorDrawable — они точно будут поддерживаться на устройстве, которые сможет отобразить адаптивную иконку.
К сожалению, использовать кастомные типы drawable для вашей иконки не получится. Так как иконка будет загружаться процессами других приложений, вы должны придерживаться стандартных платформенных типов.
Использование векторной графики крайне выгодно в плане оптимизации размера сборки. Одно и то же изображение будет одинаково хорошо смотреться на экранах с любым dpi.
Многие ещё не успели опробовать реализацию градиентов для VectorDrawable. Если это ваш случай, стоит прочесть пост Ian Lake, раскрывающий некоторые нюансы использования VectorDrawable для адаптивных иконок:
Ian приводит пример использования простого линейного градиента, хотя VectorDrawable умеет и кое-что поинтереснее. Ниже представлен пример реализации “длинной тени” с использованием радиального градиента. Обратите внимание, что в примере используется синтаксис инлайна ресурсов, который позволяет легко объединить несколько файлов ресурсов в один (немного AAPT-магии):

Многие иконки содержат тени (это позволяют гайдлайны материал-дизайна). К сожалению, VectorDrawable не поддерживает тени. При создании адаптивной иконки, для решения этой проблемы можно пойти двумя путями:
- Так как лаунчеры получили возможность накладывать на иконки различные маски, они могут использовать эту возможность и для наложения теней. Вам, как разработчику, больше не придётся заботиться о тенях;
- Каждая адаптивная иконка состоит из двух отдельных изображений, поэтому если на каком-либо из этих слоёв не требуются тени — его можно выполнить в векторном формате, а другой в растровом.
Некоторые простые тени можно реализовать с помощью градиентов, но это, к сожалению, не всегда подходящий вариант.
Если без растра никуда
Если вашу задумку не получается реализовать при помощи векторной графики — используете PNG, в этом нет ничего страшного. Иконка приложения — это первое, что увидит пользователь, и первое, что произвёдет на него впечатление. Столь важный нюанс определённо стоит нескольких лишних байт, если это поможет достичь желаемого эффекта.
Стоит помнить об одном трюке, который позволит немного облегчить рендеринг адаптивных иконок в том случае, если изображение переднего плана содержит большое количество прозрачных пикселей. Несмотря на то, что графические ресурсы неплохо сжимаются при сборке, каждый пиксель изображения (даже прозрачный) занимает 8 бит памяти при отрисовке. На первый взгляд, это не много, но для отрисовки иконки 512px*512px устройству потребуется 2Мб памяти. Если ваше изображение — это графический элемент на прозрачной подложке, количество пикселей в иконке можно значительно уменьшить, просто кадрировав изображение, обрезав лишнюю прозрачность по краям. Для сохранения исходных габаритов иконки в 108dp*108dp, оберните кадрированное изображение в InsetDrawable. InsetDrawable позволяет обернуть любое изображение и снабдить его дополнительными отступами по краями. Слабое место этого подхода заключается в том, что InsetDrawable не очень хорошо растягивается (например, если вы задали верхний отступ в 16dp — он всегда будет 16dp, и не важно, как при этом будет масштабироваться изображение). Эта проблема решена в API 26 с появлением относительных отступов. Теперь для корректного масштабирования можно задавать отступы не в фиксированных единицах, а в процентах от исходных размеров иконки.
Допустим, у вас есть изображение 54dp*54dp, окружённое прозрачной рамкой. Вот как это можно оптимизировать при помощи InsetDrawable:
<inset ...
android:drawable="@mipmap/ic_fg_trimmed"
android:insetLeft="25%"
android:insetTop="25%"
android:insetRight="25%"
android:insetBottom="25%" />Здесь приведён пример использования этой техники:

Да, вам всё равно придётся включить в проект целый набор ресурсов для экранов с разной плотностью пикселей, но, по крайней мере, каждый из этих ресурсов будет немного меньше.
И последнее
Если вы создаёте адаптивную иконку, вам может быть полезно приложение Adaptive Icon Playground. Оно позволяет увидеть, какой будет ваша иконка на разных устройствах, как на ней скажется применения различных масок и динамических эффектов.

Вы можете скачать APK или посмотреть исходный код на Github:
Надеемся, что цикл статей помог вам в понимании концепции адаптивных иконок и раскрыл некоторые нюансы их реализации. А теперь, попробуйте сделать что-то своё! Успехов.
