اگرچه Material سیستم طراحی پیشنهادی ماست و Jetpack Compose پیادهسازیای از Material را ارائه میدهد، اما شما مجبور به استفاده از آن نیستید. Material کاملاً بر اساس APIهای عمومی ساخته شده است، بنابراین میتوانید سیستم طراحی خود را به همان روش ایجاد کنید.
چندین رویکرد وجود دارد که میتوانید اتخاذ کنید:
- گسترش
MaterialThemeبا مقادیر قالببندی اضافی - جایگزینی یک یا چند سیستم متریال -
Colors،TypographyیاShapes- با پیادهسازیهای سفارشی، در حالی که سایر موارد حفظ میشوند - پیادهسازی یک سیستم طراحی کاملاً سفارشی برای جایگزینی
MaterialTheme
همچنین ممکن است بخواهید به استفاده از کامپوننتهای متریال با یک سیستم طراحی سفارشی ادامه دهید. انجام این کار امکانپذیر است، اما نکاتی وجود دارد که باید در نظر داشته باشید تا با رویکردی که در پیش گرفتهاید، مطابقت داشته باشد.
برای کسب اطلاعات بیشتر در مورد سازههای سطح پایین و APIهای مورد استفاده توسط MaterialTheme و سیستمهای طراحی سفارشی، به بخش آناتومی یک قالب در راهنمای Compose مراجعه کنید.
گسترش تم متریال
Compose 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 به صورت تو در تو قرار دهید.
از اجزای متریال استفاده کنید
هنگام گسترش قالببندی متریال، مقادیر موجود MaterialTheme حفظ میشوند و کامپوننتهای متریال همچنان مقادیر پیشفرض معقولی دارند.
اگر میخواهید از مقادیر توسعهیافته در کامپوننتها استفاده کنید، آنها را در توابع composable خودتان قرار دهید، مقادیری را که میخواهید تغییر دهید مستقیماً تنظیم کنید و مقادیر دیگر را به عنوان پارامتر در اختیار composable حاوی آنها قرار دهید:
@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 = { /* ... */ }) { /* ... */ } } }
زیرسیستمهای جایگزینی مواد
به جای گسترش قالببندی متریال، ممکن است بخواهید یک یا چند سیستم - 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 به همان صورت ممکن است منجر به مقادیر رنگ، نوع یا شکل ناخواسته در Material شود.
اگر میخواهید از مقادیر جایگزین در کامپوننتها استفاده کنید، آنها را در توابع composable خودتان قرار دهید، مقادیر را مستقیماً برای سیستم مربوطه تنظیم کنید و مقادیر دیگر را به عنوان پارامتر در اختیار composable حاوی آنها قرار دهید.
@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 = { /* ... */ }) { /* ... */ } } }
پیادهسازی یک سیستم طراحی کاملاً سفارشی
شاید بخواهید قالببندی متریال را با یک سیستم طراحی کاملاً سفارشی جایگزین کنید. در نظر داشته باشید که MaterialTheme سیستمهای زیر را ارائه میدهد:
-
Colors،TypographyوShapes: سیستمهای قالببندی متریال -
TextSelectionColors: رنگهای مورد استفاده برای انتخاب متن توسطTextوTextField -
RippleوRippleTheme: پیادهسازی مادی ازIndication
اگر میخواهید به استفاده از کامپوننتهای متریال ادامه دهید، باید برخی از این سیستمها را در قالب یا قالبهای سفارشی خود جایگزین کنید، یا سیستمهای موجود در کامپوننتهای خود را مدیریت کنید تا از رفتارهای ناخواسته جلوگیری شود.
با این حال، سیستمهای طراحی محدود به مفاهیمی نیستند که متریال به آنها متکی است. شما میتوانید سیستمهای موجود را تغییر داده و سیستمهای کاملاً جدیدی - با کلاسها و انواع جدید - معرفی کنید تا مفاهیم دیگر با قالبها سازگار شوند.
در کد زیر، ما یک سیستم رنگ سفارشی را مدلسازی میکنیم که شامل گرادیانها ( 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 به همین صورت، منجر به مقادیر و رفتار نشانگر رنگ، نوع و شکل ناخواستهی Material خواهد شد.
اگر میخواهید از مقادیر سفارشی در کامپوننتها استفاده کنید، آنها را در توابع composable خودتان قرار دهید، مقادیر را مستقیماً برای سیستم مربوطه تنظیم کنید و مقادیر دیگر را به عنوان پارامتر در اختیار composable حاوی آنها قرار دهید.
توصیه میکنیم به مقادیری که از قالب سفارشی خود تنظیم میکنید دسترسی داشته باشید. از طرف دیگر، اگر قالب شما Color ، TextStyle ، Shape یا سیستمهای دیگر را ارائه نمیدهد، میتوانید آنها را به صورت hardcode تنظیم کنید.
@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 نگاهی بیندازید.
برای شما توصیه میشود
- توجه: متن لینک زمانی نمایش داده میشود که جاوا اسکریپت غیرفعال باشد.
- طراحی متریال ۳ در Compose
- مهاجرت از متریال ۲ به متریال ۳ در Compose
- آناتومی یک تم در Compose