83 lines
5.3 KiB
Markdown
83 lines
5.3 KiB
Markdown
# Спецификация 03: экран поля — макет и отрисовка
|
||
|
||
Источники: module_reqs разд. 1 (единица 5) и 2, Ф-2.1/Ф-2.2 (с
|
||
errata), ui-ux разд. 2 (glanceable, цвет дублируется текстом),
|
||
tech-req 6 п. 1. Единица 5.
|
||
|
||
## 1. Макет (resources\layouts\layout.xml)
|
||
|
||
Параметрический макет семейства квадратных MIP-экранов — якоря
|
||
(`loc="center"` и верх/низ), без единой фиксированной координаты:
|
||
позиции и шрифты вычисляются от `dc.getWidth()/getHeight()` в
|
||
`onLayout`. Один макет на всё семейство (fenix6x 280×280, fenix6
|
||
260×260, fenix6s 240×240 — добавление таргета без правок, инвариант
|
||
5).
|
||
|
||
Элементы (сверху вниз):
|
||
|
||
| Элемент | Содержимое | Шрифт |
|
||
|---|---|---|
|
||
| `zoneLbl` | подпись зоны + цветная плашка-ободок | FONT_TINY |
|
||
| `hrLbl` | текущий пульс или «—» | FONT_NUMBER_LARGE (запасной FONT_NUMBER_MEDIUM — §3) |
|
||
| `unitLbl` | «уд/мин» | FONT_TINY |
|
||
| `stateLbl` | «ОТДЫХ mm:ss» / «ПОДХОД mm:сс · ср N» | FONT_SMALL |
|
||
|
||
## 2. Содержимое и цвета
|
||
|
||
- фон — чёрный (`COLOR_BLACK`), текст — белый; MIP, читаемость на
|
||
солнце без подсветки;
|
||
- **зона**: плашка/ободок цвета зоны (`COLOR_BLUE/GREEN/YELLOW/RED`
|
||
по ZoneCalc) вокруг подписи зоны; подпись текстом — цвет не
|
||
единственный носитель (ui-ux): «восстановление»/«лёгкая»/
|
||
«средняя»/«высокая»; нет пульса → NONE: серая плашка
|
||
(`COLOR_DK_GRAY`), подпись «—»;
|
||
- **пульс**: крупное число `currentHeartRate`; null → «—»
|
||
(unitLbl гасится);
|
||
- **строка состояния**:
|
||
- идёт отдых (`RestTimer.isResting`) → «ОТДЫХ 1:48» — обратный
|
||
отсчёт остатка (mm:сс, при остатке ≥ 10 мин «мм:сс» с ведущими
|
||
разрядами не рвётся — формат `m:ss`/`mm:ss`);
|
||
- иначе → «ПОДХОД 2:05 · ср 132»: `SetTracker.elapsedMs` (время с
|
||
последнего Lap — errata Ф-2.1) и `SetTracker.avgBpm`; среднее
|
||
null → «ср —»;
|
||
- форматирование времени — чистая функция `mmss(ms)` (округление
|
||
вниз до секунд; часы не нужны — интервалы < 10 мин).
|
||
|
||
## 3. Шрифт пульса (адаптивный)
|
||
|
||
`onLayout`: берётся `FONT_NUMBER_LARGE`; если высота глифов +
|
||
прочих элементов не влезает в высоту области (мелкие страницы
|
||
данных, 2+ поля на странице) — шаг вниз до `FONT_NUMBER_MEDIUM`.
|
||
Пульс — главное число экрана (Ф-2.1: шрифт пульса максимальный из
|
||
доступных на области).
|
||
|
||
## 4. Класс `FenixDataField` (связка)
|
||
|
||
- `initialize()`: создание ядра (SetTracker/RestTimer/ZoneCalc/
|
||
Vibe/FieldSettings), начальное состояние — «нет данных»
|
||
(переменные инициализируются здесь, не в `compute` — асинхронность
|
||
compute/onUpdate по документации);
|
||
- `compute(info)`: раз в секунду — `currentHeartRate` →
|
||
`SetTracker.onSample`, кэш пульса; `RestTimer.onEnd(timerTime)`
|
||
→ лёгкая вибрация; `Vibe.onZone(zone, timerTime)` → двойная при
|
||
красной (гистерезис);
|
||
- `onTimerLap()`: `SetTracker.onLap` + `RestTimer.start` — Lap =
|
||
конец подхода = старт отдыха (errata Ф-2.3);
|
||
- `onTimerStart/Reset`: `SetTracker` к нулю, `RestTimer` не активен;
|
||
- `onUpdate(dc)`: отрисовка по кэшу (§2); данные могли ещё не
|
||
прийти — рисуем прочерки;
|
||
- `onTimerPause/Resume`: без кода — `timerTime` стоит/идёт, таймеры
|
||
замирают сами (спека 02 §3).
|
||
|
||
## 5. Симуляторный смок (критерий приёмки 2, шаг 3)
|
||
|
||
1. Поле добавляется в профиль Strength симулятора, отображается без
|
||
ошибок отрисовки;
|
||
2. симуляция пульса: число обновляется ≈1/с, плашка зоны и подпись
|
||
соответствуют порогам (прогнать пульс через все 4 зоны);
|
||
3. Lap (симулятор): метрики обнуляются, строка «ОТДЫХ …» тикает
|
||
вниз, по нулю — однократная вибрация (лог симулятора);
|
||
4. пульс в красной зоне — двойная вибрация, повтор не чаще 60 с;
|
||
5. пауза активности — отсчёты стоят; resume — продолжаются;
|
||
6. нулевой/отсутствующий пульс — «—», поле живое (не падает).
|