Архитектурное наложение 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 and 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 собирается из 4 компонентов:

  1. Материальная Surface , обеспечивающая фон, форму, обработку щелчков и т. д.

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

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

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

Мы опустили некоторые параметры и комментарии, чтобы сделать структуру более понятной, но весь компонент занимает всего около 40 строк кода, поскольку он просто собирает эти 4 компонента для реализации кнопки. Такие компоненты, как 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, такие как концепции текущего содержимого alpha и текущего стиля текста. Однако она заменяет материал 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 .

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}