По любви · Android × Figma · 12.09.2026

Сообщества и реферальная программа сошлись с макетом

Два последних раздела приложения, у которых есть макет: экран сообщества с логотипом и пилюлей видимости значка, список доступных сообществ с поиском, пустые состояния, приглашения поверх ленты — и реферальная программа: шкала приглашённых, история, подарочная подписка, снеки и карточка «Премиум в подарок». Плюс последний кадр шестёрки «Обновление и техработы» — «Технические работы» на табе профиля, который ждал собранного таба. Каждый кадр снят на устройстве в честном масштабе ×3 (1170×2532) и сведён с рендером Figma; кадры выше экрана — по частям с прокруткой. Это продолжение артефактов по кадрам S01–S10, S11–S20, S21–S30, S31–S40, S41–S50 и S51–S60.

Главная находка партии — про свечение: круг под размытием в ответе API отдаётся габаритом повёрнутой рамки, а не самой фигуры, и по нему свечение выходит втрое ярче — кадр давал 23,8 % расхождения на верной в остальном вёрстке. Вторая — про раскладку инстанса: у ячейки списка с однострочным именем дамп хранит числа двухстрочного мастера, и рендер считает по содержимому, а не по дампу. Третья — про меру: слишком длинный контур из макета не влезает в предел строки aapt, ресурс собирается обрезанным, и приложение падает — снимок при этом выходит «просто не тем».

52 / 52кадров с зелёной геометрией при пороге 0,3 %
0,000 %лучшая геометрия
0,268 %худшая геометрия
57замеров вместе с частями прокрутки
2новых экрана собрано с нуля
13ловушек найдено и записано

Смотровая

Кадр рядом с рендером

Тяните шторку, чтобы совместить макет и приложение. «Карта» показывает, где остаётся расхождение по геометрии: синим закрыты системный статус-бар, полоска home, клавиатура и диалоги разрешений iOS — их рисует Android, они в сравнении не участвуют (спека 6.4).

Рендер кадра из Figma Снимок экрана приложения

Числа

Четыре измерения на кадр

Полное — доля пикселей, отличающихся сильнее ΔE 6 вне масок. Без текста — то же с исключёнными текстовыми блоками. ±1 px — пиксель считается расхождением, только если не совпал ни с одним пикселем рендера в окне ±1: сдвиг растра на долю пикселя не есть ошибка вёрстки. Геометрия — два последних вместе, то есть чистая форма, положение и материал; по ней и выносится вердикт. Шкала показывает геометрию относительно порога 0,3 %.

ЭкранКадрСостояние Полное %Без текста % ±1 px %Геометрия % Шкала до порога 0,3 %

Разбор

Что оказалось не так

Каждая строка — расхождение из реестра ловушек спеки: что показала нода, что делало приложение и сколько это стоило в числах.

Круг под свечением меряется не по рамке

На чёрном экране сообщества три круга Brand/Terracotta размыты на 120 и 226 dp. В ответе API у повёрнутого круга absoluteBoundingBox — габарит повёрнутого прямоугольника рамки: 147,67 при круге 114. Размытие, посчитанное по рамке, светит втрое сильнее и накрывает пол-экрана.

Настоящие полуоси и угол лежат в дампе .fig (size и matrix), середину даёт сам бокс — поворот её не меняет. После правки кадр 13738:50794 ушёл с 23,8 % на 0,8 %, а подгонка σ по рендеру (tools/glow_fit.py) дала лучший множитель 0,42 при постоянной проекта 0,410 — разница в остатке 1,20 против 1,29 единицы канала.

23,8 % → 0,8 %кадр «com_main» после геометрии круга из .fig

Размывать надо альфу, а не RGBA

PIL размывает картинку покомпонентно, и цвет тянется к чёрному фону прозрачных пикселей: в центре свечения выходило (64, 36, 27) вместо (198, 111, 83) — на снимке оно было впятеро бледнее рендера при верной геометрии. Слой с одной краской размывается по альфе, цвет остаётся постоянным.

64 → 198единиц красного в центре свечения

У инстанса с переопределённым текстом дамп хранит раскладку мастера

В ячейке списка сообществ имя «Храм Христа Спасителя» занимает одну строку — и её высота в дампе верна (20). А счётчик участников под ней стоит на 640 — от двухстрочного мастера (596 + 44), и разделитель ячейки — на 674 вместо 654. Рендер считает по содержимому: счётчик на 620, разделитель на 654.

Пересчёт вертикальной стопки включается, только если сумма высот детей не сходится с высотой контейнера, и только у контейнера «по содержимому»; дети с абсолютным позиционированием (свечения и ручка шторки) в стопку не входят, ряд с переносом не считается вовсе, а высота ряда пересчитывается лишь тогда, когда внутри уже что-то поехало. Без этих четырёх ограничений пересчёт уводил содержимое на сотни dp.

−20 dpсчётчик и разделитель ячейки

Слишком длинный контур роняет приложение

Ладошка иллюстрации приглашения разбирается из .fig в 7 172 точки — 47 340 знаков pathData при пределе строки aapt 32 767. Ресурс собрался обрезанным, и Compose упал с Unknown command for: R: снимок вышел домашним экраном Android — «просто не тем кадром». Контур прорежен до 378 точек (5 072 знака), картинки до и после совпадают.

47 340 → 5 072знака в пути ладошки

Свечение внутри мордочки обрезается её контуром

У иллюстрации «две мордочки» рамка shape-brand равна габариту тела, и обрезка свечения по ней оставляла в углах светлый прямоугольник, которого в рендере нет. Маской служит альфа того же контура, нарисованного со своим поворотом.

6,0 % → 1,1 %кадр «ref_main» с мордочками

Прозрачность узла нельзя применить дважды

Тело мордочки нарисовано краской #E7C9B2 при opacity 0,9 — и ассет из .fig уже несёт её в альфе заливки. Приложение умножало на ту же прозрачность второй раз, и тело выходило на 26 единиц темнее рендера.

186 → 212единиц красного в теле мордочки

Штрих линии лежит над её координатой

Разделитель ячейки стоит на y = 244 с обводкой 1 dp «по центру», а рендер закрашивает 243…244 — проверено на шести разделителях списка и трёх строках истории приглашений. Полоса рисуется как (y − толщина, толщина).

0,9 % → 0,3 %кадр списка сообществ

Наружная обводка уходит за габарит

У пилюли «Значок видно всем» обводка 3 dp стоит снаружи: в рендере она начинается на 82 dp при узле на 85. Нарисованная внутрь, она давала тридцать тысяч расходящихся пикселей на каждом кадре экрана сообщества.

30 000 pxрасхождения от половины толщины

«Не переносить» — не значит «одна строка»

Подпись карточки набрана «дней подписки⏎получено» собственным переводом строки Figma (U+2028) при ширине «по содержимому». Запрет переноса по ширине в Compose обрезал текст до одной строки, и блок уезжал на 10 dp.

2 строкивместо одной обрезанной

Заливка панели, которой нет в рендере

У панели кадров реферальной программы заливка переопределена на Brand/Pale Beige, а рендер рисует там чёрный фон кадра. Контейнер панели (мастер 375 в кадре 390) рендер растягивает на всю ширину, и его чёрная заливка закрывает бежевую. У списка сообществ заливки у контейнера нет — и видна белая панель.

15 dpполоса справа от контейнера

Мордочек две, а имя у них одно

Иллюстрация пустого состояния на страницах V4 и V5 называется одинаково, но лица разные: на V4 грустное (бровь домиком и дуга рта вниз), на V5 — довольное. Один ассет на оба давал ровную геометрию и неверную картинку — поймала сплошная сверка кадра рядом с рендером.

2 ассетавместо одного «универсального»

Посимвольная краска там, где её не ждёшь

У ответа анкеты стрелка «→» набрана серым, а сам ответ — чёрным: в одной ноде два стиля через characterStyleOverrides. Правка нашлась на уже закрытых кадрах редактирования анкеты, когда двигатель научился собирать такие куски.

#B3ACA9краска стрелки против #514A47 у ответа

Два разных логотипа названы одинаково

В ячейке «Surf» списка сообществ логотип — росчерк, а в макете его нода называется «Union» — ровно как одиннадцать нод креста другого сообщества. Карта по имени рисовала в этой ячейке крест: кадр целиком оставался зелёным, а часть с прокруткой держала 0,340 % — два пятна на тысячу с лишним пикселей каждое. Логотип теперь берётся по адресу ноды, а не по её имени.

1401 pxпятно чужого логотипа в ячейке

Контур обводки в файле макета — двойной

У кружка значка сообщества обводка 1,5 внутрь, а в файле лежит кольцо двойной толщины по центру: так Figma хранит внутреннюю обводку и обрезает её самой фигурой при отрисовке. Резчик ассетов заливал кольцо целиком — значок выходил и крупнее, и вдвое толще. Наружная половина теперь отрезается контуром узла.

27 → 24 dpи толщина 3 → 1,5

Прозрачность, применённая дважды

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

52 %пикселей брови мимо рендера

Ряд, ставший ниже, заново центрирует детей

У ячейки списка сообществ имя занимает одну строку, а файл макета хранит раскладку двухстрочного мастера. Пересчёт стопки чинил имя и счётчик, но значок «вступить» оставался центрированным по старой высоте ряда — на 10 dp ниже, чем в рендере. Кадру это стоит 0,04 %, и метрика молчала: нашла сплошная сверка ассетов.

10 dpниже, чем в рендере, на трёх кадрах

Экраны

Два новых экрана и один слой

Экран сообщества и реферальная программа собраны тем же способом, что редактирование анкеты: кадр — список примитивов в порядке обхода ноды. Слой примитивов переехал из экрана профиля в кит и стал общим.

Сообщества — 20 кадров на три состояния

Экран сообщества (значок виден и скрыт, с соседями и без, приватное и открытое, со снеком и с системным диалогом выхода), список доступных сообществ (пустой поиск, ввод, «ничего не найдено», вступление) и пустые состояния. Логотипы сообществ — векторы из .fig: знак без круга, потому что круг рисует сама нода своей краской.

20 кадровплюс семь приглашений поверх ленты

Реферальная программа — от нуля до двадцати друзей

Четыре состояния счётчика, шкала 0…20, история приглашений, две иллюстрации (коробка с карточками «+5» и две мордочки с ладошкой), загрузка и ошибка, подарочная подписка тремя текстами и снеки «ссылка уже использована», «ты уже участвуешь», «нельзя пригласить себя».

20 кадроввместе с подарочной подпиской и снеками

Слой примитивов стал общим

Разбор кадра в примитивы (прямоугольник, значок, растр, текст) вынесен в ui/kit/NodeCanvas.kt и обслуживает три экрана и две шторки поверх ленты. В двигатель фикстур добавились повороты и отражения из матриц .fig, посимвольные краски, пересчёт устаревшей раскладки и крики о незнакомых значках, стилях и контурах — генератор больше не молчит.

3 экранана одном слое примитивов

Вопросы

Что стоит посмотреть в макете

Здесь не ошибки вёрстки, а места, где макет спорит сам с собой или с собственным рендером. Мы делаем как в рендере и передаём пару дизайнеру.

ЧтоГдеКак сейчас
Заливка панели13026:8410 и соседние переопределена на Brand/Pale Beige, а рендер рисует чёрный фон кадра: 15 dp справа от контейнера 375
Ячейка списка с коротким именем17060:59606 счётчик и разделитель стоят по числам двухстрочного мастера, рендер считает по содержимому — на 20 dp выше
Сердечки иллюстрации13180:42135 · 13180:42142 прозрачность задана дважды — заливкой (0,5) и узлом (0,6 и 0,85), у соседних сердечек её нет вовсе
Подпись шкалы13390:2948 «Пригласиеще 4 друга» — пропущен пробел
Текст шторки приглашения13738:51268 против 13738:50794 «Все сообщества приватны…» и «Сообщество приватно…» — два текста одного состояния на соседних кадрах
Два логотипа под одним именем14314:6634 против 17060:85295 росчерк «Surf» и крест «Храма» названы одинаково — «Union»: по имени их не различить, и картинку приходится брать по адресу узла