Хотя Material является нашей рекомендуемой системой проектирования, а Jetpack Compose предоставляет реализацию Material, вы не обязаны ее использовать. Материал полностью построен на общедоступных API, поэтому таким же образом можно создать собственную систему дизайна.
Вы можете использовать несколько подходов:
- Расширение
MaterialTheme
дополнительными значениями тем. - Замена одной или нескольких систем материалов —
Colors
,Typography
илиShapes
— на пользовательские реализации с сохранением остальных. - Внедрение полностью настраиваемой системы дизайна для замены
MaterialTheme
Вы также можете продолжить использовать компоненты Material с собственной системой проектирования. Это возможно, но есть вещи, которые следует иметь в виду, чтобы соответствовать выбранному вами подходу.
Чтобы узнать больше о конструкциях нижнего уровня и API, используемых MaterialTheme
и системами индивидуального проектирования, ознакомьтесь с руководством «Анатомия темы» в Compose .
Расширение темы материала
Compose Material точно моделирует Material Theming, чтобы упростить и обеспечить безопасность типов следования рекомендациям по Material. Однако можно расширить наборы цветов, типографики и фигур дополнительными значениями.
Самый простой подход — добавить свойства расширения:
// Use with MaterialTheme.colorScheme.snackbarAction
val ColorScheme.snackbarAction: Color
@Composable
get() = if (isSystemInDarkTheme()) Red300 else Red700
// Use with MaterialTheme.typography.textFieldInput
val Typography.textFieldInput: TextStyle
get() = TextStyle(/* ... */)
// Use with MaterialTheme.shapes.card
val Shapes.card: Shape
get() = RoundedCornerShape(size = 20.dp)
Это обеспечивает согласованность с API-интерфейсами использования MaterialTheme
. Примером этого, определенного самим Compose, является surfaceColorAtElevation
, которая определяет цвет поверхности, который следует использовать в зависимости от отметки.
Другой подход — определить расширенную тему, которая «обертывает» MaterialTheme
и ее значения.
Предположим, вы хотите добавить два дополнительных цвета — caution
и onCaution
, желтый цвет, используемый для полуопасных действий, — сохраняя при этом существующие цвета материала:
@Immutable
data class ExtendedColors(
val caution: Color,
val onCaution: Color
)
val LocalExtendedColors = staticCompositionLocalOf {
ExtendedColors(
caution = Color.Unspecified,
onCaution = Color.Unspecified
)
}
@Composable
fun ExtendedTheme(
/* ... */
content: @Composable () -> Unit
) {
val extendedColors = ExtendedColors(
caution = Color(0xFFFFCC02),
onCaution = Color(0xFF2C2D30)
)
CompositionLocalProvider(LocalExtendedColors provides extendedColors) {
MaterialTheme(
/* colors = ..., typography = ..., shapes = ... */
content = content
)
}
}
// Use with eg. ExtendedTheme.colors.caution
object ExtendedTheme {
val colors: ExtendedColors
@Composable
get() = LocalExtendedColors.current
}
Это похоже на API использования MaterialTheme
. Он также поддерживает несколько тем, поскольку вы можете вкладывать ExtendedTheme
так же, как MaterialTheme
.
Использовать компоненты материала
При расширении Material Theming существующие значения MaterialTheme
сохраняются, а компоненты Material по-прежнему имеют разумные значения по умолчанию.
Если вы хотите использовать расширенные значения в компонентах, оберните их в свои собственные составные функции, напрямую устанавливая значения, которые вы хотите изменить, и предоставляя другие значения в качестве параметров для содержащего составного объекта:
@Composable
fun ExtendedButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Button(
colors = ButtonDefaults.buttonColors(
containerColor = ExtendedTheme.colors.caution,
contentColor = ExtendedTheme.colors.onCaution
/* Other colors use values from MaterialTheme */
),
onClick = onClick,
modifier = modifier,
content = content
)
}
Затем вы должны заменить использование Button
на ExtendedButton
, где это возможно.
@Composable
fun ExtendedApp() {
ExtendedTheme {
/*...*/
ExtendedButton(onClick = { /* ... */ }) {
/* ... */
}
}
}
Заменить подсистемы материалов
Вместо расширения Material Theming вы можете заменить одну или несколько систем — Colors
, Typography
или Shapes
— собственной реализацией, сохранив при этом остальные.
Предположим, вы хотите заменить системы типов и форм, сохранив при этом систему цветов:
@Immutable
data class ReplacementTypography(
val body: TextStyle,
val title: TextStyle
)
@Immutable
data class ReplacementShapes(
val component: Shape,
val surface: Shape
)
val LocalReplacementTypography = staticCompositionLocalOf {
ReplacementTypography(
body = TextStyle.Default,
title = TextStyle.Default
)
}
val LocalReplacementShapes = staticCompositionLocalOf {
ReplacementShapes(
component = RoundedCornerShape(ZeroCornerSize),
surface = RoundedCornerShape(ZeroCornerSize)
)
}
@Composable
fun ReplacementTheme(
/* ... */
content: @Composable () -> Unit
) {
val replacementTypography = ReplacementTypography(
body = TextStyle(fontSize = 16.sp),
title = TextStyle(fontSize = 32.sp)
)
val replacementShapes = ReplacementShapes(
component = RoundedCornerShape(percent = 50),
surface = RoundedCornerShape(size = 40.dp)
)
CompositionLocalProvider(
LocalReplacementTypography provides replacementTypography,
LocalReplacementShapes provides replacementShapes
) {
MaterialTheme(
/* colors = ... */
content = content
)
}
}
// Use with eg. ReplacementTheme.typography.body
object ReplacementTheme {
val typography: ReplacementTypography
@Composable
get() = LocalReplacementTypography.current
val shapes: ReplacementShapes
@Composable
get() = LocalReplacementShapes.current
}
Использовать компоненты материала
Если одна или несколько систем MaterialTheme
были заменены, использование компонентов Material как есть может привести к нежелательным значениям цвета, типа или формы материала.
Если вы хотите использовать замещающие значения в компонентах, оберните их в свои собственные составные функции, напрямую устанавливая значения для соответствующей системы и предоставляя другие значения в качестве параметров для содержащего составного объекта.
@Composable
fun ReplacementButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Button(
shape = ReplacementTheme.shapes.component,
onClick = onClick,
modifier = modifier,
content = {
ProvideTextStyle(
value = ReplacementTheme.typography.body
) {
content()
}
}
)
}
Затем вы должны заменить использование Button
на ReplacementButton
, где это возможно.
@Composable
fun ReplacementApp() {
ReplacementTheme {
/*...*/
ReplacementButton(onClick = { /* ... */ }) {
/* ... */
}
}
}
Внедрить полностью настраиваемую систему дизайна
Возможно, вы захотите заменить Material Theming полностью настраиваемой системой дизайна. Учтите, что MaterialTheme
предоставляет следующие системы:
-
Colors
,Typography
иShapes
: системы тем оформления материалов -
TextSelectionColors
: цвета, используемые для выделения текста с помощьюText
иTextField
-
Ripple
иRippleTheme
: материальная реализацияIndication
Если вы хотите продолжать использовать компоненты Material, вам необходимо заменить некоторые из этих систем в вашей пользовательской теме или темах или обработать системы в ваших компонентах, чтобы избежать нежелательного поведения.
Однако системы проектирования не ограничиваются концепциями, на которые опирается Material. Вы можете модифицировать существующие системы и вводить совершенно новые — с новыми классами и типами — чтобы сделать другие концепции совместимыми с темами.
В следующем коде мы моделируем пользовательскую систему цвета, включающую градиенты ( List<Color>
), включаем систему типов, вводим новую систему высот и исключаем другие системы, предоставляемые MaterialTheme
:
@Immutable
data class CustomColors(
val content: Color,
val component: Color,
val background: List<Color>
)
@Immutable
data class CustomTypography(
val body: TextStyle,
val title: TextStyle
)
@Immutable
data class CustomElevation(
val default: Dp,
val pressed: Dp
)
val LocalCustomColors = staticCompositionLocalOf {
CustomColors(
content = Color.Unspecified,
component = Color.Unspecified,
background = emptyList()
)
}
val LocalCustomTypography = staticCompositionLocalOf {
CustomTypography(
body = TextStyle.Default,
title = TextStyle.Default
)
}
val LocalCustomElevation = staticCompositionLocalOf {
CustomElevation(
default = Dp.Unspecified,
pressed = Dp.Unspecified
)
}
@Composable
fun CustomTheme(
/* ... */
content: @Composable () -> Unit
) {
val customColors = CustomColors(
content = Color(0xFFDD0D3C),
component = Color(0xFFC20029),
background = listOf(Color.White, Color(0xFFF8BBD0))
)
val customTypography = CustomTypography(
body = TextStyle(fontSize = 16.sp),
title = TextStyle(fontSize = 32.sp)
)
val customElevation = CustomElevation(
default = 4.dp,
pressed = 8.dp
)
CompositionLocalProvider(
LocalCustomColors provides customColors,
LocalCustomTypography provides customTypography,
LocalCustomElevation provides customElevation,
content = content
)
}
// Use with eg. CustomTheme.elevation.small
object CustomTheme {
val colors: CustomColors
@Composable
get() = LocalCustomColors.current
val typography: CustomTypography
@Composable
get() = LocalCustomTypography.current
val elevation: CustomElevation
@Composable
get() = LocalCustomElevation.current
}
Использовать компоненты материала
Если MaterialTheme
отсутствует, использование компонентов Material как есть приведет к нежелательным значениям цвета, типа и формы материала, а также к поведению индикации.
Если вы хотите использовать пользовательские значения в компонентах, оберните их в свои собственные составные функции, напрямую устанавливая значения для соответствующей системы и предоставляя другие значения в качестве параметров для содержащего составного объекта.
Мы рекомендуем вам получить доступ к значениям, которые вы установили из своей пользовательской темы. Альтернативно, если ваша тема не предоставляет Color
, TextStyle
, Shape
или другие системы, вы можете жестко запрограммировать их.
@Composable
fun CustomButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Button(
colors = ButtonDefaults.buttonColors(
containerColor = CustomTheme.colors.component,
contentColor = CustomTheme.colors.content,
disabledContainerColor = CustomTheme.colors.content
.copy(alpha = 0.12f)
.compositeOver(CustomTheme.colors.component),
disabledContentColor = CustomTheme.colors.content
.copy(alpha = 0.38f)
),
shape = ButtonShape,
elevation = ButtonDefaults.elevatedButtonElevation(
defaultElevation = CustomTheme.elevation.default,
pressedElevation = CustomTheme.elevation.pressed
/* disabledElevation = 0.dp */
),
onClick = onClick,
modifier = modifier,
content = {
ProvideTextStyle(
value = CustomTheme.typography.body
) {
content()
}
}
)
}
val ButtonShape = RoundedCornerShape(percent = 50)
Если вы ввели новые типы классов — например, List<Color>
для представления градиентов — тогда, возможно, лучше реализовать компоненты с нуля, а не обертывать их. В качестве примера рассмотрим JetsnackButton
из примера Jetsnack.
Пока рекомендаций нет.
Попытайтесь войти в свой аккаунт Google.