Как установить выравнивание для пользовательских Composables в Jetpack Compose?

Вопрос или проблема

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

В общем, я стараюсь сделать код максимально чистым, модульным, легким для отладки и читаемым (много кода Jetpack выглядит некрасиво и болезненно для глаза для команды, которая не знакома с синтаксисом Kotlin), и из-за этого у меня есть привычка оборачивать компоненты пользовательского интерфейса в кастомные компоненты. Например:

@Composable
fun ColoredBackgroundTextBox(text: String, color: Color)
{
    ColoredBackgroundBox(content = {
        Text(text, modifier = Modifier.align(Alignment.Center));
    }, color = Color.Gray, width = 200, height = 30)
}

@Composable
fun ColoredBackgroundBox(content: @Composable () -> Unit, color: Color, width: Int, height: Int)
{
    Box(modifier = Modifier.size(width.dp, height.dp).background(color))
    {
        content();
    }
}

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

Проблема в том, что функция расширения Modifier.align доступна только в контексте Box, Row или Column. Как вы можете видеть, content всегда создается/вызывается внутри коробки, но Android Studio/Kotlin, похоже, “не знают” об этом. Единственный обходной путь, похоже, заключается в том, чтобы убрать посреднические функции компонентов и просто делать все непосредственно внутри Box, но это выглядит очень грубо и неэстетично, и усложняет дальнейшее модифицирование/расширение кода.

Есть ли способ просто “сказать” компилятору “эй, даже если это не выглядит так, как будто это вызывается из правильного контекста, это так, так что перестань выбрасывать ошибки и просто работай”?

Modifier.align() является ограниченным Modifier и доступен только в пределах области, в которой он был объявлен; для Box это BoxScope.

Если вы измените content : @Composable () -> Unit в ColoredBackgroundBox на content : @Composable BoxScope.() -> Unit, чтобы иметь приемник BoxScope, содержимое сможет получить доступ к модификаторам, принадлежащим BoxScope. Это также будет означать, что вы сможете вызывать content() только внутри Box.

@Composable
fun ColoredBackgroundTextBox(text: String, color: Color) {
    ColoredBackgroundBox(
        color = Color.Gray,
        width = 200,
        height = 30,
        content = {
            Text(text, modifier = Modifier.align(Alignment.Center))
        }
    )
}

@Composable
fun ColoredBackgroundBox(
    content: @Composable BoxScope.() -> Unit,
    color: Color,
    width: Int,
    height: Int,
) {
    Box(modifier = Modifier.size(width.dp, height.dp).background(color))
    {
        content()
    }
}

Используйте BoxScope.

Когда используется BoxScope, он определяет область ассоциированного компонента.

content: @Composable BoxScope.() -> Unit,

Ответ или решение

Как установить выравнивание для пользовательских Composables в Jetpack Compose

Разработка Android-приложений с использованием Jetpack Compose открывает множество возможностей для создания чистого и модульного кода. Однако некоторые сложности, такие как правильное использование модификаторов для настройки выравнивания пользовательских Composables, могут вызвать затруднения. В этой статье мы рассмотрим, как правильно применить выравнивание в ваших пользовательских компонентах.

Пример ситуации

Предположим, вы разрабатываете пользовательский компонент ColoredBackgroundTextBox, который включает в себя текст, обернутый в цветной фон. Ниже приведён пример вашего кода:

@Composable
fun ColoredBackgroundTextBox(text: String, color: Color) {
    ColoredBackgroundBox(content = {
        Text(text, modifier = Modifier.align(Alignment.Center))
    }, color = Color.Gray, width = 200, height = 30)
}

@Composable
fun ColoredBackgroundBox(content: @Composable () -> Unit, color: Color, width: Int, height: Int) {
    Box(modifier = Modifier.size(width.dp, height.dp).background(color)) {
        content()
    }
}

Проблема

Ваша проблема заключается в том, что функция Modifier.align() доступна только в контексте Box, Row или Column. Ваш компилятор не распознаёт, что content вызывается внутри Box, так как тип content не указывает, что он должен быть вызван в BoxScope.

Решение

Чтобы правильно настроить выравнивание в вашем пользовательском компоненте, измените объявление content в функции ColoredBackgroundBox так, чтобы оно принимало BoxScope:

@Composable
fun ColoredBackgroundBox(
    content: @Composable BoxScope.() -> Unit,
    color: Color,
    width: Int,
    height: Int,
) {
    Box(modifier = Modifier.size(width.dp, height.dp).background(color)) {
        content() // Здесь контент будет получен из BoxScope
    }
}

После этой модификации, способ создания вашего компонента ColoredBackgroundTextBox будет выглядеть следующим образом:

@Composable
fun ColoredBackgroundTextBox(text: String, color: Color) {
    ColoredBackgroundBox(
        color = Color.Gray,
        width = 200,
        height = 30,
        content = {
            Text(text, modifier = Modifier.align(Alignment.Center))
        }
    )
}

Почему это работает?

Используя BoxScope в качестве типа для параметра content, вы позволяете вашему Composable получить доступ к модификаторам, специфичным для Box, таким как align(). Это позволяет создать более уложенный, модульный и чистый код, что является основным приоритетом в разработке приложений.

Заключение

Применение правильного контекста для пользовательских Composables в Jetpack Compose—это ключ к созданию чистого и поддерживаемого кода. Использование BoxScope в вашей реализации, как было описано выше, позволит вам управлять выравниванием ваших компонентов из другого Composable, оставаясь в рамках заданного архитектурного подхода.

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

Оцените материал
Добавить комментарий

Капча загружается...