Jetpack Compose предлагает реализацию Material Design — комплексной системы проектирования для создания цифровых интерфейсов. Компоненты Material Design (кнопки, карточки, переключатели и т. д.) создаются на основе Material Theming , который представляет собой систематический способ настройки Material Design для лучшего отражения бренда вашего продукта. Тема материала содержит атрибуты цвета , типографики и формы . Когда вы настраиваете эти атрибуты, ваши изменения автоматически отражаются в компонентах, которые вы используете для создания своего приложения.
Jetpack Compose реализует эти концепции с помощью компонуемого MaterialTheme
:
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
Настройте параметры, которые вы передаете в MaterialTheme
для оформления вашего приложения.
Рисунок 1. На первом снимке экрана показано приложение, которое не настраивает MaterialTheme
и поэтому использует стиль по умолчанию. На втором снимке экрана показано приложение, которое передает параметры в MaterialTheme
для настройки стиля.
Цвет
Цвета моделируются в Compose с помощью класса Color
, простого класса хранения данных.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
Хотя вы можете организовать их так, как вам нравится (как константы верхнего уровня, в пределах одного элемента или определенные в строке), мы настоятельно рекомендуем указывать цвета в вашей теме и получать цвета оттуда. Такой подход позволяет легко поддерживать темную тему и вложенные темы.
Рисунок 2. Цветовая система материала.
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
, предоставленные для компоновки 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), // ... ) { /* ... */ }
Это позволяет вам не только установить цвет составного объекта, но также предоставить цвет по умолчанию для содержимого и составных элементов, содержащихся в нем. Многие составные элементы используют этот цвет содержимого по умолчанию. Например, цвет Text
основывается на цвете содержимого родительского элемента, а Icon
использует этот цвет для установки своего оттенка.
Рис. 3. Установка разных цветов фона приводит к разным цветам текста и значков.
Метод contentColorFor()
извлекает соответствующий «включенный» цвет для любых цветов темы. Например, если вы установили primary
цвет фона на Surface
, он использует эту функцию, чтобы установить onPrimary
в качестве цвета содержимого. Если вы установили цвет фона, не относящийся к теме, вам также следует указать соответствующий цвет содержимого. Используйте LocalContentColor
чтобы получить предпочтительный цвет содержимого для текущего фона в заданной позиции в иерархии.
Альфа-контент
Часто вам нужно изменить степень подчеркивания контента, чтобы передать его важность и обеспечить визуальную иерархию. Рекомендации по разборчивости текста Material Design советуют использовать разные уровни непрозрачности для передачи разных уровней важности.
Jetpack Compose реализует это через LocalContentAlpha
. Вы можете указать альфу контента для иерархии , указав значение для этого CompositionLocal
. Вложенные составные элементы могут использовать это значение для применения альфа-обработки к своему содержимому. Например, Text
и Icon
по умолчанию используют комбинацию LocalContentColor
настроенную для использования LocalContentAlpha
. Материал определяет некоторые стандартные значения альфа ( 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 .
Рисунок 4. Примените к тексту различные уровни выделения, чтобы визуально передать иерархию информации. Первая строка текста — это заголовок, она содержит наиболее важную информацию и поэтому использует ContentAlpha.high
. Вторая строка содержит менее важные метаданные и поэтому использует ContentAlpha.medium
.
Темная тема
В Compose вы реализуете светлые и темные темы, предоставляя различные наборы Colors
для компонуемого MaterialTheme
:
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
В этом примере MaterialTheme
заключена в собственную составную функцию, которая принимает параметр, указывающий, использовать ли темную тему или нет. В этом случае функция получает значение по умолчанию для 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" )
Наложения высот
В «Материале» поверхности в темных темах с большей высотой получают наложения высот , которые осветляют их фон. Чем выше высота поверхности (поднимая ее ближе к предполагаемому источнику света), тем светлее становится эта поверхность.
Эти наложения применяются автоматически компонуемым элементом Surface
при использовании темных цветов, а также для любого другого компонуемого материала, в котором используется поверхность:
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }
Рисунок 5. Карточки и нижняя навигация используют цвет 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
в нужной точке составной иерархии:
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
Ограниченные цветовые акценты
Material рекомендует применять ограниченные цветовые акценты для темных тем, предпочитая в большинстве случаев использование цвета surface
primary
цвету. Компонуемые материалы, такие как TopAppBar
и BottomNavigation
реализуют это поведение по умолчанию.
Рисунок 6. Темная тема материала с ограниченным количеством цветовых акцентов. Верхняя панель приложения использует основной цвет в светлой теме и цвет поверхности в темной теме.
Для пользовательских сценариев используйте свойство расширения primarySurface
:
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
Типография
Материал определяет систему типов , поощряя вас использовать небольшое количество стилей с семантически именованными именами.
Рисунок 7. Система типов материала.
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 parameter
и опустите fontFamily
для любых элементов TextStyle
:
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
Использование стилей текста
Доступ к TextStyle
осуществляется через MaterialTheme.typography
. Получите TextStyle
следующим образом:
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )
Рисунок 8. Используйте различные шрифты и стили, чтобы выразить свой бренд.
Форма
Материал определяет систему форм , позволяющую определять формы для больших, средних и малых компонентов.
Рисунок 9. Система форм 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, /*...*/ ) { /*...*/ }
Рисунок 10. Используйте фигуры, чтобы выразить бренд или государство.
Стили по умолчанию
В разделе «Создание стилей по умолчанию из Android Views» нет эквивалентной концепции. Вы можете обеспечить аналогичную функциональность, создав свои собственные компонуемые функции «перегрузки», которые обертывают компоненты материала. Например, чтобы создать стиль кнопки, оберните кнопку в свою собственную составную функцию, напрямую задав параметры, которые вы хотите изменить, и представив другие параметры в качестве параметров для содержащего составного объекта.
@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 в Compose, вложив компонуемые элементы MaterialTheme
. Поскольку MaterialTheme
по умолчанию устанавливает цвета, типографику и формы в соответствии с текущим значением темы, если тема устанавливает только один из этих параметров, остальные параметры сохраняют свои значения по умолчанию.
Кроме того, при переносе экранов на основе просмотра в Compose следите за использованием атрибута android:theme
. Вероятно, вам понадобится новая MaterialTheme
в этой части дерева пользовательского интерфейса Compose.
В этом примере на экране сведений используется PinkTheme
для большей части экрана, а затем BlueTheme
для соответствующего раздела. Смотрите скриншот и код ниже.
Рисунок 11. Вложенные темы.
@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
Состояния компонентов
Компоненты материала, с которыми можно взаимодействовать (щелкать, переключать и т. д.), могут находиться в разных визуальных состояниях. Состояния включают включенное, отключенное, нажатое и т. д.
Компонуемые объекты часто имеют enabled
параметр. Установка значения false
предотвращает взаимодействие и изменяет такие свойства, как цвет и высота, чтобы визуально передать состояние компонента.
Рисунок 12. Кнопка с 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 ) ) { /* ... */ }
Рисунок 13. Кнопка с enabled = true
(слева) и enabled = false
(справа) с настроенными значениями цвета и высоты.
Рябь
Компоненты материала используют рябь, указывающую на то, что с ними взаимодействуют. Если вы используете MaterialTheme
в своей иерархии, Ripple
будет использоваться в качестве Indication
по умолчанию внутри модификаторов, таких как clickable
indication
.
В большинстве случаев вы можете положиться на Ripple
по умолчанию. Если вы хотите настроить их внешний вид, вы можете использовать RippleTheme
для изменения таких свойств, как цвет и альфа.
Вы можете расширить RippleTheme
и использовать служебные функции defaultRippleColor
и defaultRippleAlpha
. Затем вы можете добавить свою собственную тему Ripple в свою иерархию, используя 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 ) }
Рисунок 14. Кнопки с разными значениями пульсации, предоставленные через RippleTheme
.
Узнать больше
Чтобы узнать больше о темировании материалов в Compose, обратитесь к следующим дополнительным ресурсам.
Кодлабы
Видео
{% дословно %}Рекомендуется для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Системы индивидуального дизайна в Compose
- Миграция с Материала 2 на Материал 3 в Compose
- Доступность в Compose