Tuy Material là hệ thống thiết kế mà chúng tôi đề xuất và Jetpack Compose hỗ trợ việc triển khai Material, nhưng bạn không nhất thiết phải sử dụng Material. Material được xây dựng hoàn toàn trên các API công khai, vì vậy, bạn cũng có thể tạo hệ thống thiết kế của riêng mình theo cách tương tự.
Sau đây là một số phương pháp bạn có thể thực hiện:
- Mở rộng
MaterialTheme
bằng cách thêm giá trị giao diện bổ sung - Thay thế một hoặc nhiều hệ thống Material –
Colors
,Typography
hoặcShapes
— bằng cách triển khai các tuỳ chọn triển khai tuỳ chỉnh, trong khi vẫn duy trì các hệ thống khác - Triển khai hệ thống thiết kế tuỳ chỉnh hoàn toàn để thay thế
MaterialTheme
Bạn cũng nên tiếp tục sử dụng các thành phần Material với thiết kế tuỳ chỉnh hệ thống. Bạn có thể thực hiện việc này nhưng có một số điều cần lưu ý để phù hợp với phương pháp mà bạn đã thực hiện.
Để tìm hiểu thêm về API và cấu trúc cấp thấp hơn mà MaterialTheme
và các hệ thống thiết kế tuỳ chỉnh sử dụng, hãy tham khảo hướng dẫn Cấu tạo của một giao diện trong Compose.
Mở rộng giao diện Material
Compose Material lập mô hình Sắp xếp theo chủ đề Material một cách chặt chẽ để đảm bảo tính năng này đơn giản và an toàn về loại nhằm tuân thủ nguyên tắc của Material Design. Tuy nhiên, để mở rộng các tập hợp màu sắc, kiểu chữ và hình dạng với giá trị.
Cách đơn giản nhất là thêm thuộc tính mở rộng:
// 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)
Việc này giúp đảm bảo tính nhất quán giữa các API sử dụng MaterialTheme
. Ví dụ:
do chính Compose xác định là
surfaceColorAtElevation
!
Mã này xác định màu của bề mặt sẽ được sử dụng tuỳ thuộc vào độ cao.
Một phương pháp khác là xác định giao diện mở rộng "bao bọc" MaterialTheme
và
giá trị của nó.
Giả sử bạn muốn thêm hai màu khác — caution
và onCaution
, một
màu vàng được dùng cho những hành động bán nguy hiểm, trong khi vẫn giữ
màu hiện có trên Material:
@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 }
Điều này tương tự như các API sử dụng MaterialTheme
. Cách này cũng hỗ trợ nhiều giao diện do bạn có thể lồng các ExtendedTheme
giống như MaterialTheme
.
Sử dụng thành phần Material
Khi mở rộng phạm vi Sắp xếp theo chủ đề Material, các giá trị MaterialTheme
hiện có sẽ được duy trì và các thành phần Material vẫn có giá trị mặc định hợp lý.
Nếu bạn muốn sử dụng giá trị mở rộng trong các thành phần, hãy gói các giá trị đó vào trong riêng của bạn hàm có khả năng kết hợp, trực tiếp đặt các giá trị bạn muốn thay đổi và hiển thị các giá trị khác dưới dạng tham số cho thành phần kết hợp chứa:
@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 ) }
Sau đó, bạn cần thay thế việc sử dụng Button
bằng ExtendedButton
khi thích hợp.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
Thay thế các hệ thống phụ của Material
Thay vì mở rộng tính năng Tuỳ chỉnh giao diện Material, bạn có thể thay thế một hoặc nhiều
hệ thống – Colors
, Typography
hoặc Shapes
– với cách triển khai tuỳ chỉnh,
trong khi vẫn duy trì các lợi ích khác.
Giả sử bạn muốn thay thế hệ thống kiểu và hình dạng trong khi vẫn giữ màu hệ thống:
@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 }
Sử dụng thành phần Material
Khi một hoặc nhiều hệ thống của MaterialTheme
được thay thế, việc sử dụng các thành phần Material nguyên trạng có thể dẫn đến các giá trị, kiểu hoặc hình dạng Material không mong muốn.
Nếu bạn muốn sử dụng giá trị thay thế trong các thành phần, hãy tự gói các giá trị đó hàm có khả năng kết hợp, trực tiếp đặt giá trị cho hệ thống liên quan và hiển thị các yếu tố khác dưới dạng tham số cho thành phần kết hợp chứa tham số.
@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() } } ) }
Sau đó, bạn cần thay thế việc sử dụng Button
bằng ReplacementButton
khi thích hợp.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
Triển khai hệ thống thiết kế tuỳ chỉnh hoàn toàn
Bạn nên thay thế tính năng Tuỳ chỉnh giao diện Material bằng một hệ thống thiết kế tuỳ chỉnh hoàn toàn.
Hãy cân nhắc việc MaterialTheme
cung cấp các hệ thống sau đây:
Colors
,Typography
vàShapes
: Hệ thống giao diện MaterialTextSelectionColors
: Màu sắc đượcText
vàTextField
sử dụng để chọn văn bảnRipple
vàRippleTheme
: Cách triển khai của Material choIndication
Nếu muốn tiếp tục sử dụng các thành phần Material, bạn cần thay thế một số hệ thống này trong giao diện hoặc giao diện tuỳ chỉnh của bạn hoặc xử lý các hệ thống trong thành phần của bạn để tránh hành vi không mong muốn.
Tuy nhiên, hệ thống thiết kế không giới hạn ở các khái niệm mà Material dựa trên đó. Bạn có thể sửa đổi các hệ thống hiện có và đưa ra các hệ thống hoàn toàn mới – với các lớp (class) và kiểu (type) mới – để các khái niệm khác tương thích với giao diện của mình.
Trong mã sau, chúng tôi mô hình hoá một hệ thống màu tuỳ chỉnh bao gồm các màu chuyển màu (List<Color>
) (bao gồm cả một hệ thống kiểu) cho thấy một hệ thống nâng (elevation) mới và loại trừ các hệ thống khác do MaterialTheme
cung cấp:
@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 }
Sử dụng thành phần Material
Khi không có MaterialTheme
, việc sử dụng các thành phần Material nguyên trạng sẽ dẫn đến các giá trị màu, kiểu và hình dạng Material không mong muốn cũng như hành vi chỉ báo không mong muốn.
Nếu bạn muốn sử dụng giá trị tuỳ chỉnh trong các thành phần, hãy gói các giá trị đó vào thành phần kết hợp của riêng bạn trực tiếp đặt giá trị cho hệ thống có liên quan và hiển thị các tệp khác dưới dạng tham số cho thành phần kết hợp chứa mã.
Bạn nên truy cập vào các giá trị mà bạn đặt từ giao diện tuỳ chỉnh của mình.
Ngoài ra, nếu giao diện của bạn không cung cấp Color
, TextStyle
, Shape
hoặc
các hệ thống khác, bạn có thể mã hoá cứng chúng.
@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)
Nếu bạn đã ra mắt các loại lớp mới, chẳng hạn như List<Color>
để đại diện cho
chuyển màu — thì bạn nên triển khai các thành phần từ đầu
là gói chúng. Ví dụ: hãy xem
JetsnackButton
từ mẫu Jetsnack.
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Material Design 3 trong Compose
- Di chuyển từ Material 2 sang Material 3 trong Compose
- Cấu trúc của một giao diện trong Compose