Русский
Русский
English
Статистика
Реклама

Темная тема

Дизайнеру приложений как создать и передать в разработку тёмную тему

16.07.2020 12:18:27 | Автор: admin


В конце 2019 года зарелизили iOS 13 и Android 10 с поддержкой автопереключения на тёмную тему. Мы решили добавить её в приложение Ростелеком Ключ под iOS и Android, над которым работали в тот момент. В процессе не обошлось без сложностей. Рассказываем о нашем опыте, чтобы вы в аналогичной ситуации сэкономили время и нервы.

Зачем делать тёмную тему


Может показаться, что это всё на волне хайпа. Но не только :)

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

Если мы выходим из тёмного помещения в светлое, то ненадолго слепнем, но привыкаем к новым условиям уже через 46 минут. Когда же мы переходим из светлого места в тёмное, адаптация занимает больше времени: в среднем 3045 минут. Если человеку постоянно приходится переводить взгляд между ярким приложением и тёмным окружением, адаптация к яркому дисплею будет быстрой, а вот обратная адаптация к темноте медленной.

По этой причине некоторые приложения, например навигаторы, выпустили тёмные темы раньше операционных систем. Если человек едет по дороге ночью, важно, чтобы экран навигатора не отвлекал от движения.

В нашем случае приложение РТ Ключ кросс-платформенный сервис для управления устройствами на территории дома: домофонами, шлагбаумами, камерами видеонаблюдения во дворе и в подъезде. Жители домов используют его как днём, так и ночью.

Справедливо заметить, что тёмная тема также помогает экономить заряд (для некоторых типов экранов: OLED/amoled да, LCD нет). А в долгосрочной перспективе она может замедлить развитие близорукости.

Как перейти на тёмную сторону: пошаговая инструкция


Если вы совсем ничего не знаете о тёмной теме, то можно начать знакомство со статей в Human Interface Guidelines для iOS и в Material Guide для Android. Там подробно разобрано, как цвета и слои взаимодействуют друг с другом в ночном режиме. Перейдём к нашим советам:

1. Приведите в порядок макеты и соберите UI kit

UI kit серьёзно ускоряет работу дизайнера и разработчиков. Обычно мы собираем его сразу после утверждения визуальной концепции на примере нескольких основных экранов приложения. В UI kit входят:

  • Цветовая палитра стилей для обычного состояния элементов, состояния при нажатии, цвета для неактивных элементов, инпутов в фокусе и так далее.
  • Текстовые стили.
  • Все элементы интерфейса (кнопки, поля ввода, элементы списков, блоки с заголовками и так далее) в различных состояниях и ситуациях в виде master components.
  • Сет иконок в черном цвете.
  • Иллюстрации.

UI kit нашего приложения можно рассмотреть в Figma.

Все последующие экраны собираем уже из готовых компонентов. Но при экспорте макетов из Sketch в Figma наш UI kit серьёзно пострадал: пришлось его обновить, заново назначить цветовой стиль для каждого элемента и пересобрать экраны из компонентов.

Все последующие экраны собираем уже из готовых компонентов. Но при экспорте макетов из Sketch в Figma наш UI kit серьёзно пострадал: пришлось его обновить, заново назначить цветовой стиль для каждого элемента и пересобрать экраны из компонентов.

2. Договоритесь о названиях цветов

Чтобы дизайнерам, iOS и Android-разработчикам было проще общаться между собой, цвета мы решили назвать универсально для обеих платформ. В прошлой версии UI kit цветовые стили мы обозначили незатейливо по номерам: C1, C2, C3 Это было не слишком удобно: при обсуждении все называли цвета не по цифрам, а по оттенкам: фиолетовый, оранжевый, чёрный и т. д.

Однако с появлением тёмной темы использовать оттенки в наименовании цветов стало невозможно: например, белый цвет фона в светлой теме сменился на тёмный. Значит, нужно было придумать новый принцип наименований.

После нескольких встреч с участием всей команды определили, что название цвета должно зависеть от его назначения и состоять из следующих частей:

  • Назначение цвета или элемент, на котором он используется.
  • Приоритетность использования (опционально).
  • Состояние элемента, если это применимо (опционально).


Слева наименования для цветов кнопки в обычном состоянии, при нажатии на iOS, цвет Ripple в Android и неактивная кнопка на обеих платформах. Справа имена для текстов на различных поверхностях

В общем, если в вашем проекте ещё нет UI kit, а вместо цветовых стилей назначены обычные цвета, пора причесать макеты.

3. Подберите цвета для тёмной темы

Катя Рокитян, дизайнер Redmadrobot:
Чтобы не тратить драгоценное время, мы не прорабатывали все макеты в тёмной теме, а выбрали только несколько экранов с принципиально различающимися компонентами. С их помощью мы подбирали удачные цвета и добавляли их в палитру. Затем разработчики обновляли цвета в коде приложения, готовили для нас тестовую сборку, а мы изучали, как тёмная тема смотрится живьём. Если выявляли ошибки, правили макеты и палитру.

3.1. Фоновый цвет

Начать перерисовку макета стоит с фонового цвета: он занимает большую часть экрана. В гайдах Material Design советуют брать за основу нейтральный тёмно-серый (#121212). Он, в отличие от черного, оставляет простор для построения глубины экрана.

Фоновый цвет должен хорошо сочетаться с цветом интерактивных элементов: кнопок, иконок и т. д. У нас в светлой палитре для интерактивных элементов использовался фирменный фиолетовый цвет Ростелекома #7700ff. В дальнейшей работе отталкивались от него.

Нейтральный тёмно-серый плохо смотрелся с брендовым фиолетовым, поэтому мы последовали советам гайдлайнов Material Design. Ребята рекомендуют наложить поверх нейтрального фонового серого #121212 фирменный цвет с 8% непрозрачности.


Нейтральные и брендированные цвета фона в тёмной теме

Помимо основного фона у нас встречаются карточки с небольшим подъемом над поверхностью. В Material Design рекомендуют в тёмной теме создавать элевацию элементов за счёт осветления фона: чем ближе карточка к пользователю, тем светлее.

Подобрать подходящие цвета можно, накладывая на фоновый цвет полупрозрачный белый слой. Мы так и сделали.



3.2. Создание базовой палитры

В Material Design рекомендуют при создании брендированной тёмной темы заменять цвета на менее насыщенные аналогичного оттенка. В качестве фирменного цвета для РТ Ключ мы использовали фиолетовый.

При попытке осветлить его по рецепту Material Design его оттенок стал розоватым. Оригинальный фирменный цвет выглядел синее, поэтому мы вручную изменили его оттенок.


Оттенок, который мы получили с помощью наложения белого, был слишком розовый сместили тон в сторону синего

В светлой теме мы использовали один фиолетовый цвет для больших кнопок, линейных иконок и навигационной панели (navbar). При подборе цветов для тёмной темы мы не смогли найти фиолетовый оттенок, который бы одинаково удачно смотрелся на всех этих формах.

Navbar и крупные кнопки в осветленной фиолетовой версии особенно сильно отвлекали внимание от контента, а если мы снижали яркость, линейные иконки терялись на тёмном фоне.


В исходной светлой теме насыщенный фиолетовый цвет одинаково хорошо смотрится на крупных блоках с белым текстом и на тонких линейных иконках на светлом фоне. А на тёмном фоне всё не так

Пришлось как в тёмной, так и в светлой теме заменить один исходный цвет на три: один для контурных иконок, второй для больших кнопок с фоном, третий для уголка на главном экране и navbar на остальных.


Заменили один фиолетовый на три так намного лучше

Помимо фиолетового мы использовали оранжевый, желтый, бирюзовый и красный. Оранжевый для call to action элементов, желтый для уведомлений, бирюзовый для подтверждения операции, красный для ошибок.

Когда мы осветлили эти четыре цвета по совету Material Design, серьезно изменилась контрастность приложения по сравнению со светлой темой. Пришлось отступить от гайдов: оранжевый, бирюзовый и красный мы не осветлили, а затемнили. Жёлтый оставили неизменным, поскольку при затемнении он грязнил.



3.3. Особенности палитры iOS

В iOS можно задать оттеночный цвет (он же tint), которым будут окрашены все интерактивные элементы: кнопки, иконки, переключатели и т. д. Например, в настройках iPhone используется синий цвет, а в приложении часы оранжевый.

В светлой теме у нас tint совпадает с цветом больших кнопок, а в тёмной теме мы сделали его чуть светлее, чтобы линейные иконки были хорошо различимы на тёмном фоне.


Оттеночный цвет (tint) в темной теме

В iOS заложена возможность менять цвет интерактивных элементов при нажатии. Это полезно, так пользователю сразу понятно, отреагировало приложение на его действие или нет.

По умолчанию цвет при нажатии становится полупрозрачным. Мы же подумали, что интереснее будет смотреться, если при нажатии кнопки будут углубляться, то есть темнеть.

3.4. Особенности палитры Android

В Android при нажатии на интерактивный элемент от точки прикосновения к нему расходится волна. Этот эффект называет ripple. По умолчанию ripple немного затемняет исходный элемент, а мы хотели его, наоборот, осветлять.

Your browser does not support HTML5 video.

Ripple в Android приложении

Также в Android есть особенности отрисовки status bar и navigation bar. Status bar строка состояния, где отображаются уведомления, уровень сигнала, заряд батареи и время. Navigation bar панель, где располагаются кнопки назад, домой и недавние приложения.

С появлением смартфонов с крупными дисплеями, доходящими до самого края устройства, челками и дырками под камеру, в Material Design представили концепцию edge to edge. Её суть в том, что служебные элементы не должны съедать полезную площадь, и как можно большее место на экране должен занимать полезный контент.

Для этого в Android доработали поддержку прозрачных status bar и navigation bar. Дело в том, что эти панельки не на всех телефонах имеют одинаковую высоту. И если до начала поддержки edge to edge мы назначали им прозрачный фон, на некоторых устройствах они некрасиво накладывались на контент экрана. Теперь в материальных компонентах появились системные отступы: разработчики могут определять размер status bar и navigation bar и задавать соответствующий отступ для контента. Поэтому раньше в Material Design рекомендовали выбирать непрозрачный фон для status bar и navigation bar, а теперь наоборот.

Однако важно учитывать, что не во всех поддерживаемых версиях Android можно назначить цвет иконок в системных компонентах:

  • до 6.0 иконки в status bar и navigation bar всегда белые;
  • с версии 6.0 можно задать, белыми или черными будут иконки в status bar, но navigation bar будет вести себя так, как в предыдущих версиях.
  • с версии 8.1 можно выбрать цвет иконок как в status, так и в navigation bar.

Чтобы избежать странных наложений на контент в Ключе, мы поступили так:

  • в старых версиях Android до 6.0 для обеих панелек задали черный фон с 50% прозрачности на нем хорошо смотрятся белые иконки;
  • с 6.0 и до 8.1 navigation bar остается с полупрозрачным черным фоном, а status bar полностью прозрачный;
  • с версии 8.1 фон обеих панелек полностью прозрачный.

Если вы по каким-то причинам не готовы к такой поддержке edge to edge, лучше сделать status bar и navigation bar универсальными. Поддержка edge to edge:

3.5. Проверьте контрастность элементов

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

Лучше всего проверять контрастность с помощью тестирования: открыть интерфейс на смартфоне, используя Figma Mirror или аналогичный софт, выйти на яркое солнце (тёмная тема предназначена для использования в темноте, но людям ничто не помешает использовать её на свету).

Также будет полезно попросить посмотреть на приложение людей с нарушениями зрения (близорукостью, дальнозоркостью, дальтонизмом). Но если такой возможности нет, контрастность можно проверить на сайте contrast-ratio или с помощью плагина в Figma.

3.6. Иллюстрации и анимации
Если в вашем приложении используются иллюстрации, для тёмной темы их придётся перекрашивать. Насыщенные цвета, которые отлично смотрятся на светлых поверхностях, на тёмных режут глаз и затрудняют восприятие. Используйте менее насыщенные тона.


Для тёмной темы приходится делать отдельные версии иллюстраций и анимаций в тёмных цветах

Как передавать в разработку

Мы работали в связке Figma + Zeplin. Это может показаться странным, но мы всей компанией перешли на Figma из Sketch в конце лета 2019, прямо перед началом работы нам тёмной темой. И чтобы сэкономить время на адаптацию разработчиков к новому инструменту, продолжили работать с Zeplin. И тут он преподнес нам несколько сюрпризов.

В палитре Zeplin нельзя создать цветовые стили с одинаковыми HEX. Поэтому нам пришлось незначительно, практически незаметно для глаза, менять HEX у фиолетового цвета в светлой теме.
Даниил Субботин, iOS-разработчик Redmadrobot subdan:
Обнаружилось, что ни один инструмент дизайнера, в том числе Zeplin, не поддерживает темную тему и поэтому не позволяет иметь несколько цветовых палитр в одном проекте. Пришлось искать пути обхода. Например, мы создали два проекта: один со светлой палитрой, а другой с тёмной.


1. Особенности iOS dev


Следующий сюрприз, который нам преподнес Zeplin: из него можно экспортировать только одну палитру в Xcode-проект. Либо светлую, либо тёмную. При попытке экспортировать цвета тёмной палитры Zeplin просто заменял ими все светлые. Мы стали переносить цвета вручную, копируя HEX-значения. То же самое делали и с изображениями.

Даниил Субботин, iOS-разработчик Redmadrobot subdan:
После долгих мучений я написал утилиту, которая выгружает обе цветовые палитры прямо в Xcode-проект, используя Zeplin API. Это сильно упростило жизнь. Дизайнер сообщает, что добавил новый цвет или изменил старый, я запускаю скрипт, и все изменения автоматически подтягиваются в проект

2. Особенности Android dev


На Android тёмная тема доступна на всех версиях ОС (мы тестировали начиная с пятой). Но переключать её в системе можно только на 10-м Android, поэтому стоит добавить в приложение возможность переключать её вручную.

Владислав Шипугин, android-разработчик Redmadrobot shipa_o:
Мы добавили следующие варианты выбора темы: всегда светлая, всегда тёмная, выбирается в зависимости от режима энергосбережения (9-я версия андроида и ниже), переключается в зависимости от настроек системы (10-я версия андроида и выше). Но важно учесть, что выбранная пользователем тема приложения в системе не сохраняется. Нужно запоминать её внутри и активировать при каждом запуске приложения


В Android есть своя система цветов для материальных компонентов (кнопок, app bar, текстовых полей и т. д.). Гайдлайны Material Design о цвете.

Важно сопоставить вашу палитру с материальной с помощью описания темы приложения. Вот статья, которая поможет вашим Android-разработчикам разобраться, как это сделать. Но даже после этого может сохраниться проблема с неправильными цветами. Чтобы это исправить, можно описать стиль конкретного компонента.

Как поддерживать и развивать


С тех пор как мы проработали тёмную тему, у нас добавилось несколько новых функций, появились планшетные пользователи, а продуктовый офис в Ростелекоме задумался о том, чтобы выпустить приложение РТ Ключ в виде white label для застройщиков в их фирменных цветах.

Оля Сартакова, арт-директор Redmadrobot:
Мы поняли, что текущую версию дизайна, разработанную для запуска MVP, пора переосмыслить как структурно, так и визуально. Мы полностью переработали структуру приложения с учетом тех функций, которые должны появиться в ближайшие два года, добавили дашборд для быстрого доступа к самым популярным функциям, избавились от ярких фирменных элементов в оформлении (вроде уголка на главном экране и цветного nav bar). А вот грамотная работа над цветовой системой позволила практически полностью сохранить её при тотальном редизайне приложения.

Вторая версия дизайна приложения в той же цветовой палитре:


Даниил Субботин, iOS-разработчик Redmadrobot subdan:
К моменту запуска второй версии мы безболезненно переехали на Figma, я адаптировал утилиту, которую изначально написал для Zeplin, под Figma. Теперь мы обновляем цвета, иконки и картинки в Xcode и Android Studio в один клик


Скачать нашу утилиту можно здесь.

Выводы: как у нас, только лучше


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

  • Соберите UI kit.
  • Продумайте систему наименования цветов или воспользуйтесь нашей.
  • Подберите брендированный фоновый цвет.
  • Подберите оттенок для основного и второстепенных фирменных цветов.
  • Если ваше приложение кроссплатформенное, составьте список отличий цветовых схем iOS и Android. А также проверьте, не нужно ли добавить в приложение на Android ручную настройку тёмной темы.
  • Проверьте контрастность выбранных цветов.
  • Подготовьте иллюстрации и анимации для тёмной темы.
  • Обновите UI kit.
  • Воспользуйтесь нашей утилитой для передачи элементов в разработку.

Полезные материалы


Чтобы ещё глубже погрузится в тёмную тему:


И немного полезностей для разработчиков:

Подробнее..

Android-разработчикам как сократить время реализации тёмной темы с пары месяцев до недели

20.10.2020 14:07:37 | Автор: admin

Привет, меня зовут Влад Шипугин, я Android-разработчик в Redmadrobot. В этой статье я хочу поделится опытом реализации тёмной темы, создания удобного UI Kit, как для разработки, так и для дизайнеров. Я расскажу про использование Material Components и работу с Vector Drawable. Также вы узнаете, как быстро поддержать режим edge-to-edge с использованием Window Insets и познакомитесь с моей библиотекой edge-to-edge-decorator.

Где-то полгода назад мы начали разработку тёмной темы для приложения Ростелеком Ключ. Наши дизайнеры уже писали про ценность тёмной темы, как её спроектировать и передать в разработку. В этой статье я продолжу рассказ от лица разработчика, расскажу с какими проблемами мы столкнулись в процессе реализации тёмной темы, почему это заняло у нас 3 месяца и как реализовать тёмную тему всего за одну неделю.

Этой осенью Android 10 исполнился год, и именно такое время требуется, чтобы изучить все новинки, реализовать их и протестировать, поэтому в статье будет живой опыт из реальных проектов.

Не важно, давно ли вы начали разработку под Android или только делаете ваше первое приложение, думаю, вам будет интересно почитать эту статью, потому что в ней я поделился всем накопленным опытом и самым актуальным набором материалов по дизайну Android-приложений.

Как сделать удобный UI Kit

Изначально у нас был простой план: дизайнеры делают тёмную тему, а мы просто добавляем файл value-night/color.xml и всё. Но позже мы столкнулись с проблемами. И теперь, когда приложение давно опубликовано в сторе, я могу рассказать о решении этих проблем, чтобы вы никогда не наступали на наши грабли.

Проблема 1: сложность при выборе названий для палитры цветов

Первый вариант именования цветов использование названия цвета как есть: realblue, darkgrey и так далее.

Пример палитры цветов из ZeplinПример палитры цветов из Zeplin

Думаю, каждый сталкивается с проблемой, когда при использовании такого подхода, в будущем, при редизайне, получается, что realblue меняет свой hex на hex красного цвета или жёлтого. Это можно исправить, если переименовать цвет во всем приложении, но тогда возрастает вероятность ошибиться, а отлавливать такие ошибки крайне тяжело.

Второй вариант это абстрактные названия, мы решили использовать C1, C2 (С от слова Color) и так далее. Эта абстракция позволяет отвязать значение цвета от его названия. Сегодня это может быть красный, а потом желтый это не важно, и вам не нужно рефакторить всё приложение.

Пример палитры цветов из ZeplinПример палитры цветов из Zeplin

Но тут появляется другая проблема ты не можешь держать таблицу соответствий цветов в голове. Приходится всё время её открывать: при обсуждении с дизайнером или при разработке всегда нужно проверять конкретный цвет приложения.

Редко, когда удается запомнить конкретную цифру цвета. Какой тут должен быть цвет: С4 или С7? станет самым частым вопросом на обсуждениях дизайна. Мы долгое время пытались найти баланс между понятным названием цвета, таким как realblue и максимально абстрактным цветом для простоты рефакторинга и редизайна: С1, C2, C3 и так далее.

Есть и альтернативный, третий вариант когда названия цветов зависят от компонента, например, гайдлайны Material Design или Apple HIG.

material.iomaterial.io

Этот вариант казался самым логичным, но в нём было больше всего проблем:

  1. Цвета даёт дизайнер. И начинающим дизайнерам сложно придумывать названия цветов, а подключать всегда арт-директора для такой мелочи невыгодно.

  2. Некоторые цвета могут содержать одинаковый hex. В целом, в этом нет проблемы, но как оказалось, в палитре цветов Zeplin не может содержаться два цвета с одинаковым hex, но разными названиями.

  3. Сложно объединить рекомендации Material Design и Apple HIG в одну палитру, да и стандартных цветов может быть недостаточно для приложения.

Много встреч прошло за обсуждением названий цветов. Они должны были быть удобными для всех: Android- и iOS-разработчиков, и дизайнеров. Через некоторое время мы остановились на третьем варианте, но сформировали чёткие правила по наименованию цветов, чтобы не тратить много времени на придумывание названия. Также мы договорились добавлять цвета с одинаковым hex отличающимся на единицу.

Название цвета

iOS

Android

text/primary

textPrimary

text_primary

button/pressed

buttonPressed

button_pressed

Вот пример готовой палитры из Zeplin:

Пример финальной палитры из ZeplinПример финальной палитры из Zeplin

Позже мы отказались от Zeplin и перешли на Figma. Вот такая палитра в Figma у нас получилась. А подробнее про переход с Zeplin на Figma уже писал наш iOS разработчик Даниил Субботин @subdan в статье про утилиту экспорта UI Kit из Figma figma-export.

Проблема 2: непонятные стили шрифтов

Особых проблем в работе со штифтовыми стилями у нас не было. Дизайнеры обычно заносят все шрифтовые стили приложения в UI Kit, но их наличие не гарантирует, что в макетах будут использоваться только они. Периодически то там, то тут, Zeplin не определял указанный шрифт и приходилось отвлекать дизайнера, чтобы узнать какой именно шрифт необходимо использовать.

Сейчас в макетах всегда отображаются правильные стили шрифтов. Дизайнеры добились этого за счёт перехода со Sketch + Zeplin на Figma она лучше распознает шрифтовые стили.

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

Пример наших шрифтовых стилей можно посмотреть тут.

Проблема 3: дублирование иконок и трудности с их именованием

Пока дизайнеры переделывали палитру цветов, я столкнулся с новой проблемой иконки должны быть разных цветов в зависимости от выбранной пользователем темы. Самый простой вариант это добавить альтернативный набор иконок в директорию drawable-night. Но за простоту нужно платить:

  1. Количество иконок увеличивается в два раза, а значит, приложение весит больше. И App Bundle не поможет, потому что тема меняется динамически и все иконки должны находиться в итоговом APK.

  2. Названия иконок разных цветов должны всегда совпадать. Если дизайнер опечатался или изменил название, то вы добавите новую иконку рядом, а не замените старую. В таком случае искать ошибку придется вручную, а это сложно и долго.

  3. Всегда нужно помнить про альтернативные цвета иконок. Если добавляешь новую иконку, то нужно всегда добавлять её для темной темы, и на раннем этапе мы об этом часто забывали

Эти проблемы довольно существенны и такой вариант меня сразу не устроил. Позже я наткнулся на рекомендации Google и Apple. Они советуют использовать все иконки одного цвета: черного или белого, и перекрашивать их в нужный цвет в рантайме.

Мы сначала использовали иконки белого цвета, но потом столкнулись с проблемами их видимости. В Figma мы добавляли их на черный фон, а вот в операционной системе черный фон есть не везде, и иконок бывает просто не видно. Поэтому мы перешли на использование черных иконок.

Ещё одна проблема названия иконок придумывают дизайнеры и не всегда они совпадают с ходом мысли разработчиков: дизайнеры называют иконку исходя из визуальной составляющей, а разработчики исходя из предметной области. Поэтому разработчики переименовывают иконки у себя в проекте.

Ещё бывают моменты, когда дизайнер сам решает поменять название иконки и забывает сообщить об этом разработчикам. Все эти изменения приводят к тому, что при обновлении или добавлении иконок может появиться дублер иконки, но с другим названием. В таком случае много времени придется потратить на поиски одинаковых иконок.

Чтобы такого не было, следует сразу договориться о правильном именовании иконок. Мы остановились на таком варианте:

Название иконки

iOS

Android

ic/24/flash_on

ic24flashOn

ic_24_flash_on

ic/24/flash_off

ic24flashOff

ic_24_flash_off

Пример с нашим набором иконок можно посмотреть здесь.

Проблема 4: организация иллюстраций

Перекрашивать иконки удобно, но если картинка содержит больше одного цвета, то такой вариант не подходит. В данном случае остается добавить иллюстрацию альтернативного цвета в директорию drawable-night и попросить дизайнера подготавливать иллюстрации в альтернативной теме.

У нас были случаи, когда иллюстраций не хватало или они рендерились криво. Чтобы избежать ошибок, мы собрали все иллюстрации в одном удобном для QA месте, и включили их в ручное регрессионное тестирование. Позже этот процесс можно будет автоматизировать с помощью скриншотного тестирования.

Вот пример того, что у нас получилось.

Проблема 5: отсутствие базовых компонентов или неправильное их использование

При проектировании дизайна приложений дизайнер сначала делает экраны, а потом переносит основные компоненты в UI Kit. При этом не каждый дизайнер уделяет достаточно времени технической составляющей при описании этих компонентов. В UI Kit могут отсутствовать некоторые состояния, которые разработчик может встретить в процессе реализации экранов. Такой UI Kit создается неполным, и если дизайнер сразу не сделает хороший UI Kit, то после создания тот и вовсе перестает поддерживаться.

При данном подходе UI Kit не является единым источником правды. Можно сказать, что в данном случае это UI Kit для галочки, и так делать нельзя.

Но как только дизайн-макеты начинают передавать в разработку, то единственным источником правды должен стать UI Kit и мастер-компоненты в нем. Иначе UI Kit будет вам мешать и замедлять работу, а не ускорять её.

Ценность UI Kit в том, что вы реализуете все базовые компоненты, а потом из этих компонентов собираете экраны. Для этого можно использовать мастер-компоненты в Figma, и styles или CustomViews в Android. В таком случае UI Kit экономит вам много времени.

Вот пример с описанием кнопок приложения, а полный UI Kit можно посмотреть тут.

Пример описания кнопок приложенияПример описания кнопок приложения

Как правильно реализовать UI Kit

После создания дизайнерами UI Kit можно подключаться и разработчикам. Мой план по реализации UI Kit с учетом темной темы был такой:

  1. Привести цвета, тему приложения и стили компонентов в порядок.

    • сделать палитру цветов (color.xml);

    • описать тему приложения;

    • описать стили компонентов, которые отличаются от базовой темы.

  2. Заменить все иконки на черный и окрашивать их в нужный цвет в момент отрисовки.

  3. Добавить альтернативные цвета values-night/color.xml.

  4. Добавить выбор темы в настройках приложения.

Я уже писал выше про утилиту Figma-export и статью, в которой раскрыты подробности её реализации. Эта утилита помогает избежать проблем с экспортом цветов, иконок и иллюстраций из Figma. С её помощью экспорт компонентов происходит автоматически, что позволяет полностью исключить человеческий фактор из этого процесса.

Реализуем палитру цветов

С помощью Figma-export создание палитры цветов происходит одной командой:

./figma-export colors -i figma-export.yaml

После этого в вашем приложении добавится или изменится файл color.xml.

Реализуем тему приложения

Для реализации темы в Android-приложении следует использовать библиотеку material-components. Именно так я и поступил: создал палитру цветов в color.xml и начал делать тему приложения. Но после этого я столкнулся с проблемой toolbar, cardview и ещё пару компонентов имели не тот цвет.

Прописывать все цвета по месту использования компонента мне показалось плохим решением слишком много дублирования.

На тот момент, в библиотеке material-components, ещё не было документации по темам и стилям. Как и многие Android-разработчики, я не знал, как правильно описывать тему приложения. Разработчики из Google даже шутили про это на Android Dev Summit 2019.

Моя тема приложения была описана неправильно и материальные компоненты, такие как кнопки, иконки и текстовые поля, выглядели не так, как я планировал. Например, цвет toolbar использовал цвет primary для светлой темы и, почему-то, переключался на surface в темной.

Позже оказалось, что в теме приложения, по умолчанию, цвет toolbar принимает значение, равное атрибуту ?attr/colorPrimarySurface. Тогда, чтобы понять почему так происходит, мне пришлось ковыряться в исходниках материальных компонентов. Мне удалось понять, какой смысл вкладывали авторы в описание темы приложения, и потом статья про темы и стили в Android-приложениях подтвердила мои догадки.

Сейчас вы можете узнать интересные детали в новой статье по материальным компонентам от Google. Картинка из этой статьи наглядно показывает, как работает тема с атрибутом colorPrimarySurface.

Примеры работы PrimarySurface атрибутаПримеры работы PrimarySurface атрибута

При реализации темы приложения важно понять концепции цветов и атрибутов, а также различия темы и стилей. Сейчас проблем с документацией больше нет. Я не стану описывать эти вещи в данной статье, чтобы не повторяться.

Google представили подробную серию статей по материальным компонентам. Поэтому, я рекомендую их к прочтению. Они помогут вам полностью разобраться в создании темы для Android-приложений:

Доп. информацию по материальным компонентам можно найти тут и в конце статьи:

Также, при реализации темы приложения, следует подключить дизайнера, чтобы он помог создать описание темы приложения и сконвертировать удобную палитру цветов для дизайна, в тему материальных компонентов. Здесь на помощь приходит раздел материальных компонентов, MaterialThemeBuilder и примеры от Google:

В итоге, я остановился на таком варианте описания темы и стилей.

Структура организации ресурсов:

  1. colors.xml цвета приложения;

  2. type.xml шрифты приложения;

  3. shape.xml формы приложения;

  4. themes.xml темы приложения;

  5. styles_button.xml кнопки приложения;

  6. styles_text_input.xml текстовые поля;

  7. styles_list_item.xml элементы списков;

  8. styles.xml прочие стили виджетов.

Итоговая тема приложения
<style name="Theme.App" parent="Theme.MaterialComponents.DayNight.NoActionBar">    <!-- Base color attributes -->    <item name="colorPrimary">@color/tint</item>    <item name="colorSecondary">@color/tint</item>    <item name="colorControlHighlight">@color/tint_ripple</item>    <item name="android:colorBackground">@color/background_primary</item>    <item name="colorSurface">@color/background_secondary</item>    <item name="colorError">@color/error</item>    <item name="colorOnPrimary">@color/text_primary</item>    <item name="colorOnSecondary">@color/text_primary</item>    <item name="colorOnBackground">@color/text_secondary</item>    <item name="colorOnError">@color/text_primary</item>    <item name="colorOnSurface">@color/text_secondary</item>    <item name="android:windowBackground">@color/background_primary</item>    <item name="android:statusBarColor">@android:color/black</item>    <item name="android:navigationBarColor">@android:color/black</item>    <item name="android:enforceNavigationBarContrast" tools:targetApi="q">false</item>    <item name="android:listDivider">@drawable/divider_horizontal_primary</item>    <!--Material shape attributes-->    <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.App.SmallComponent</item>    <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.App.MediumComponent</item>    <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.App.LargeComponent</item>    <!--Component styles-->    <item name="appBarLayoutStyle">@style/Widget.App.AppBarLayout</item>    <item name="toolbarStyle">@style/Widget.App.Toolbar</item>    <item name="drawerArrowStyle">@style/Widget.App.DrawerArrowToggle</item>    <item name="toolbarNavigationButtonStyle">@style/Widget.App.Toolbar.Button.Navigation.Tinted</item>    <item name="bottomNavigationStyle">@style/Widget.App.BottomNavigationView</item>    <item name="cardViewStyle">@style/Widget.App.CardView</item>    <item name="textInputStyle">@style/Widget.App.TextInputLayout</item>    <item name="editTextStyle">@style/Widget.App.TextInputEditText</item>    <item name="switchStyle">@style/Widget.App.Switch</item>    <item name="materialCalendarTheme">@style/ThemeOverlay.App.Calendar</item>    <item name="dialogTheme">@style/ThemeOverlay.App.Dialog</item></style>

Реализуем стили шрифтов

В теме приложения с material-components стандартным решением для реализации шрифтов является textAppearance. В нашем приложении используются в два раза меньше шрифтов и всего три цвета для текста. А ещё textAppearance можно описывать не все атрибуты например, там нет свойства android:lineSpacingMultiplier. Поэтому, я решил не использовать textAppearance, а использовал просто стили, которые прописывались каждому текстовому полю.

Например, мы нигде не использовали стиль Header2 и вместо него применяли унаследованный от него стиль с указанием цвета: Header2.Primary или Header2.Secondary. Такой вариант позволял сразу определить и цвет текстового поля и его шрифт.

Иногда приходилось делать исключения и использовать просто стиль, как Header2, с указанием цвета прямо в верстке экрана, например, для отображения ошибок, но таких мест всего 23 в приложении.

Стиль шрифта содержит следующие атрибуты:

  • textSize размер;

  • lineHeight межстрочный интервал, когда в текстовом поле две строки;

  • android:minHeight и android:gravity нужны, чтобы указать межстрочный интервал, когда в текстовом поле всего одна строка (да, lineHeight в таком случае игнорируется и приходится выкручиваться костылями :))

  • android:fontFamily начертание шрифта.

    Вот один из примерв описания шрифта:

<style name="Header2">    <item name="android:textSize">18sp</item>    <item name="lineHeight">24sp</item>    <item name="android:minHeight">24sp</item>    <item name="android:gravity">center_vertical</item>    <item name="android:fontFamily">@font/basis_grotesque_pro_bold</item></style><style name="Header2.Primary">    <item name="android:textColor">@color/text_primary</item></style><style name="Header2.Secondary">    <item name="android:textColor">@color/text_secondary</item></style>

Переиспользуем иконки при помощи окрашивания

Как я писал ранее, все иконки в приложении следует делать одного цвета, и потом их раскрашивать. Чтобы добавить все иконки из Figmа, вновь воспользуемся утилитой Figma-export:

./figma-export icons -i figma-export.yaml

После этого вы увидите, какие иконки удалятся, а какие добавятся или изменятся. Останется только покрасить их в нужный цвет.

В Android давно добавили поддержку перекрашивания иконок, это различные tint, но до сих пор это работает плохо и не на всех версиях Android, поэтому я написал extension для работы с drawable через drawable compat, и придерживался следующего алгоритма:

  1. Если можешь сделать tint в верстке делай tint.

  2. Если это кнопка или компонент с несколькими состояниями, то тут поможет selector.

  3. Если требуется программная смена цвета, то необходимо использовать DrawableCompat для корректной установки цвета, и обязательно нужно сделать mutate, иначе иконка закешируется, и поменяет цвет во всем приложении. Для этого я написал следующие extension-функции:

fun Drawable.withTint(context: Context, @ColorRes color: Int): Drawable {    return DrawableCompat.wrap(this).mutate().apply {        DrawableCompat.setTint(this, ContextCompat.getColor(context, color))    }}fun Int.toDrawableWithTint(context: Context, @ColorRes color: Int): Drawable {    return requireNotNull(AppCompatResources.getDrawable(context, this)).withTint(context, color)}

Добавляем иллюстрации

С добавлением иллюстраций тоже всё просто. В Figma-export есть нужная команда для их добавления в проект:

./figma-export images -i figma-export.yaml

Вызываем команду, и она добавляет иллюстрации в values и, если, вы поддерживаете тёмную тему, то и в values-night.

Реализуем стили компонентов

Я уже выше делился материалами по реализации темы приложения и стилей компонентов. Если вы их почитаете, то вопросов у вас возникнуть не должно.

Пример того, как я организовал стили компонентов кнопок в styles-button.xml
<resources>   <style name="Widget.AppTheme.Button" parent="Widget.MaterialComponents.Button.UnelevatedButton">       <item name="backgroundTint">@drawable/selector_button</item>       <item name="rippleColor">@color/button_ripple</item>       <item name="android:textAllCaps">false</item>       <item name="android:textAppearance">@style/Body1</item>       <item name="android:textColor">@color/button_text_color</item>       <item name="android:paddingStart">16dp</item>       <item name="android:paddingTop">12dp</item>       <item name="android:paddingEnd">16dp</item>       <item name="android:paddingBottom">12dp</item>       <item name="android:insetTop">0dp</item>       <item name="android:insetBottom">0dp</item>   </style>   <style name="Widget.AppTheme.Button.Secondary">       <item name="backgroundTint">@color/background_secondary</item>       <item name="rippleColor">@color/tint_ripple</item>       <item name="android:textColor">@color/button_secondary_text_color</item>   </style>   <style name="Widget.AppTheme.Button.Onboarding">       <item name="backgroundTint">@color/onboarding_button</item>       <item name="rippleColor">@color/tint_ripple</item>       <item name="android:textColor">@color/button_secondary_text_color</item>   </style>   <style name="Widget.AppTheme.Button.Accent">       <item name="backgroundTint">@color/accent</item>       <item name="rippleColor">@color/tint_ripple</item>   </style>   <style name="Widget.AppTheme.TextButton" parent="Widget.MaterialComponents.Button.TextButton">       <item name="rippleColor">@color/tint_ripple</item>       <item name="android:textAllCaps">false</item>       <item name="android:textAppearance">@style/Body1</item>       <item name="android:insetTop">0dp</item>       <item name="android:insetBottom">0dp</item>       <item name="android:paddingStart">16dp</item>       <item name="android:paddingEnd">16dp</item>   </style>   <style name="Widget.AppTheme.Button.TextButton.Dialog" parent="Widget.MaterialComponents.Button.TextButton.Dialog">       <item name="android:textAllCaps">true</item>       <item name="android:textAppearance">@style/Body1</item>       <item name="android:paddingStart">16dp</item>       <item name="android:paddingEnd">16dp</item>       <item name="rippleColor">@color/tint_ripple</item>   </style>   <style name="Widget.AppTheme.TextButton.Icon" parent="Widget.MaterialComponents.Button.TextButton.Icon">       <item name="rippleColor">@color/tint_ripple</item>       <item name="android:textAllCaps">false</item>       <item name="android:textAppearance">@style/Body1</item>       <item name="android:textColor">@color/tint</item>       <item name="android:insetTop">0dp</item>       <item name="android:insetBottom">0dp</item>       <item name="android:paddingStart">16dp</item>       <item name="android:paddingEnd">16dp</item>   </style>   <style name="Widget.AppTheme.ToolbarButton" parent="Widget.MaterialComponents.Button.TextButton">       <item name="rippleColor">@color/tint_ripple</item>       <item name="android:textAllCaps">false</item>       <item name="android:textAppearance">@style/Header1</item>       <item name="android:textColor">@color/toolbar_button_text_color</item>       <item name="android:paddingStart">16dp</item>       <item name="android:paddingEnd">16dp</item>   </style></resources>

Также стоит учитывать, что в верстке вы можете использовать просто Button или AppCompatButton, потому что есть такой компонент, как MaterialComponentsViewInflater, который автоматически будет переводить их в MaterialButton, если ваша тема наследуется от "material-components".

Вот кусочек кода из него:

@NonNull@Overrideprotected AppCompatButton createButton(    @NonNull Context context,     @NonNull AttributeSet attrs  ) {    if (shouldInflateAppCompatButton(context, attrs)) {    return new AppCompatButton(context, attrs);  }  return new MaterialButton(context, attrs);}

Как поддержать режимedge-to-edge

На этапе проектирования палитры цветов, нам очень сильно мешали цвета statusBar, да и в целом, окрашивание statusBar всегда вызывало проблемы на разных версиях Android. Раньше его цвет был равен colorPrimaryDark, а теперь Google отказались от этого варианта и рекомендуют использовать режим edge-to-edge. Кроме этого, мой OnePlus получил обновление до Android 10, поэтому я решил попробовать добавить поддержку режима edge-to-edge.

Режим edge-to-edge это новая концепция из материального дизайна, которая заключается в том, что вы отрисовываете контент под системными компонентами: statusBar и navigationBar и телефон становится визуально более безрамочным.

Отрисовка контента под statusBar позволяет добавить в ваше приложение поддержку челок и вырезов под камеру, а под navigationBar визуально улучшает работу с жестами из новой жестовой навигации в системе Android.

Для того, чтобы добавить поддержку режива edge-to-edge в ваше приложение нужно:

  1. Добавить поддержку системных отступов (insets).

  2. Активировать режим edge-to-edge для statusBar и navigationBar. По факту вам нужно сделать их прозрачными.

Добавляем поддержку Window Insets

Если простыми словами, то при работе с Window Insets, вы получаете размер системных компонентов и вставляете их как padding в верстку для ваших компонентов экрана AppBar или RootView. Insets поддерживается всеми версиями Android, что позволяет реализовать концепцию edge-to-edge для всех пользователей. Подробности можно почитать или посмотреть в докладе Константина Цховребова с AppsConf.

Сначала я использовал его решение, а потом, когда вышла новая версия библиотеки Insetter от Криса Бэйнса, перешел на неё. Вам предлагаю сразу использовать Insetter.

Также я собрал пару полезных статей, на случай, если вы захотите разобраться в этом детальнее:

Материалы по режиму edge-to-edge лежат тут и в конце статьи:

Окрашиваем statusBar и navigationBar

Эффект безрамочности в режиме edge-to-edge достигается за счет того, что вы отрисовываете контент под statusBar и navigationBar и делаете их прозрачными. При этом, нужно сохранять контрастность иконок в этих компонентах.

Тут существует одна проблема, которая находится глубоко в системе и исправить её после релиза OS уже нельзя. Это изменение цвета иконок в системных компонентах (statusBar и navigationBar) со светлого на темный. Поэтому, нужно учитывать следующие правила, в зависимости от версии Android:

  • до 6.0 версии Android иконки statusBar и navigationBar всегда светлые и перекрасить их в темный цвет нельзя. Флаг View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR доступен с 23 API. Если у вас контент всегда темного цвета, то проблем не будет. Но чтобы сохранить контрастность иконок на фоне контента, следует добавлять на системные компоненты наложение фона, например, черного фона с 50% прозрачности;

  • с версии Android 6.0 можно задать, какими будут иконки в statusBar: белыми или черными. Однако navigationBar будет вести себя как в предыдущих версиях, поэтому наложение можно убрать только для statusBar. Флаг View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR доступен с 26 API;

  • с версии Android 8.0 можно выбрать белый или черный цвет иконок для обоих компонентов. Поэтому наложения можно убрать полностью.

Я нашел интересный пример WindowPreferencesManager, который реализовывал эту логику в приложении-каталоге материальных компонентов. Но там было много лишнего и разбираться в этом, думаю, захочет не каждый, поэтому я сделал мини утилиту edge-to-edge-decorator. Она хорошо кастомизируется под ваши нужды и реализует логику окрашивания statusBar и navigationBar за вас. Подробнее про реализацию можно почитать в документации.

Пример работы библиотеки:

Android 5.0

(API level 21)

Android 7.1

(API level 25)

Android 9

(API level 28)

Android 11

(API level 30)

Добавляем тёмную тему приложения

Теперь, после того, как у вас готов UI Kit приложения и вы изучили и поддержали новый подход с использованием Material Components, можно вернуться к реализации темной темы в приложении.

Я рекомендую следовать согласно следующему алгоритму:

  1. Изучаем гайды и статьи по проектированию темной темы (ссылки лежат в конце статьи).

  2. Дизайнер готовит первый прототип и цветовую схему темной темы приложения.

  3. Создание полноценной темной палитры цветов.

  4. Добавляем альтернативную палитру цветов в приложениях и проверяем или кастомизируем тему, и стили компонентов для темной темы.

Если основная тема вашего приложения описана правильно, то добавление темной темы не создаст проблем: нужно просто добавить color.xml в values-night, как мы и планировали в самом начале (как же мы тогда ошибались :))

Сама активация темной темы хорошо описана в документации. Коротко, что нужно сделать:

1) Поменять базовую тему приложения на DayNight.

<style name="Theme.App" parent="Theme.MaterialComponents.DayNight.NoActionBar">

2) Установить нужный режим отображения через метод AppCompatDelegate.setDefaultNightMode.

В системе доступно 4 варианта темы:

  • всегда светлая: AppCompatDelegate.MODE_NIGHT_NO;

  • всегда тёмная: AppCompatDelegate.MODE_NIGHT_YES;

  • выбирается в зависимости от режима энергосбережения (Android 9 и ниже): AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY;

  • переключается в зависимости от настроек системы (Android 10 и выше): AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;

Добавляем выбор темы приложения

Светлая тема

Темная тема

После того, как вы реализовали тёмную тему, вишенкой на торте станет выбор темы в настройках приложения. Почему это важно? Потому что ресурсы values-night были добавлены ещё в API level 8, но включение темной темы на уровне системы реализовали только в Android 10. Чтобы темная тема работала у всех пользователей, необходимо добавить возможность её выбора в приложении.

Для удобного API я написал вот такой класс:
enum class NightModeType(   val customOrdinal: Int,   @NightMode val value: Int,   @StringRes val title: Int) {   MODE_NIGHT_NO(       0,       AppCompatDelegate.MODE_NIGHT_NO,       R.string.mode_night_no   ),   MODE_NIGHT_YES(       1,       AppCompatDelegate.MODE_NIGHT_YES,       R.string.mode_night_yes   ),   MODE_NIGHT_FOLLOW_SYSTEM(       2,       AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM,       R.string.mode_night_follow_system   ),   MODE_NIGHT_AUTO_BATTERY(       2,       AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY,       R.string.mode_night_auto_battery   );   companion object {       fun fromValue(@NightMode value: Int) = values().firstOrNull { it.value == value } ?: getDefaultMode()       fun fromCustomOrdinal(ordinal: Int): NightModeType {           return if (ordinal == 2) {               getDefaultMode()           } else {               values().firstOrNull { it.customOrdinal == ordinal } ?: getDefaultMode()           }       }       fun getDefaultMode(): NightModeType {           return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {               MODE_NIGHT_FOLLOW_SYSTEM           } else {               MODE_NIGHT_AUTO_BATTERY           }       }   }}
А выбор темы можно реализовать таким образом:
private fun createNightModeChooserDialog(command: ShowNightModeChooserDialog): AlertDialog {   return AlertDialog       .Builder(ContextThemeWrapper(requireContext(), R.style.ThemeOverlay_AppTheme_AlertDialog))       .apply {           setTitle(getString(R.string.item_dark_theme_text_view_title_text))           val nightModes = arrayOf(               getString(NightModeType.MODE_NIGHT_NO.title),               getString(NightModeType.MODE_NIGHT_YES.title),               getString(NightModeType.getDefaultMode().title)           )           val selectedMode = command.selectedMode.customOrdinal           setSingleChoiceItems(nightModes, selectedMode) { dialog, which ->               val nightMode = NightModeType.fromCustomOrdinal(which)               persistentStorage.saveNightMode(nightMode.value)               AppCompatDelegate.setDefaultNightMode(nightMode.value)               dialog.dismiss()           }           setNegativeButton(               getString(R.string.fragment_dialog_night_mode_chooser_button_cancel_text),               null           )       }       .create()}

И тут тоже есть проблема: выбранная пользователем тема нигде не запоминается, поэтому её необходимо сохранять. Сделать это можно вот такой проверкой в методе onCreate у вашего activity:

override fun onCreate(savedInstanceState: Bundle?) {   checkNightMode()   setTheme(R.style.AppTheme)   super.onCreate(savedInstanceState)}private fun checkNightMode() {   val savedNightModeValue = persistentStorage.getSavedNightMode(AppCompatDelegate.MODE_NIGHT_UNSPECIFIED)   val selectedNightMode = NightModeType.fromValue(savedNightModeValue)   AppCompatDelegate.setDefaultNightMode(selectedNightMode.value)}

Заключение

Вместо пары недель на реализацию тёмной темы у нас ушло три месяца. Но мы не просто сделали тёмную тему на проекте Ростелеком Ключ, но и подняли дизайн приложения на новый уровень:

  • сформировали четкий и полный UI kit в Figma;

  • автоматизировали экспорт UI kit в Figma и опубликовали утилиту figma-export;

  • правильно реализовали все базовые компоненты в Android приложении;

  • поддержали новый режим edge-to-edge, и опубликовали библиотеку edge-to-edge-decorator, которая поможет быстро добавить режим edge-to-edge на других проектах.

Так сказать, мы вышли из зоны комфорта, ради темной темы, и поправили кучу моментов в основном процессе работы :)

Материалы для глубокого изучения

Все ссылки я оформил в виде gist, поэтому можете сохранить его себе поставив звездочку.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru