Bạn có thể tạo ứng dụng theo nhiều cách bằng cách sử dụng Kiểu. Lựa chọn của bạn phụ thuộc vào mức độ áp dụng Material Design của ứng dụng:
- Hệ thống thiết kế hoàn toàn tuỳ chỉnh, không dùng Material Design
- Đề xuất: Xác định các kiểu thành phần sử dụng giá trị từ giao diện và hiển thị các tham số kiểu trên các thành phần của hệ thống thiết kế.
- Sử dụng Material Design
- Đề xuất: Chờ Material được áp dụng để tích hợp với Styles. Sử dụng kiểu trên các thành phần của riêng bạn nếu có thể.
Lớp Kiểu
Trong mô hình Compose truyền thống, hoạt động tuỳ chỉnh thường phụ thuộc nhiều vào việc ghi đè các mã thông báo chung (màu sắc và kiểu chữ) do MaterialTheme cung cấp hoặc bao bọc và ghi đè các thuộc tính của một thành phần kết hợp hệ thống thiết kế nếu có thể.
Đôi khi, có những thuộc tính trong lớp Material (Vật liệu) không được hiển thị thông qua các hệ thống con hoặc tham số, nhưng là giá trị mặc định được mã hoá cứng trên chính thành phần đó.
Với Styles API, sẽ có một lớp trừu tượng mới là cầu nối giữa các hệ thống con và thành phần: Styles.
| Lớp | Trách nhiệm | Ví dụ |
|---|---|---|
| Giá trị hệ thống con | Giá trị được đặt tên | val Primary = Color(0xFF34A85E) |
| Kiểu nguyên tử | Kiểu chỉ thay đổi một thuộc tính | val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then largeSize then interactiveShadowAtomic |
| Kiểu thành phần | Cấu hình dành riêng cho thành phần | Một nút có nền chính và khoảng đệm 16 dp. val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) } |
| Thành phần | Phần tử trên giao diện người dùng chức năng sử dụng một Kiểu. | Button(style = buttonStyle) { ... } |
Kiểu nguyên tử so với kiểu nguyên khối
Với Styles API, bạn có thể chia một Kiểu thành các kiểu riêng biệt.
Thay vì xác định các kiểu phức tạp, dành riêng cho thành phần như baseButtonStyle, bạn cũng có thể tạo các kiểu tiện ích nhỏ, có một mục đích duy nhất. Đây sẽ là "nguyên tử" của bạn.
// Define single-purpose "atomic" styles val paddingAtomic = Style { contentPadding(16.dp) } val roundedCornerShapeAtomic = Style { shape(RoundedCornerShape(8.dp)) } val primaryBackgroundAtomic = Style { background(Color.Blue) } val largeSizeAtomic = Style { size(100.dp, 40.dp) } val interactiveShadowAtomic = Style { hovered { animate { dropShadow( Shadow( offset = DpOffset( 0.dp, 0.dp ), radius = 2.dp, spread = 0.dp, color = Color.Blue, ) ) } } }
Thành phần sử dụng "then"
Một trong những tính năng mạnh mẽ của Styles API mới là toán tử then, cho phép bạn hợp nhất nhiều đối tượng Style. Điều này cho phép bạn tạo một thành phần bằng các lớp tiện ích nguyên tử.
Truyền thống (không phải nguyên tử):
// One large monolithic style val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) }
Tái cấu trúc nguyên tử:
// Combine atoms to create the final appearance val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then interactiveShadowAtomic
Áp dụng Kiểu trong hệ thống thiết kế
Hãy cân nhắc những lựa chọn sau đây khi áp dụng Kiểu trong hệ thống thiết kế của bạn, tuỳ thuộc vào vị trí của hệ thống thiết kế trong phổ.
Hệ thống thiết kế tuỳ chỉnh bằng Kiểu
Trường hợp cần cân nhắc: Bạn đã được giao một hướng dẫn toàn diện về thương hiệu không dựa trên Material Design và bạn không có ý định sử dụng Material Design.
Chiến lược: Triển khai một hệ thống thiết kế hoàn toàn tuỳ chỉnh và hiển thị các kiểu trong thành phần của giao diện.
Đây là đường dẫn tuỳ chỉnh nếu bạn không sử dụng Material làm ngôn ngữ hệ thống thiết kế chính. Bạn hoàn toàn bỏ qua MaterialTheme để có các định nghĩa trực quan và đã tạo giao diện tuỳ chỉnh của riêng mình. Bạn tạo một CompanyTheme đóng vai trò là vùng chứa cho các Kiểu của bạn.
- Cách hoạt động: Tạo một đối tượng
CompanyThemechứa các đối tượngStylecho mọi thành phần trong hệ thống của bạn. Các thành phần của bạn (trình bao bọc xung quanh logic Material hoặc các triển khaiBoxhoặcLayouttuỳ chỉnh) sẽ sử dụng trực tiếp các kiểu này và hiển thị một tham sốStylecho người dùng hệ thống thiết kế của bạn. - Lớp Kiểu: Kiểu là định nghĩa chính của hệ thống thiết kế. Mã thông báo là các biến có tên được đưa vào những kiểu này. Điều này cho phép tuỳ chỉnh sâu, chẳng hạn như xác định các ảnh động riêng biệt cho các thay đổi về trạng thái (ví dụ: tạo ảnh động cho tỷ lệ và màu sắc khi nhấn).
Nếu bạn đang tạo giao diện tuỳ chỉnh của riêng mình mà không sử dụng Material và muốn áp dụng các kiểu, hãy thêm danh sách kiểu vào Giao diện của bạn. Nhờ đó, bạn có thể truy cập vào các kiểu cơ sở từ mọi nơi trong dự án.
Tạo một lớp
Styleslưu trữ nhiều kiểu trong ứng dụng của bạn và tạo các kiểu mặc định. Ví dụ: trong ứng dụng Jetsnack, lớp này có tên làJetsnackStyles:object JetsnackStyles{ val buttonStyle: Style = Style { shape(shapes.medium) background(colors.brand) contentColor(colors.textPrimary) contentPaddingVertical(8.dp) contentPaddingHorizontal(24.dp) textStyle(typography.labelLarge) disabled { animate { background(colors.brandSecondary) } } } val cardStyle: Style = Style { shape(shapes.medium) background(colors.uiBackground) contentColor(colors.textPrimary) } }
Cung cấp
Stylestrong giao diện tổng thể của bạn và hiển thị các hàm tiện ích mở rộng trợ giúp trênStyleScopeđể truy cập vào các hệ thống con:@Immutable class JetsnackTheme( val colors: JetsnackColors = LightJetsnackColors, val typography: androidx.compose.material3.Typography = androidx.compose.material3.Typography(), val shapes: Shapes = Shapes() ) { companion object { val colors: JetsnackColors @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.colors val typography: androidx.compose.material3.Typography @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.typography val shapes: Shapes @Composable @ReadOnlyComposable get() = LocalJetsnackTheme.current.shapes val styles: JetsnackStyles = JetsnackStyles val LocalJetsnackTheme: ProvidableCompositionLocal<JetsnackTheme> get() = LocalJetsnackThemeInstance } } val StyleScope.colors: JetsnackColors get() = LocalJetsnackTheme.currentValue.colors val StyleScope.typography: androidx.compose.material3.Typography get() = LocalJetsnackTheme.currentValue.typography val StyleScope.shapes: Shapes get() = LocalJetsnackTheme.currentValue.shapes internal val LocalJetsnackThemeInstance = staticCompositionLocalOf { JetsnackTheme() } @Composable fun JetsnackTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colors = if (darkTheme) DarkJetsnackColors else LightJetsnackColors val theme = JetsnackTheme(colors = colors) CompositionLocalProvider( LocalJetsnackTheme provides theme, ) { MaterialTheme( typography = LocalJetsnackTheme.current.typography, shapes = LocalJetsnackTheme.current.shapes, content = content, ) } }
Truy cập vào
JetsnackStylestrong thành phần kết hợp của bạn:@Composable fun CustomButton(modifier: Modifier, style: Style = Style, text: String) { val interactionSource = remember { MutableInteractionSource() } val styleState = remember(interactionSource) { MutableStyleState(interactionSource) } // Apply style to top level container in combination with incoming style from parameter. Box(modifier = modifier .clickable( interactionSource = interactionSource, indication = null, enabled = true, role = Role.Button, onClick = { }, ) .styleable(styleState, JetsnackTheme.styles.buttonStyle, style)) { Text(text) } }
Ngoài việc áp dụng giao diện chung, bạn có thể sử dụng các chiến lược thay thế để kết hợp Styles vào ứng dụng của mình. Bạn có thể tận dụng Styles nội tuyến cho các trang web gọi cụ thể hoặc sử dụng các định nghĩa tĩnh khi không cần đến các chức năng tạo giao diện đầy đủ.
Styles không được hoán đổi có điều kiện trừ phi toàn bộ kiểu chữ khác biệt về cơ bản. Bạn nên ưu tiên truy cập vào các mã thông báo động bên trong một định nghĩa trực quan thay vì chuyển đổi giữa các đối tượng kiểu riêng biệt.