Jetpack Compose предлагает реализацию Material Design – комплексной системы проектирования для создания цифровых интерфейсов. Компоненты Material Design (кнопки, карточки, переключатели и т. д.) созданы на основе тематического оформления Material Design, которое позволяет систематически настраивать Material Design, чтобы лучше отражать бренд вашего продукта. Тема Material содержит атрибуты цвета, типографики и формы. Когда вы настраиваете эти атрибуты, изменения автоматически отражаются в компонентах, которые вы используете для создания приложения.
В Jetpack Compose эти концепции реализованы с помощью composable-функции MaterialTheme:
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Настройте параметры, которые вы передаете в MaterialTheme, чтобы задать тему приложения.
Цвет
Цвета моделируются в Compose с помощью класса Color, который содержит данные.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Вы можете организовать их как угодно (в виде констант верхнего уровня, в рамках синглтона или встроенных элементов), но мы настоятельно рекомендуем указать цвета в теме и извлекать их оттуда. Такой подход позволяет поддерживать тёмную тему и вложенные темы.
Compose предоставляет класс Colors для моделирования системы цветов Material. Colors предоставляет функции для создания наборов светлых или темных цветов:
private val Yellow200 = Color(0xffffeb46) private val Blue200 = Color(0xff91a4fc) // ... private val DarkColors = darkColors( primary = Yellow200, secondary = Blue200, // ... ) private val LightColors = lightColors( primary = Yellow500, primaryVariant = Yellow400, secondary = Blue700, // ... )
Определив Colors, вы можете передать их в MaterialTheme:
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
Использовать цвета темы
Вы можете получить Colors, предоставленный composable-функции MaterialTheme, с помощью MaterialTheme.colors.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
Цвет поверхности и контента
Многие компоненты принимают пару цветов: цвет фона и цвет контента.
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
Это позволяет не только задать цвет composable-функции, но и указать цвет по умолчанию для контента, то есть composable-функций, содержащихся в ней. Многие composable-функции используют этот цвет по умолчанию. Например, цвет Text основан на цвете контента родительского элемента, а Icon использует этот цвет для установки оттенка.
Метод contentColorFor() извлекает подходящий цвет для любого цвета темы. Например, если вы зададите цвет фона primary для Surface, эта функция установит onPrimary в качестве цвета контента.
Если вы задали цвет фона, не соответствующий теме, укажите подходящий цвет контента. Используйте LocalContentColor, чтобы получить предпочтительный цвет контента для текущего фона в заданной позиции в иерархии.
Альфа-канал контента
Часто вам нужно варьировать, насколько вы подчеркиваете контент, чтобы сообщить о важности и обеспечить визуальную иерархию. В рекомендациях по удобочитаемости текста в Material Design советуется использовать разные уровни непрозрачности для передачи разных уровней важности.
В Jetpack Compose для этого используется LocalContentAlpha. Вы можете указать альфа-версию контента для иерархии, задав значение для этого CompositionLocal. Вложенные composable-функции могут использовать это значение, чтобы применить альфа-обработку к своему контенту. Например, в Text и Icon по умолчанию используется комбинация LocalContentColor, настроенная для использования LocalContentAlpha. В Material Design есть стандартные значения альфа-канала (high, medium, disabled), которые моделируются объектом ContentAlpha.
// By default, both Icon & Text use the combination of LocalContentColor & // LocalContentAlpha. De-emphasize content by setting content alpha CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( // ... ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon( // ... ) Text( // ... ) }
Подробнее о CompositionLocal рассказывается в статье Локальные данные с CompositionLocal.
ContentAlpha.high. Вторая строка содержит менее важные метаданные, поэтому используется ContentAlpha.medium.Тёмная тема
В Compose светлая и тёмная темы реализуются путем передачи разных наборов Colors в composable-функцию MaterialTheme:
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
В этом примере MaterialTheme заключен в собственную composable-функцию, которая принимает параметр, указывающий, использовать ли тёмную тему. В этом случае функция получает значение по умолчанию для darkTheme, запрашивая настройку темы устройства.
Вы можете использовать следующий код, чтобы проверить, какая тема используется в Colors:
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
Наложения высот
В Material Design поверхности в тёмных темах с более высокими уровнями получают наложения уровней, которые осветляют их фон. Чем выше уровень поверхности (чем ближе она к источнику света), тем светлее она становится.
Функция Surface автоматически применяет эти наложения при использовании темных цветов, как и любая другая функция Material, использующая поверхность:
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
surface в качестве фона. Поскольку карточки и нижняя панель навигации находятся на разных уровнях высоты над фоном, они имеют немного разные цвета: карточки светлее фона, а нижняя панель навигации светлее карточек.Для пользовательских сценариев, не связанных с Surface, используйте LocalElevationOverlay – CompositionLocal, содержащий ElevationOverlay, который используется компонентами Surface:
// Elevation overlays // Implemented in Surface (and any components that use it) val color = MaterialTheme.colors.surface val elevation = 4.dp val overlaidColor = LocalElevationOverlay.current?.apply( color, elevation )
Чтобы отключить наложение высоты, укажите null в выбранной точке иерархии composable-функций:
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Ограниченное количество цветовых акцентов
В тёмных темах Material Design рекомендуется использовать ограниченное количество цветовых акцентов. В большинстве случаев лучше выбирать цвет surface, а не primary. Такие composable-функции Material, как TopAppBar и BottomNavigation, реализуют это поведение по умолчанию.
Для особых сценариев используйте свойство расширения primarySurface:
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Параметры текста
В Material Design используется система типов, которая позволяет использовать небольшое количество стилей с семантическими названиями.
Compose реализует систему типов с помощью классов Typography, TextStyle и классов, связанных со шрифтами. Конструктор Typography предлагает значения по умолчанию для каждого стиля, поэтому вы можете пропустить те, которые не хотите настраивать:
val raleway = FontFamily( Font(R.font.raleway_regular), Font(R.font.raleway_medium, FontWeight.W500), Font(R.font.raleway_semibold, FontWeight.SemiBold) ) val myTypography = Typography( h1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W300, fontSize = 96.sp ), body1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W600, fontSize = 16.sp ) /*...*/ ) MaterialTheme(typography = myTypography, /*...*/) { /*...*/ }
Если вы хотите использовать одну и ту же гарнитуру, укажите параметр defaultFontFamily и опустите fontFamily для всех элементов TextStyle:
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Как использовать стили текста
Доступ к элементам TextStyle осуществляется с помощью MaterialTheme.typography. Получите элементы TextStyle следующим образом:
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
Фигура
В Material есть система фигур, которая позволяет задавать фигуры для больших, средних и маленьких компонентов.
В Compose система фигур реализована с помощью класса Shapes, который позволяет указать CornerBasedShape для каждой категории размера:
val shapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(0f), large = CutCornerShape( topStart = 16.dp, topEnd = 0.dp, bottomEnd = 0.dp, bottomStart = 16.dp ) ) MaterialTheme(shapes = shapes, /*...*/) { /*...*/ }
Многие компоненты используют эти формы по умолчанию. Например, для Button, TextField и FloatingActionButton по умолчанию используется малый размер, для AlertDialog – средний, а для ModalDrawer – большой. Полный список сопоставлений можно найти в справочнике по схемам фигур.
Как использовать фигуры
Доступ к элементам Shape осуществляется с помощью MaterialTheme.shapes. Получите элементы Shape с помощью следующего кода:
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
Стили по умолчанию
В Compose нет эквивалента стилей по умолчанию из Android Views. Вы можете создать аналогичную функцию, создав собственные composable-функции overload, которые будут включать компоненты Material. Например, чтобы создать стиль кнопки, оберните кнопку в свою собственную composable-функцию, напрямую задав параметры, которые вы хотите или должны изменить, и предоставив другие в качестве параметров для содержащей composable-функции.
@Composable fun MyButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary ), onClick = onClick, modifier = modifier, content = content ) }
Наложения тем
Вы можете добиться того же эффекта, что и при использовании наложений тем в Android Views, если вложите друг в друга composable-функции MaterialTheme. Поскольку MaterialTheme по умолчанию использует цвета, типографику и формы из текущей темы, все остальные параметры сохраняют свои значения по умолчанию, когда тема устанавливает только один из этих параметров.
Кроме того, при переносе экранов на основе View в Compose обратите внимание на использование атрибута android:theme. Вероятно, вам нужен новый MaterialTheme в этой части дерева Compose UI.
В этом примере на экране сведений используется PinkTheme для большей части экрана, а затем BlueTheme для связанного раздела. Это показано на скриншоте и в коде ниже.
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
Состояния компонентов
Компоненты Material, с которыми можно взаимодействовать (нажимать, переключать и т. д.), могут иметь разные визуальные состояния. Состояния включают включено, отключено, нажато и т. д.
У composable-функций часто есть параметр enabled. Если задать для него значение false, взаимодействие будет невозможно, а свойства, такие как цвет и высота, изменятся, чтобы визуально передать состояние компонента.
enabled = true (слева) и enabled = false (справа).В большинстве случаев можно использовать значения по умолчанию, например для цвета и высоты. Если вам нужно настроить значения, используемые в разных состояниях, воспользуйтесь доступными классами и функциями. Рассмотрим следующий пример кнопки:
Button( onClick = { /* ... */ }, enabled = true, // Custom colors for different states colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary, disabledBackgroundColor = MaterialTheme.colors.onBackground .copy(alpha = 0.2f) .compositeOver(MaterialTheme.colors.background) // Also contentColor and disabledContentColor ), // Custom elevation for different states elevation = ButtonDefaults.elevation( defaultElevation = 8.dp, disabledElevation = 2.dp, // Also pressedElevation ) ) { /* ... */ }
enabled = true (слева) и enabled = false (справа) со скорректированными значениями цвета и высоты.Рябь
Компоненты Material Design используют рябь, чтобы показать, что с ними взаимодействуют. Если вы используете MaterialTheme в иерархии, Ripple используется как Indication по умолчанию внутри модификаторов, таких как clickable и indication.
В большинстве случаев можно использовать значение по умолчанию – Ripple. Если вам нужно настроить их внешний вид, вы можете использовать RippleTheme, чтобы изменить такие свойства, как цвет и альфа-канал.
Вы можете расширить RippleTheme и использовать служебные функции defaultRippleColor и defaultRippleAlpha. Затем вы можете указать свою тему пульсации в иерархии с помощью LocalRippleTheme:
@Composable fun MyApp() { MaterialTheme { CompositionLocalProvider( LocalRippleTheme provides SecondaryRippleTheme ) { // App content } } } @Immutable private object SecondaryRippleTheme : RippleTheme { @Composable override fun defaultColor() = RippleTheme.defaultRippleColor( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) @Composable override fun rippleAlpha() = RippleTheme.defaultRippleAlpha( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) }
RippleConfiguration.Подробнее…
Чтобы узнать больше о тематическом оформлении Material Design в Compose, ознакомьтесь со следующими дополнительными ресурсами.
Практические работы
Видео
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Специальные системы проектирования в Compose
- Переход с Material 2 на Material 3 в Compose
- Специальные возможности в Compose