Архитектурные уровни Jetpack Compose

На этой странице приводится общее описание архитектурных уровней, из которых состоит Jetpack Compose, и основных принципов, лежащих в основе этой архитектуры.

Jetpack Compose – это не один монолитный проект, а набор модулей, которые вместе образуют полный стек. Знание различных модулей, из которых состоит Jetpack Compose, позволяет:

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

Слои

Основные уровни Jetpack Compose:

Рисунок 1. Основные уровни Jetpack Compose.

Каждый уровень опирается на предыдущий, объединяя функции для создания компонентов более высокого уровня. Каждый уровень использует общедоступные API более низких уровней, чтобы проверять границы модулей и позволять вам при необходимости заменять любой уровень. Рассмотрим эти уровни снизу вверх.

Время выполнения
В этом модуле рассказывается об основах среды выполнения Compose, таких как remember, mutableStateOf, аннотация @Composable и SideEffect. Вы можете использовать этот уровень, если вам нужны только возможности Compose по управлению деревом, а не его интерфейс.
Интерфейс
Уровень интерфейса состоит из нескольких модулей (ui-text, ui-graphics, ui-tooling и т. д.). Эти модули реализуют основные функции набора инструментов для создания интерфейса, такие как LayoutNode, Modifier, обработчики ввода, пользовательские макеты и рисование. Вы можете использовать этот уровень, если вам нужны только базовые концепции набора инструментов для создания интерфейса.
Фонд
Этот модуль предоставляет независимые от системы дизайна строительные блоки для Compose UI, такие как Row и Column, LazyColumn, распознавание определенных жестов и т. д. Вы можете использовать базовый уровень для создания собственной системы дизайна.
Материал
Этот модуль реализует систему Material Design для Compose UI, предоставляя систему тем, стилизованные компоненты, индикаторы пульсации и значки. Используйте этот уровень при работе с Material Design в приложении.

Принципы дизайна

Основной принцип Jetpack Compose – предоставлять небольшие, узкоспециализированные фрагменты функциональности, которые можно объединять (составлять) вместе, а не несколько монолитных компонентов. У этого подхода есть ряд преимуществ.

Управление

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

Например, если вы хотите анимировать цвет компонента, можно использовать API animateColorAsState:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

Однако если вам нужно, чтобы компонент всегда был серым, этот API не подойдет. Вместо этого можно использовать API более низкого уровня:Animatable

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

API более высокого уровня animateColorAsState создан на основе API более низкого уровня Animatable. Использование API более низкого уровня сложнее, но дает больше возможностей. Выберите уровень абстракции, который лучше всего подходит для ваших целей.

Настройка канала

Собирать компоненты более высокого уровня из небольших блоков гораздо проще, чем настраивать их по отдельности. Например, рассмотрим реализацию Button, предоставленную слоем Material:

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button состоит из четырех компонентов:

  1. Материал Surface с фоном, формой, обработкой кликов и т. д.

  2. A CompositionLocalProvider которая меняет альфа-канал контента, когда кнопка включена или отключена

  3. A ProvideTextStyle устанавливает стиль текста по умолчанию.

  4. Row задает правила макета по умолчанию для контента кнопки.

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

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

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

В приведенной выше реализации по-прежнему используются компоненты уровня Material, такие как концепции текущего альфа-канала контента и текущего стиля текста. Однако он заменяет материал Surface на Row и применяет к нему стили, чтобы получить желаемый внешний вид.

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

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

Jetpack Compose использует самые простые названия для компонентов верхнего уровня. Например,androidx.compose.material.Text основан на androidx.compose.foundation.text.BasicText. Это позволяет реализовать собственную версию с наиболее подходящим названием, если вы хотите заменить более высокие уровни.

Как выбрать подходящий уровень абстракции

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

Например, если вы хотите добавить поддержку жестов в свой компонент, вы можете создать его с нуля, используя Modifier.pointerInput, но есть и другие компоненты более высокого уровня, созданные на его основе, которые могут стать лучшим пунктом отправления, например Modifier.draggable, Modifier.scrollable или Modifier.swipeable.

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

Подробнее…

Пример создания собственной системы дизайна можно найти в образце Jetsnack.