Архитектурное наложение 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 это сделать невозможно. Вместо этого можно использовать низкоуровневый Animatable API:

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, такие как концепции текущего альфа-контента и текущего стиля текста. Однако она заменяет материал 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 %}