Как преобразовать спецификации в абсолютные значения вьюшек в пикселях без использования HashMap
Что проверяют
Понимаете ли вы, как размеры из макета (dp, sp) превращаются в пиксели конкретного экрана, и умеете ли обойтись без самодельной таблицы соответствий. Иногда под «спецификациями» имеют в виду MeasureSpec из onMeasure, поэтому уместно уточнить, о чём речь.
Как отвечать
Формула одна: px = dp * displayMetrics.density, где density равна 1 на mdpi (160 dpi), 2 на xhdpi, 3 на xxhdpi. Системный способ:
val px = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics)Для текста берут COMPLEX_UNIT_SP: он учитывает системный масштаб шрифта, а с Android 14 этот масштаб нелинейный, поэтому умножать на scaledDensity вручную уже неверно.
Почему HashMap не нужен:
- ресурсы: значения из спецификации лежат в
res/values/dimens.xmlи читаются черезresources.getDimensionPixelSize(R.dimen.card_padding). Система сама переведёт в пиксели и округлит, а под планшет можно дать отдельныйvalues-sw600dp; - enum: если размеры это токены дизайн-системы (S, M, L), значение в dp хранится полем enum и переводится в px при обращении. Типобезопасно и без поиска по строковому ключу;
- Compose: конверсия встроена,
with(LocalDensity.current) { 16.dp.toPx() }.
Если речь о MeasureSpec: это int, где два старших бита хранят режим (EXACTLY, AT_MOST, UNSPECIFIED), а остальные размер. Разбирают его через MeasureSpec.getMode() и getSize(), итог считают resolveSize() и отдают в setMeasuredDimension(). Упаковка в int сделана как раз ради того, чтобы измерение не создавало объектов.
Где спотыкаются
Берут xdpi вместо density. xdpi это физическая плотность матрицы, она не совпадает с корзиной плотности, и размеры плывут от устройства к устройству.
Кэшируют пиксели один раз при старте. После смены масштаба экрана или шрифта в настройках значения устаревают, читайте их из текущих resources.
Переводят sp в px как dp и ломают доступность: пользователь с крупным шрифтом получает текст, который не увеличился.