← Назад к вопросам

Как Jetpack Compose адаптируется к разным размерам экрана

1.3 Junior🔥 61 комментариев
#UI и вёрстка

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Адаптация Jetpack Compose к разным размерам экрана

Jetpack Compose, современный UI toolkit для Android, предлагает несколько ключевых подходов для создания адаптивных интерфейсов, которые корректно отображаются на устройствах с разными размерами экрана, плотностью пикселей и ориентацией. Основная философия заключается в декларативном описании того, как компонент должен себя вести при изменении доступного пространства, а не в жесткой фиксации размеров.

1. Использование доступных размеров и контейнеры-адаптеры

Compose предоставляет информацию о доступном пространстве через BoxWithConstraints и параметры Modifier.fillMaxSize() или Modifier.widthIn(min = dp). Это фундамент для адаптивности.

@Composable
fun AdaptiveBox() {
    BoxWithConstraints {
        if (maxWidth < 600.dp) {
            // Мобильная версия: вертикальный список
            Column {
                Item1()
                Item2()
            }
        } else {
            // Tablet/Desktop версия: горизонтальное расположение
            Row {
                Item1()
                Item2()
            }
        }
    }
}

Ключевые контейнеры-адаптеры:

  • BoxWithConstraints: Позволяет получить реальные ограничения (min/max ширину/высоту) для текущего composable и выбирать логику внутри.
  • ConstraintLayout: Аналог классического ConstraintLayout, позволяет создавать сложные адаптивные схемы с привязками и цепочками.
  • LazyColumn/Row: Для списков и сеток, автоматически подстраиваются под доступную высоту/ширину.

2. Адаптивные модификаторы и вычисления размеров

Вместо фиксированных dp часто используют относительные размеры и ограничения.

@Composable
fun AdaptiveButton() {
    Button(
        onClick = { /* */ },
        modifier = Modifier
            .fillMaxWidth(0.8f) // Занимает 80% доступной ширины
            .heightIn(min = 48.dp, max = 64.dp) // Ограничение по высоте
    ) {
        Text("Кнопка")
    }
}

Ключевые модификаторы для адаптивности:

  • .fillMaxWidth(fraction) / .fillMaxHeight(fraction) – занимают часть пространства.
  • .widthIn(min, max) / .heightIn(min, max) – устанавливают диапазон.
  • .aspectRatio(ratio) – фиксирует соотношение сторон (важно для изображений).

3. Работа с состояниями WindowSizeClass (Material 3)

Material Design 3 в Compose официально рекомендует использовать классификацию размеров окна для адаптивной логики. Это особенно важно для foldable устройств и планшетов.

@Composable
fun MyApp(windowSizeClass: WindowSizeClass) {
    val navController = rememberNavController()
    
    when (windowSizeClass) {
        WindowSizeClass.Compact -> {
            // Маленькие экраны: возможно, только контент
            MyNavHost(navController)
        }
        WindowSizeClass.Medium -> {
            // Средние экраны: контент + небольшая панель
            Row {
                NavigationRail { /* ... */ }
                MyNavHost(navController)
            }
        }
        WindowSizeClass.Expanded -> {
            // Большие экраны: полноценная боковая панель
            Row {
                NavigationDrawer { /* ... */ }
                MyNavHost(navController)
            }
        }
    }
}

WindowSizeClass определяет три категории: Compact (телефоны в портретной ориентации), Medium (телефоны в альбомной, небольшие планшеты), Expanded (большие планшеты, десктопные режимы).

4. Учет ориентации и плотности пикселей

Compose автоматически перекомпозирует UI при изменении ориентации, но важно управлять ресурсами:

  • Ориентация: Логика внутри BoxWithConstraints или windowSizeClass уже учитывает изменение ширины/высоты.
  • Density: Compose использует dp (density-independent pixels), которые автоматически конвертируются в физические пиксели на основе LocalDensity.current. Для изображений используйте painterResource, который учитывает плотность.
val density = LocalDensity.current
val pxValue = with(density) { 16.dp.toPx() } // Конвертация в пиксели для сложных расчетов

5. Кастомная логика с измерениями и состоянием

Для сложных случаев можно использовать custom layouts или явно измерять компоненты.

@Composable
fun TwoColumnLayout(data: List<String>) {
    Layout(content = {
        for (item in data) {
            Text(item, modifier = Modifier.padding(4.dp))
        }
    }) { measurables, constraints ->
        // Логика размещения: если ширина позволяет, два столбца
        val isSingleColumn = constraints.maxWidth < 600.dp
        // ... измерения и расположение элементов
    }
}

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

  • Тестирование: Используйте разные конфигурации в Preview.
    @Preview(widthDp = 360) // телефон портрет
    @Preview(widthDp = 640) // телефон альбом/планшет
    @Composable fun PreviewMyScreen() { MyScreen() }
    
  • Разделение логики: Создавайте отдельные composable для разных конфигураций (например, CompactScreen и ExpandedScreen).
  • Адаптивные значения: Используйте вычисления на основе LocalConfiguration.current или windowSizeClass для определения количества элементов в ряду сетки (LazyVerticalGrid) или размеров паддингов.
  • Избегайте жестких размеров: Старайтесь не использовать фиксированные dp для ключевых контейнеров.

Итог: Jetpack Compose адаптируется к размерам экрана через декларативное описание поведения UI, основанное на доступном пространстве (BoxWithConstraints), официальной классификации (WindowSizeClass), относительных модификаторах и кастомной логике измерений. Это позволяет создавать интерфейсы, которые естественно масштабируются от маленьких телефонов до больших планшетов и десктопных режимов Chrome OS.

Как Jetpack Compose адаптируется к разным размерам экрана | PrepBro