1. Trước khi bắt đầu
Material Design là một hệ thống thiết kế do những nhà thiết kế và nhà phát triển của Google xây dựng và hỗ trợ nhằm tạo ra trải nghiệm kỹ thuật số chất lượng cao cho Android, cũng như cho các nền tảng web và di động khác. Hệ thống này đưa ra hướng dẫn về cách xây dựng giao diện người dùng của ứng dụng sao cho dễ đọc, hấp dẫn và nhất quán.
Trong lớp học lập trình này, bạn sẽ tìm hiểu về cách Tuỳ chỉnh giao diện Material để sử dụng Material Design trong ứng dụng của mình, kèm theo hướng dẫn về cách tuỳ chỉnh màu sắc, kiểu chữ và hình dạng. Bạn có thể tuỳ chỉnh ít hoặc nhiều tuỳ thích cho ứng dụng của mình. Bạn cũng sẽ tìm hiểu cách thêm thanh ứng dụng trên cùng để cho thấy tên và biểu tượng của ứng dụng.
Điều kiện tiên quyết
- Làm quen với ngôn ngữ Kotlin, bao gồm cú pháp, hàm và các biến.
- Có thể tạo bố cục trong tính năng Compose, bao gồm các hàng và cột có khoảng đệm.
- Có thể tạo danh sách đơn giản trong Compose.
Kiến thức bạn sẽ học được
- Cách áp dụng tính năng Tuỳ chỉnh giao diện Material cho ứng dụng Compose.
- Cách thêm bảng màu tuỳ chỉnh vào ứng dụng của bạn.
- Cách thêm phông chữ tuỳ chỉnh vào ứng dụng của bạn.
- Cách thêm hình dạng tuỳ chỉnh vào các thành phần trong ứng dụng của bạn.
- Cách thêm thanh ứng dụng trên cùng vào ứng dụng của bạn.
Sản phẩm bạn sẽ tạo ra
- Bạn sẽ xây dựng một ứng dụng đẹp mắt kết hợp các phương pháp hay nhất về Material Design.
Bạn cần có
- Phiên bản mới nhất của Android Studio.
- Kết nối Internet để tải mã khởi đầu và phông chữ xuống.
2. Tổng quan về ứng dụng
Trong lớp học lập trình này, bạn sẽ tạo Woof, một ứng dụng cho thấy danh sách các chú chó và dùng Material Design để mang lại một trải nghiệm đẹp mắt cho ứng dụng.
Qua lớp học lập trình này, chúng ta sẽ tìm hiểu một số việc có thể thực hiện qua tính năng Tuỳ chỉnh giao diện Material. Hãy tham gia lớp học lập trình này để có ý tưởng về cách sử dụng tính năng Tuỳ chỉnh giao diện Material nhằm cải thiện giao diện các ứng dụng mà bạn sẽ tạo ra sau này.
Bảng màu
Dưới đây là các bảng màu cho cả giao diện sáng và tối mà chúng ta sẽ tạo.
Dưới đây là ứng dụng hoàn thiện trong cả giao diện sáng lẫn giao diện tối.
Giao diện sáng | Giao diện tối |
Kiểu chữ
Dưới đây là các kiểu chữ bạn sẽ sử dụng trong ứng dụng.
Tệp giao diện
Tệp Theme.kt là tệp chứa toàn bộ thông tin về giao diện (theme) của ứng dụng, được xác định thông qua màu sắc, kiểu chữ và hình dạng. Đây là tệp quan trọng mà bạn cần biết. Bên trong tệp này là thành phần kết hợp WoofTheme()
, giúp thiết lập màu sắc, kiểu chữ và hình dạng của ứng dụng.
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
setUpEdgeToEdge(view, darkTheme)
}
}
MaterialTheme(
colorScheme = colorScheme,
shapes = Shapes,
typography = Typography,
content = content
)
}
/**
* Sets up edge-to-edge for the window of this [view]. The system icon colors are set to either
* light or dark depending on whether the [darkTheme] is enabled or not.
*/
private fun setUpEdgeToEdge(view: View, darkTheme: Boolean) {
val window = (view.context as Activity).window
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.Transparent.toArgb()
val navigationBarColor = when {
Build.VERSION.SDK_INT >= 29 -> Color.Transparent.toArgb()
Build.VERSION.SDK_INT >= 26 -> Color(0xFF, 0xFF, 0xFF, 0x63).toArgb()
// Min sdk version for this app is 24, this block is for SDK versions 24 and 25
else -> Color(0x00, 0x00, 0x00, 0x50).toArgb()
}
window.navigationBarColor = navigationBarColor
val controller = WindowCompat.getInsetsController(window, view)
controller.isAppearanceLightStatusBars = !darkTheme
controller.isAppearanceLightNavigationBars = !darkTheme
}
Trong MainActivity.kt, WoofTheme()
được thêm vào để cung cấp tính năng Tuỳ chỉnh giao diện Material cho toàn bộ ứng dụng.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WoofTheme {
Surface(
modifier = Modifier.fillMaxSize()
) {
WoofApp()
}
}
}
}
}
Hãy cùng xem WoofPreview()
. WoofTheme()
được thêm vào để cung cấp tính năng Tuỳ chỉnh giao diện Material mà bạn thấy trong WoofPreview()
.
@Preview
@Composable
fun WoofPreview() {
WoofTheme(darkTheme = false) {
WoofApp()
}
}
3. Lấy đoạn mã khởi đầu
Để bắt đầu, hãy tải mã khởi đầu xuống:
Ngoài ra, bạn có thể sao chép kho lưu trữ GitHub cho mã:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git $ cd basic-android-kotlin-compose-training-woof $ git checkout starter
Bạn có thể xem đoạn mã này trong kho lưu trữ GitHub Woof app
.
Khám phá mã khởi đầu
- Mở mã khởi đầu trong Android Studio.
- Mở tệp com.example.woof > dữ liệu > Dog.kt. Mục này chứa
Dog data class
sẽ được dùng để đại diện cho ảnh, tên, tuổi và sở thích của chú chó. Bảng này cũng chứa danh sách các chú chó và thông tin mà bạn sẽ sử dụng làm dữ liệu trong ứng dụng của mình. - Mở res > đối tượng có thể vẽ được. Đối tượng này chứa tất cả các thành phần hình ảnh mà bạn cần cho dự án này, bao gồm biểu tượng ứng dụng, hình ảnh và biểu tượng của chó.
- Mở res > values > strings.xml. (tài nguyên > giá trị > strings.xml.) Tệp này chứa các chuỗi bạn sẽ sử dụng trong ứng dụng, bao gồm tên ứng dụng, tên chó, nội dung mô tả và các chuỗi khác.
- Mở tệp MainActivity.kt. Tệp này chứa đoạn mã để tạo một danh sách đơn giản cho thấy hình ảnh, tên và tuổi của một chú chó.
WoofApp()
chứaLazyColumn
cho thấy cácDogItem
.DogItem()
chứaRow
cho thấy hình ảnh và thông tin về chú chó.DogIcon()
hiển thị ảnh của chú chó.DogInformation()
chứa tên và tuổi của chú chó.WoofPreview()
cho phép bạn xem trước ứng dụng trong ngăn Design (Thiết kế).
Đảm bảo trình mô phỏng/thiết bị ở giao diện sáng
Ở lớp học lập trình này, bạn sẽ tìm hiểu cả giao diện sáng lẫn tối, tuy nhiên, hầu hết các lớp học lập trình đều chỉ tập trung vào giao diện sáng. Trước khi bắt đầu, hãy đảm bảo thiết bị/trình mô phỏng của bạn ở giao diện sáng.
Để xem ứng dụng của bạn ở giao diện sáng, trên trình mô phỏng hoặc thiết bị thực, hãy thao tác như sau:
- Chuyển đến ứng dụng Cài đặt trên thiết bị.
- Tìm Giao diện tối rồi nhấp vào giao diện đó.
- Nếu Giao diện tối đang bật, hãy tắt chế độ này.
Chạy mã khởi đầu để xem bạn đang bắt đầu từ đâu; đó là một danh sách hiển thị các chú chó cùng với hình ảnh, tên và tuổi của chúng. Chức năng này hoạt động nhưng trông không ổn lắm, thế nên chúng ta sẽ khắc phục việc đó.
4. Thêm màu sắc
Thành phần đầu tiên bạn cần sửa đổi trong ứng dụng Woof là bảng phối màu.
Bảng phối màu là tổ hợp màu mà ứng dụng của bạn sử dụng. Mỗi kiểu kết hợp màu sắc lại gợi lên tâm trạng riêng, điều này ảnh hưởng đến cảm nhận của mọi người khi họ sử dụng ứng dụng của bạn.
Màu trong hệ thống Android được biểu thị bằng giá trị màu thập lục phân (mã màu hex). Mã màu hex bắt đầu bằng ký tự dấu thăng (#), theo sau là 6 chữ cái và/hoặc số đại diện cho các thành phần màu đỏ, xanh lục và xanh dương (RGB) của màu đó. Hai chữ cái/số đầu tiên đề cập đến màu đỏ, hai chữ cái tiếp theo đề cập đến màu xanh lục và hai chữ cái cuối cùng đề cập đến màu xanh lam.
Một màu cũng có thể bao gồm giá trị alpha — chữ cái và/hoặc số — thể hiện độ trong suốt của màu (#00 là độ mờ 0% (trong suốt hoàn toàn), #FF là độ mờ 100% (mờ hoàn toàn). Khi được đưa vào, giá trị alpha là 2 ký tự đầu tiên của mã màu hex sau ký tự dấu thăng (#). Nếu giá trị alpha không được đưa vào thì giá trị này được giả định là #FF, nghĩa là độ mờ 100% (mờ hoàn toàn).
Dưới đây là một số ví dụ về màu và giá trị hex tương ứng.
Sử dụng Material Theme Builder để tạo bảng phối màu
Để tạo bảng phối màu tuỳ chỉnh cho ứng dụng, chúng ta sẽ sử dụng Material Theme Builder (Trình tạo giao diện Material).
- Nhấp vào đường liên kết này để chuyển đến Material Theme Builder (Trình tạo giao diện Material).
- Trên ngăn bên trái, bạn sẽ thấy Core Colors (Màu cốt lõi), hãy nhấp vào Primary (Màu chính):
- Công cụ chọn màu HCT sẽ mở ra.
- Để tạo bảng phối màu như trong ảnh chụp màn hình ứng dụng, bạn sẽ thay đổi màu chính trong công cụ chọn màu này. Trong hộp văn bản, hãy thay thế văn bản hiện tại bằng #006C4C. Thao tác này sẽ thiết lập màu chính của ứng dụng là màu xanh lục.
Hãy lưu ý cách tính năng này cập nhật các ứng dụng trên màn hình để áp dụng bảng phối màu xanh lục.
- Di chuyển xuống dưới trang là bạn sẽ thấy bảng phối màu đầy đủ cho giao diện sáng và tối được tạo từ màu mà bạn đã nhập.
Có thể bạn sẽ thắc mắc về những vai trò này và cách sử dụng những vai trò này, sau đây là một số vai trò chính:
- Màu primary (chính) dùng cho các thành phần chính trên giao diện người dùng.
- Màu secondary (phụ) dùng cho các thành phần ít nổi bật hơn trong giao diện người dùng.
- Màu tertiary (thứ cấp) dùng cho các điểm nhấn tương phản có thể dùng để cân bằng màu chính và màu phụ hoặc gây sự chú ý đến một thành phần cụ thê, chẳng hạn như trường nhập.
- Các phần tử màu on xuất hiện ở trên các màu khác trong bảng màu và chủ yếu áp dụng cho văn bản, biểu tượng và nét vẽ. Trong bảng màu, chúng ta có một màu onSurface xuất hiện ở trên màu surface, còn màu onPrimary xuất hiện ở trên màu primary.
Tất cả cùng tạo ra một hệ thống thiết kế thống nhất, nơi các thành phần liên quan được tô màu tương đồng với nhau.
Vậy là đủ lý thuyết về màu sắc — đã đến lúc thêm bảng màu đẹp mắt này vào ứng dụng!
Thêm bảng màu vào giao diện
Trên trang Material Theme Builder (Trình tạo giao diện Material), bạn có thể nhấp vào nút Export (Xuất) để tải tệp Color.kt và tệp Theme.kt xuống giao diện tuỳ chỉnh đã tạo trong Trình tạo giao diện.
Thao tác này sẽ thực hiện việc thêm giao diện tuỳ chỉnh mà chúng ta tạo vào ứng dụng của bạn. Tuy nhiên, vì tệp Theme.kt đã tạo không chứa đoạn mã cho màu động mà sau đó chúng ta sẽ đề cập đến trong lớp học lập trình này, hãy sao chép các tệp đó.
- Mở tệp Color.kt rồi thay thế nội dung bằng đoạn mã dưới đây để sao chép trong bảng phối màu mới.
package com.example.woof.ui.theme
import androidx.compose.ui.graphics.Color
val md_theme_light_primary = Color(0xFF006C4C)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFF89F8C7)
val md_theme_light_onPrimaryContainer = Color(0xFF002114)
val md_theme_light_secondary = Color(0xFF4D6357)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFCFE9D9)
val md_theme_light_onSecondaryContainer = Color(0xFF092016)
val md_theme_light_tertiary = Color(0xFF3D6373)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFC1E8FB)
val md_theme_light_onTertiaryContainer = Color(0xFF001F29)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFBFDF9)
val md_theme_light_onBackground = Color(0xFF191C1A)
val md_theme_light_surface = Color(0xFFFBFDF9)
val md_theme_light_onSurface = Color(0xFF191C1A)
val md_theme_light_surfaceVariant = Color(0xFFDBE5DD)
val md_theme_light_onSurfaceVariant = Color(0xFF404943)
val md_theme_light_outline = Color(0xFF707973)
val md_theme_light_inverseOnSurface = Color(0xFFEFF1ED)
val md_theme_light_inverseSurface = Color(0xFF2E312F)
val md_theme_light_inversePrimary = Color(0xFF6CDBAC)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF006C4C)
val md_theme_light_outlineVariant = Color(0xFFBFC9C2)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFF6CDBAC)
val md_theme_dark_onPrimary = Color(0xFF003826)
val md_theme_dark_primaryContainer = Color(0xFF005138)
val md_theme_dark_onPrimaryContainer = Color(0xFF89F8C7)
val md_theme_dark_secondary = Color(0xFFB3CCBE)
val md_theme_dark_onSecondary = Color(0xFF1F352A)
val md_theme_dark_secondaryContainer = Color(0xFF354B40)
val md_theme_dark_onSecondaryContainer = Color(0xFFCFE9D9)
val md_theme_dark_tertiary = Color(0xFFA5CCDF)
val md_theme_dark_onTertiary = Color(0xFF073543)
val md_theme_dark_tertiaryContainer = Color(0xFF244C5B)
val md_theme_dark_onTertiaryContainer = Color(0xFFC1E8FB)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF191C1A)
val md_theme_dark_onBackground = Color(0xFFE1E3DF)
val md_theme_dark_surface = Color(0xFF191C1A)
val md_theme_dark_onSurface = Color(0xFFE1E3DF)
val md_theme_dark_surfaceVariant = Color(0xFF404943)
val md_theme_dark_onSurfaceVariant = Color(0xFFBFC9C2)
val md_theme_dark_outline = Color(0xFF8A938C)
val md_theme_dark_inverseOnSurface = Color(0xFF191C1A)
val md_theme_dark_inverseSurface = Color(0xFFE1E3DF)
val md_theme_dark_inversePrimary = Color(0xFF006C4C)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFF6CDBAC)
val md_theme_dark_outlineVariant = Color(0xFF404943)
val md_theme_dark_scrim = Color(0xFF000000)
- Mở tệp Theme.kt rồi thay nội dung bằng đoạn mã dưới đây để thêm các màu mới vào giao diện.
package com.example.woof.ui.theme
import android.app.Activity
import android.os.Build
import android.view.View
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
setUpEdgeToEdge(view, darkTheme)
}
}
MaterialTheme(
colorScheme = colorScheme,
shapes = Shapes,
typography = Typography,
content = content
)
}
/**
* Sets up edge-to-edge for the window of this [view]. The system icon colors are set to either
* light or dark depending on whether the [darkTheme] is enabled or not.
*/
private fun setUpEdgeToEdge(view: View, darkTheme: Boolean) {
val window = (view.context as Activity).window
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.Transparent.toArgb()
val navigationBarColor = when {
Build.VERSION.SDK_INT >= 29 -> Color.Transparent.toArgb()
Build.VERSION.SDK_INT >= 26 -> Color(0xFF, 0xFF, 0xFF, 0x63).toArgb()
// Min sdk version for this app is 24, this block is for SDK versions 24 and 25
else -> Color(0x00, 0x00, 0x00, 0x50).toArgb()
}
window.navigationBarColor = navigationBarColor
val controller = WindowCompat.getInsetsController(window, view)
controller.isAppearanceLightStatusBars = !darkTheme
controller.isAppearanceLightNavigationBars = !darkTheme
}
Trong WoofTheme()
, colorScheme val
sẽ sử dụng câu lệnh when
- Nếu giá trị
dynamicColor
là true và phiên bản của bản dựng là S trở lên, thì hệ thống sẽ kiểm tra xem thiết bị có đang ởdarkTheme
hay không. - Nếu thiết bị ở giao diện tối,
colorScheme
sẽ được thiết lập thànhdynamicDarkColorScheme
. - Nếu không ở giao diện tối thì giá trị này sẽ được thiết lập thành
dynamicLightColorScheme
. - Nếu ứng dụng không sử dụng
dynamicColorScheme
, hệ thống sẽ kiểm tra xem ứng dụng có đang ởdarkTheme
không. Nếu có,colorScheme
sẽ được thiết lập thànhDarkColors
. - Nếu không có kết quả nào đúng, thì
colorScheme
sẽ được thiết lập thànhLightColors
.
Nội dung được sao chép trong tệp Theme.kt có giá trị dynamicColor
được thiết lập thành false và các thiết bị mà chúng ta đang xử lý đang ở chế độ sáng, nên colorScheme
sẽ được thiết lập thành LightColors
.
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
- Chạy lại ứng dụng, bạn có thể thấy thanh ứng dụng đã tự động thay đổi màu.
Ánh xạ màu
Các thành phần Material sẽ được tự động ánh xạ tới ô màu. Các thành phần chính khác trên giao diện người dùng như Nút hành động nổi cũng được thiết lập thành màu chính theo mặc định. Tức là bạn không cần chỉ định rõ ràng màu cho một thành phần; nó sẽ tự động được liên kết với ô màu khi bạn thiết lập chủ đề màu sắc trong ứng dụng của mình. Bạn có thể ghi đè giá trị này bằng cách thiết lập rõ ràng một màu sắc trong đoạn mã. Đọc thêm về vai trò của màu sắc tại đây.
Trong phần này, chúng ta sẽ gói Row
chứa DogIcon()
và DogInformation()
bằng Card
để phân biệt màu của mục trong danh sách với màu nền.
- Trong hàm có khả năng kết hợp
DogItem()
, hãy góiRow()
bằng mộtCard()
.
Card() {
Row(
modifier = modifier
.fillMaxWidth()
.padding(dimensionResource(id = R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
}
}
- Do
Card
hiện là thành phần kết hợp con đầu tiên trongDogItem()
, hãy truyền đối tượng sửa đổi từDogItem()
sangCard
, rồi cập nhật đối tượng sửa đổi củaRow
thành một thực thể mới củaModifier
.
Card(modifier = modifier) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(id = R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
}
}
- Hãy xem tệp
WoofPreview()
. Các mục trong danh sách hiện đã tự động thay đổi màu do các thành phần kết hợpCard
. Màu sắc trông đẹp, nhưng không có khoảng cách giữa các mục trong danh sách.
Tệp kích thước
Giống như việc bạn sử dụng strings.xml để lưu trữ các chuỗi trong ứng dụng, bạn cũng nên sử dụng tệp dimens.xml để lưu trữ các giá trị kích thước. Cách này rất hữu ích để bạn không phải cố định các giá trị trong đoạn mã. Vì vậy, nếu cần, bạn có thể thay đổi chúng ở một nơi duy nhất.
Chuyển đến app > res > values > dimens.xml rồi xem tệp. Tệp này lưu trữ các giá trị kích thước cho padding_small
, padding_medium
và image_size
. Các tham số này sẽ được sử dụng trên toàn ứng dụng.
<resources>
<dimen name="padding_small">8dp</dimen>
<dimen name="padding_medium">16dp</dimen>
<dimen name="image_size">64dp</dimen>
</resources>
Để thêm giá trị từ tệp dimens.xml, hãy định dạng đúng như sau:
Ví dụ: để thêm padding_small
, bạn sẽ truyền vào dimensionResource(id = R.dimen.
padding_small
)
.
- Trong
WoofApp()
, hãy thêmmodifier
cùng vớipadding_small
trong lệnh gọi đếnDogItem()
.
@Composable
fun WoofApp() {
Scaffold { it ->
LazyColumn(contentPadding = it) {
items(dogs) {
DogItem(
dog = it,
modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
)
}
}
}
}
Trong WoofPreview()
, giờ đây sẽ có thêm định nghĩa giữa các mục danh sách.
Giao diện tối
Trong hệ thống Android, bạn có thể chuyển đổi thiết bị sang giao diện tối. Giao diện tối sử dụng màu tối hơn, dịu hơn và:
- Có thể làm giảm đáng kể mức sử dụng pin (tuỳ vào công nghệ màn hình của thiết bị).
- Giúp người dùng có thị lực kém và những người nhạy cảm với ánh sáng mạnh dễ nhìn hơn.
- Giúp mọi người dễ dàng sử dụng thiết bị trong môi trường ánh sáng yếu.
Ứng dụng của bạn có thể chọn Bật nhanh giao diện tối, nghĩa là hệ thống sẽ triển khai giao diện tối cho bạn. Tuy nhiên, người dùng sẽ có trải nghiệm tốt hơn nếu bạn triển khai giao diện tối. Theo đó, bạn vẫn có toàn quyền kiểm soát đối với giao diện ứng dụng.
Khi bạn chọn giao diện tối, điều quan trọng cần lưu ý là màu sắc cần phải đáp ứng các tiêu chuẩn về độ tương phản cùng với tính năng hỗ trợ tiếp cận. Giao diện tối sử dụng một màu tối cho bề mặt, có các điểm nhấn màu sắc hạn chế.
Xem giao diện tối trong bản xem trước
Bạn đã thêm màu cho giao diện tối trong bước trước. Để xem giao diện tối đang hoạt động, bạn sẽ thêm một thành phần kết hợp Xem trước khác vào MainActivity.kt. Nhờ đó, khi thay đổi bố cục giao diện người dùng trong mã của mình, bạn có thể thử bản xem trước của giao diện sáng và giao diện tối cùng lúc.
- Trong phần
WoofPreview()
, hãy tạo một hàm mới có tên làWoofDarkThemePreview()
và chú thích hàm này bằng@Preview
và@Composable
.
@Preview
@Composable
fun WoofDarkThemePreview() {
}
- Bên trong
DarkThemePreview()
, hãy thêmWoofTheme()
. Nếu không thêmWoofTheme()
, bạn sẽ không thấy kiểu nào chúng tôi đã thêm trong ứng dụng. Đặt tham sốdarkTheme
thành giá trị true.
@Preview
@Composable
fun WoofDarkThemePreview() {
WoofTheme(darkTheme = true) {
}
}
- Gọi
WoofApp()
bên trongWoofTheme()
.
@Preview
@Composable
fun WoofDarkThemePreview() {
WoofTheme(darkTheme = true) {
WoofApp()
}
}
Bây giờ, hãy di chuyển xuống ngăn Design (Thiết kế) để xem ứng dụng ở giao diện tối, bao gồm cả nền ứng dụng/danh sách mục có màu tối hơn và văn bản sáng hơn. So sánh sự khác biệt giữa giao diện tối và sáng.
Giao diện tối | Giao diện sáng |
Xem giao diện tối trên thiết bị hoặc trình mô phỏng
Để xem ứng dụng của bạn ở giao diện tối, trên trình mô phỏng hoặc thiết bị thực, bạn hãy thao tác như sau:
- Chuyển đến ứng dụng Cài đặt trên thiết bị.
- Tìm Giao diện tối, rồi nhấp vào giao diện đó.
- Bật Giao diện tối.
- Mở lại ứng dụng Woof và ứng dụng này sẽ ở giao diện tối.
Lớp học lập trình này tập trung nhiều hơn vào giao diện sáng. Vì vậy, trước khi tiếp tục dùng ứng dụng, hãy tắt giao diện tối.
- Chuyển đến ứng dụng Cài đặt trên thiết bị.
- Chọn Hiển thị.
- Tắt Giao diện tối.
So sánh giao diện hiện tại của ứng dụng so với ban đầu. Các mục và văn bản trong danh sách được xác định rõ hơn, đồng thời, bảng phối màu cũng bắt mắt hơn.
Không màu | Có màu (giao diện sáng) | Có màu (giao diện tối) |
Màu động
Material 3 tập trung chủ yếu vào hoạt động cá nhân hoá của người dùng. Một tính năng mới trong Material 3 là Màu động – tạo một giao diện cho ứng dụng dựa trên hình nền của người dùng. Theo đó, nếu người dùng yêu thích màu xanh lục và có nền điện thoại màu xanh dương, thì ứng dụng Woof cũng sẽ có màu xanh dương để phản ánh điều đó. Tính năng tuỳ chỉnh giao diện động chỉ có trên một số thiết bị chạy Android 12 trở lên.
Bạn có thể dùng giao diện tuỳ chỉnh cho các ứng dụng có màu sắc mang đặc trưng thương hiệu mạnh và cũng cần triển khai giao diện này cho các thiết bị không hỗ trợ tuỳ chỉnh giao diện động để ứng dụng vẫn được tuỳ chỉnh giao diện.
- Để bật tính năng màu động, hãy mở tệp Theme.kt, chuyển đến thành phần kết hợp
WoofTheme()
rồi thiết lập tham sốdynamicColor
thành true.
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
)
- Để thay đổi nền của thiết bị hoặc trình mô phỏng, hãy chuyển đến phần Cài đặt rồi tìm kiếm Hình nền.
- Thay đổi hình nền thành một màu hoặc một bộ màu.
- Chạy lại ứng dụng của bạn để xem giao diện động (lưu ý rằng thiết bị hoặc trình mô phỏng của bạn phải chạy Android 12 trở lên để xem được màu động) rồi thoải mái thay đổi các hình nền khác!
- Lớp học lập trình này tập trung vào việc tuỳ chỉnh giao diện, vì vậy, hãy tắt
dynamicColor
trước khi bạn tiếp tục.
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = false,
content: @Composable () -> Unit
)
5. Thêm hình dạng
Việc áp dụng hình dạng có thể làm thay đổi giao diện của thành phần kết hợp rất nhiều. Hình dạng thường chi phối sự chú ý, cách xác định các thành phần, trạng thái giao tiếp và thể hiện thương hiệu.
Nhiều hình dạng được xác định bằng cách sử dụng RoundedCornerShape
để mô tả một hình chữ nhật có góc được bo tròn. Số được truyền vào sẽ xác định độ tròn của các góc. Nếu bạn dùng RoundedCornerShape(0.dp)
, hình chữ nhật đó sẽ không có góc tròn; nếu bạn dùng RoundedCornerShape(50.dp)
, các góc sẽ hoàn toàn tròn.
0.dp | 25.dp | 50.dp |
Bạn cũng có thể tuỳ chỉnh hình dạng hơn nữa bằng cách thêm tỷ lệ bo tròn cho từng góc. Sẽ rất thú vị khi thay đổi các hình dạng!
Trên cùng bên trái: 50.dp | Trên cùng bên trái: 15.dp | Trên cùng bên trái: 0.dp |
Tệp Shape.kt dùng để xác định hình dạng của các thành phần trong Compose. Có 3 loại thành phần: nhỏ, trung bình và lớn. Trong phần này, bạn sẽ sửa đổi thành phần Card
, được xác định là kích thước medium
. Các thành phần được nhóm thành danh mục hình dạng dựa trên kích thước của các thành phần đó.
Trong phần này, bạn sẽ định dạng hình ảnh của chú chó thành một hình tròn và sửa đổi hình dạng của mục trong danh sách.
Sửa hình ảnh chú chó thành hình tròn
- Hãy mở tệp Shape.kt và để ý rằng tham số nhỏ được thiết lập thành
RoundedCornerShape(50.dp)
. Tham số này sẽ được sử dụng để tạo hình ảnh thành một vòng tròn.
val Shapes = Shapes(
small = RoundedCornerShape(50.dp),
)
- Mở tệp MainActivity.kt. Trong
DogIcon()
, hãy thêm thuộc tínhclip
vàomodifier
củaImage
; thao tác này sẽ cắt hình ảnh theo một hình dạng. TruyềnMaterialTheme.shapes.small
vào.
import androidx.compose.ui.draw.clip
@Composable
fun DogIcon(
@DrawableRes dogIcon: Int,
modifier: Modifier = Modifier
) {
Image(
modifier = modifier
.size(dimensionResource(id = R.dimen.image_size))
.padding(dimensionResource(id = R.dimen.padding_small))
.clip(MaterialTheme.shapes.small),
Khi nhìn vào WoofPreview()
, bạn sẽ thấy các biểu tượng chú chó có hình tròn! Tuy nhiên, một số ảnh bị cắt bớt ở hai bên và không có dạng tròn hoàn toàn.
- Để sửa tất cả ảnh thành hình tròn, hãy thêm thuộc tính
ContentScale
vàCrop
. Thao tác này sẽ cắt hình ảnh cho vừa vặn trong khung hình tròn. Xin lưu ýcontentScale
là một thuộc tính củaImage
chứ không phải củamodifier
.
import androidx.compose.ui.layout.ContentScale
@Composable
fun DogIcon(
@DrawableRes dogIcon: Int,
modifier: Modifier = Modifier
) {
Image(
modifier = modifier
.size(dimensionResource(id = R.dimen.image_size))
.padding(dimensionResource(id = R.dimen.padding_small))
.clip(MaterialTheme.shapes.small),
contentScale = ContentScale.Crop,
Đây là thành phần kết hợp DogIcon()
đầy đủ.
@Composable
fun DogIcon(
@DrawableRes dogIcon: Int,
modifier: Modifier = Modifier
) {
Image(
modifier = modifier
.size(dimensionResource(R.dimen.image_size))
.padding(dimensionResource(R.dimen.padding_small))
.clip(MaterialTheme.shapes.small),
contentScale = ContentScale.Crop,
painter = painterResource(dogIcon),
// Content Description is not needed here - image is decorative, and setting a null content
// description allows accessibility services to skip this element during navigation.
contentDescription = null
)
}
Giờ đây, trong WoofPreview()
, các biểu tượng có hình tròn.
Thêm hình dạng vào mục trong danh sách
Trong phần này, bạn sẽ thêm một hình dạng vào mục trong danh sách. Mục này đang được hiển thị thông qua Card
. Card
là nơi chứa một thành phần kết hợp duy nhất và các tuỳ chọn trang trí. Bạn có thể biến tấu giao diện thông qua đường viền, hình dạng, v.v. Trong phần này, bạn sẽ sử dụng Card
để thêm hình dạng vào mục trong danh sách.
- Mở tệp Shape.kt.
Card
là thành phần trung gian, vì vậy, bạn hãy thêm tham số trung gian của đối tượngShapes
. Đối với ứng dụng này là các góc trên cùng bên phải và dưới cùng bên trái của mục danh sách, nhưng không được bo tròn hoàn toàn. Để làm được việc đó, hãy chuyển16.dp
vào thuộc tínhmedium
.
medium = RoundedCornerShape(bottomStart = 16.dp, topEnd = 16.dp)
Vì theo mặc định, Card
đã sử dụng hình dạng trung gian, nên bạn không cần phải thiết lập rõ ràng thành hình dạng trung gian. Hãy xem Bản xem trước và xem hình dạng mới của Card
!
Nếu quay lại tệp Theme.kt trong WoofTheme()
và nhìn vào MaterialTheme()
, bạn sẽ thấy thuộc tính shapes
được thiết lập thành Shapes
val
mà bạn vừa cập nhật.
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
Dưới đây là hình ảnh so sánh các mục danh sách trước và sau khi tạo hình. Vui lòng lưu ý tác động của việc thêm hình dạng đối với việc làm cho giao diện của ứng dụng trở nên bắt mắt hơn.
Không tạo hình | Có tạo hình |
6. Thêm kiểu chữ
Kiểu chữ trong Material Design
Kiểu chữ là tập hợp kiểu phông chữ được sử dụng trên ứng dụng, đảm bảo tính linh hoạt nhưng nhất quán. Kiểu chữ trong Material Design bao gồm 15 kiểu phông chữ mà hệ thống hỗ trợ. Cách đặt tên và phân nhóm đã được đơn giản hoá thành: hiển thị, dòng tiêu đề, tiêu đề, nội dung và nhãn, với kích thước lớn, trung bình và nhỏ cho mỗi nhóm. Bạn chỉ cần sử dụng các lựa chọn này nếu muốn tuỳ chỉnh ứng dụng của mình. Nếu bạn không biết nên đặt kiểu nào cho mỗi danh mục kiểu chữ, hãy nhớ là bạn luôn có thể sử dụng kiểu chữ mặc định.
Kiểu chữ chứa các danh mục văn bản có thể tái sử dụng, mỗi loại đều có cách ứng dụng và ý nghĩa riêng.
Màn hình
Là văn bản lớn nhất trên màn hình, kiểu hiển thị được dành riêng cho văn bản/chữ số ngắn hoặc quan trọng. Các kiểu này hoạt động tốt nhất trên màn hình lớn.
Dòng tiêu đề
Dòng tiêu đề là phù hợp nhất với văn bản ngắn, có mức độ nhấn mạnh cao trên màn hình nhỏ hơn. Có thể các kiểu này sẽ phù hợp để đánh dấu các đoạn văn bản chính hoặc vùng nội dung quan trọng.
Tiêu đề
Kiểu Tiêu đề nhỏ hơn kiểu dòng tiêu đề và nên được sử dụng cho văn bản tương đối ngắn có mức độ nhấn mạnh trung bình.
Nội dung
Kiểu Nội dung được sử dụng cho các đoạn văn bản dài hơn trong ứng dụng.
Nhãn
Kiểu Nhãn là những kiểu nhỏ hơn, thực dụng, được sử dụng cho những nội dung như văn bản bên trong thành phần hoặc cho văn bản rất nhỏ trong nội dung, chẳng hạn như chú thích.
Phông chữ
Nền tảng Android cung cấp nhiều phông chữ, nhưng bạn có thể tuỳ chỉnh ứng dụng của mình bằng các phông chữ không được cung cấp theo mặc định. Phông chữ tuỳ chỉnh có thể khiến văn bản trở nên cá tính và được dùng để thể hiện thương hiệu.
Trong phần này, bạn sẽ thêm phông chữ tuỳ chỉnh có tên là Abril Fatface, Montserrat Bold và Montserrat Regular. Bạn sẽ sử dụng dòng tiêu đề displayLarge và displayMedium, và văn bản bodyLarge trong hệ thống kiểu chữ của Material, đồng thời thêm các dòng tiêu đề này vào văn bản trong ứng dụng.
Tạo thư mục tài nguyên Android cho phông chữ.
Trước khi thêm phông chữ vào ứng dụng, bạn cần thêm một thư mục phông chữ.
- Trong chế độ xem dự án của Android Studio, hãy nhấp chuột phải vào thư mục res.
- Chọn New > Android Resource Directory (Mới > Thư mục tài nguyên Android).
- Đặt tên cho Thư mục font, thiết lập kiểu Resource thành font rồi nhấp vào OK.
- Mở thư mục tài nguyên phông chữ mới nằm trong res > font (tài nguyên > phông chữ).
Tải phông chữ tuỳ chỉnh xuống
Vì đang dùng phông chữ không phải do nền tảng Android cung cấp, nên bạn cần tải phông chữ tuỳ chỉnh xuống.
- Truy cập vào https://fonts.google.com/.
- Tìm kiếm Montserrat, rồi nhấp vào Tải tập hợp xuống.
- Giải nén tệp zip.
- Mở thư mục Montserrat đã tải xuống. Trong thư mục static, hãy tìm tệp Montserrat-Bold.ttf và Montserrat-Regular.ttf (ttf, là phông chữ viết tắt của TrueType và là định dạng cho tệp phông chữ. Chọn cả hai phông chữ rồi kéo vào thư mục tài nguyên phông chữ trong dự án của bạn trong Android Studio.
- Trong thư mục phông chữ của bạn, hãy đổi tên tệp Montserrat-Bold.ttf thành montserrat_bold.ttf và đổi tên Montserrat-Regular.ttf thành Montserrat-Regular.ttf.
- Tìm Abril Fatface rồi nhấp vào Download family (Tải bộ phông chữ xuống).
- Mở thư mục Abril_Fatface đã tải xuống. Chọn AbrilFatface-Regular.ttf rồi kéo mục này vào thư mục tài nguyên phông chữ.
- Trong thư mục phông chữ của bạn, hãy đổi tên Abril_Fatface_Regular.ttf thành abril_fatface_regular.ttf.
Dưới đây là giao diện của thư mục tài nguyên phông chữ tương ứng với 3 tệp phông chữ tuỳ chỉnh:
Chạy phông chữ
- Trong cửa sổ dự án, hãy mở ui.theme > Type.kt. Chạy các phông chữ đã tải xuống bên dưới câu lệnh nhập và phía trên
Typography
val
. Trước tiên, hãy khởi chạy Abril Fatface bằng cách đặt giá trị này làFontFamily
rồi truyền vàoFont
với tệp phông chữabril_fatface_regular
.
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import com.example.woof.R
val AbrilFatface = FontFamily(
Font(R.font.abril_fatface_regular)
)
- Khởi tạo Montserrat bên dưới mục Abril Fatface bằng cách đặt giá trị này là
FontFamily
rồi truyền vàoFont
tệp phông chữmontserrat_regular
. Đối vớimontserrat_bold
,FontWeight.Bold
cũng cần được đưa vào. Mặc dù bạn chuyển sang phiên bản in đậm của tệp phông chữ, nhưng tính năng Compose không biết tệp này được in đậm, do đó bạn cần liên kết rõ ràng tệp này vớiFontWeight.Bold
.
import androidx.compose.ui.text.font.FontWeight
val AbrilFatface = FontFamily(
Font(R.font.abril_fatface_regular)
)
val Montserrat = FontFamily(
Font(R.font.montserrat_regular),
Font(R.font.montserrat_bold, FontWeight.Bold)
)
Tiếp theo, bạn đặt các kiểu dòng tiêu đề khác nhau thành phông chữ mà bạn vừa thêm vào. Đối tượng Typography
có các tham số cho 13 kiểu chữ được thảo luận ở trên. Bạn có thể xác định bao nhiêu tuỳ ý. Trong ứng dụng này, chúng ta sẽ thiết lập displayLarge
, displayMedium
và bodyLarge
. Trong phần tiếp theo của ứng dụng này, bạn sẽ sử dụng labelSmall
để thêm tại đây.
Dưới đây là bảng cho bạn thấy phông chữ, độ đậm và kích thước của mỗi dòng tiêu đề mà bạn sẽ thêm.
- Đối với thuộc tính
displayLarge
, hãy thiết lập giá trị bằngTextStyle
rồi điền thông tin trong bảng ở trên vàofontFamily
,fontWeight
vàfontSize
. Tức là tất cả văn bản đặt thànhdisplayLarge
sẽ có phông chữ Abril Fatface, với độ đậm phông chữ bình thường vàfontSize
là36.sp
.
Lặp lại quá trình này đối với displayMedium
, labelSmall
và bodyLarge
.
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp
val Typography = Typography(
displayLarge = TextStyle(
fontFamily = AbrilFatface,
fontWeight = FontWeight.Normal,
fontSize = 36.sp
),
displayMedium = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
),
labelSmall = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Bold,
fontSize = 14.sp
),
bodyLarge = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Normal,
fontSize = 14.sp
)
)
Nếu bạn chuyển đến tệp Theme.kt trong WoofTheme()
và xem MaterialTheme()
, thì tham số typography
sẽ bằng Typography val
mà bạn vừa cập nhật.
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
Thêm kiểu chữ vào văn bản ứng dụng
Giờ bạn sẽ thêm các kiểu dòng tiêu đề vào từng mục văn bản trong ứng dụng.
- Thêm
displayMedium
làm kiểu chữ chodogName
vì đây là một mẩu thông tin ngắn và quan trọng. ThêmbodyLarge
làm kiểu chữ chodogAge
vì kiểu chữ này phù hợp với văn bản có kích thước nhỏ hơn.
@Composable
fun DogInformation(
@StringRes dogName: Int,
dogAge: Int,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
Text(
text = stringResource(dogName),
style = MaterialTheme.typography.displayMedium,
modifier = Modifier.padding(top = dimensionResource(id = R.dimen.padding_small))
)
Text(
text = stringResource(R.string.years_old, dogAge),
style = MaterialTheme.typography.bodyLarge
)
}
}
- Giờ đây, trong
WoofPreview()
, tên của chú chó hiển thị bằng phông chữ Montserrat đậm trong20.sp
và tuổi của chú chó hiển thị bằng phông chữ Montserrat thông thường trong14.sp
.
Dưới đây là hình ảnh so sánh các mục danh sách trước và sau khi thêm kiểu chữ. Hãy để ý đến sự khác biệt về phông chữ giữa tên chú chó và độ tuổi của chú chó.
Không áp dụng kiểu chữ | Có áp dụng kiểu chữ |
7. Thêm thanh trên cùng
Scaffold
là một bố cục cung cấp các ô cho nhiều thành phần và thành phần trên màn hình, chẳng hạn như Image
, Row
hoặc Column
. Scaffold
cũng cung cấp một ô cho TopAppBar
mà bạn sẽ dùng trong phần này.
TopAppBar
có thể được sử dụng cho nhiều mục đích, nhưng trong trường hợp này, bạn sẽ dùng nó để thể hiện thương hiệu cũng như bản sắc của ứng dụng. Có 4 loại TopAppBar
: chính giữa, nhỏ, trung bình và lớn. Trong lớp học lập trình này, bạn sẽ triển khai thanh ứng dụng trên cùng chính giữa. Bạn sẽ tạo một thành phần kết hợp giống như ảnh chụp màn hình dưới đây rồi đặt vào phần topBar
của Scaffold
.
Đối với ứng dụng này, thanh trên cùng bao gồm Row
có hình ảnh biểu trưng và văn bản của tên ứng dụng. Biểu trưng có hình bàn chân chuyển màu dễ thương và tên ứng dụng!
Thêm hình ảnh và văn bản vào thanh trên cùng
- Trong tệp MainActivity.kt, hãy tạo một thành phần kết hợp có tên là
WoofTopAppBar()
cómodifier
không bắt buộc.
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
}
Scaffold
hỗ trợ tham sốcontentWindowInsets
có thể giúp chỉ định các phần lồng ghép cho nội dung scaffold.WindowInsets
là các phần trên màn hình nơi ứng dụng của bạn có thể giao cắt với giao diện người dùng hệ thống, các phần này sẽ được chuyển đến khung nội dung thông qua tham sốPaddingValues
. Hãy đọc thêm tại đây.
Giá trị contentWindowInsets
được truyền cho LazyColumn
dưới dạng contentPadding
.
@Composable
fun WoofApp() {
Scaffold { it ->
LazyColumn(contentPadding = it) {
items(dogs) {
DogItem(
dog = it,
modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
)
}
}
}
}
- Trong
Scaffold
, hãy thêm thuộc tínhtopBar
rồi thiết lập thuộc tính đó thànhWoofTopAppBar()
.
Scaffold(
topBar = {
WoofTopAppBar()
}
)
Dưới đây là giao diện của thành phần kết hợp WoofApp()
:
@Composable
fun WoofApp() {
Scaffold(
topBar = {
WoofTopAppBar()
}
) { it ->
LazyColumn(contentPadding = it) {
items(dogs) {
DogItem(
dog = it,
modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
)
}
}
}
}
Không có gì thay đổi trong WoofPreview()
do không có gì trong WoofTopAppBar()
. Hãy thay đổi điều đó!
- Trong
WoofTopAppBar() Composable
, hãy thêm mộtCenterAlignedTopAppBar()
và thiết lập tham số đối tượng sửa đổi thành đối tượng sửa đổi được truyền vàoWoofTopAppBar()
.
import androidx.compose.material3.CenterAlignedTopAppBar
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
CenterAlignedTopAppBar(
modifier = modifier
)
}
- Đối với tham số tiêu đề, hãy truyền
Row
vào để lưu giữImage
vàText
củaCenterAlignedTopAppBar
.
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier){
CenterAlignedTopAppBar(
title = {
Row() {
}
},
modifier = modifier
)
}
- Thêm biểu trưng
Image
vàoRow
.
- Thiết lập kích thước hình ảnh trong
modifier
làimage_size
trong tệpdimens.xml
và khoảng đệm làpadding_small
trong tệpdimens.xml
. - Sử dụng
painter
để thiết lậpImage
làmic_woof_logo
trong thư mục đối tượng có thể vẽ. - Thiết lập
contentDescription
thành null. Trong trường hợp này, biểu trưng của ứng dụng không thêm bất kỳ thông tin có ngữ nghĩa nào cho người dùng khiếm thị, nên chúng ta không phải thêm nội dung mô tả.
Row() {
Image(
modifier = Modifier
.size(dimensionResource(id = R.dimen.image_size))
.padding(dimensionResource(id = R.dimen.padding_small)),
painter = painterResource(R.drawable.ic_woof_logo),
contentDescription = null
)
}
- Tiếp theo, hãy thêm thành phần kết hợp
Text
bên trongRow
sauImage.
- Sử dụng
stringResource()
để thiết lập thành giá trịapp_name
. Thao tác này sẽ thiết lập văn bản thành tên của ứng dụng được lưu trữ trongstrings.xml
. - Thiết lập kiểu chữ của văn bản thành
displayLarge
vì tên ứng dụng là văn bản ngắn và quan trọng.
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.displayLarge
)
Đây là nội dung hiển thị trong WoofPreview()
, có vẻ hơi lệch vì biểu tượng và văn bản chưa được căn chỉnh theo chiều dọc.
- Để khắc phục vấn đề này, hãy thêm tham số giá trị
verticalAlignment
vàoRow
và thiết lập tham số đó bằngAlignment.CenterVertically
.
import androidx.compose.ui.Alignment
Row(
verticalAlignment = Alignment.CenterVertically
)
Như vậy trông đẹp hơn nhiều!
Đây là thành phần kết hợp WoofTopAppBar()
đầy đủ:
@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
CenterAlignedTopAppBar(
title = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier
.size(dimensionResource(id = R.dimen.image_size))
.padding(dimensionResource(id = R.dimen.padding_small)),
painter = painterResource(R.drawable.ic_woof_logo),
contentDescription = null
)
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.displayLarge
)
}
},
modifier = modifier
)
}
Chạy ứng dụng và chiêm ngưỡng cách TopAppBar
liên kết các thành phần của ứng dụng lại với nhau đẹp như thế nào.
Không có thanh ứng dụng trên cùng | Có thanh ứng dụng trên cùng |
Bây giờ, hãy xem ứng dụng hoàn thiện ở chế độ giao diện tối!
Xin chúc mừng, bạn đã đến phần cuối của lớp học lập trình này!
8. Lấy mã nguồn giải pháp
Để tải mã này xuống khi lớp học lập trình đã kết thúc, bạn có thể sử dụng các lệnh git sau:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-woof.git $ cd basic-android-kotlin-compose-training-woof $ git checkout material
Ngoài ra, bạn có thể tải kho lưu trữ xuống dưới dạng tệp zip rồi giải nén và mở trong Android Studio.
Nếu bạn muốn xem mã giải pháp, hãy xem mã đó trên GitHub.
9. Kết luận
Đây là ứng dụng đầu tiên bạn tạo ra bằng Material! Bạn đã tạo một bảng màu tuỳ chỉnh cho cả giao diện sáng lẫn tối, tạo hình cho các thành phần, tải phông chữ xuống và thêm chúng vào ứng dụng, đồng thời tạo một thanh trên cùng tuyệt đẹp để liên kết chúng lại với nhau. Hãy dùng những kỹ năng bạn đã học được ở lớp học lập trình này để điều chỉnh màu, hình dạng và kiểu chữ nhằm tạo một ứng dụng hoàn toàn theo ý bạn!
Tóm tắt
- Tính năng Tuỳ chỉnh giao diện Material cho phép bạn sử dụng Material Design trong ứng dụng của mình, cùng với hướng dẫn về cách tuỳ chỉnh màu sắc, kiểu chữ và hình dạng.
- Tệp Theme.kt là nơi xác định giao diện, thông qua một thành phần kết hợp có tên là
[your app name]+Theme()
(trong trường hợp của ứng dụng này làWoofTheme()
). Trong hàm này, đối tượngMaterialTheme
thiết lậpcolor
,typography
,shapes
vàcontent
của ứng dụng. - Color.kt là nơi bạn liệt kê các màu sẽ sử dụng trong ứng dụng. Sau đó, trong tệp Theme.kt, bạn cần chỉ định màu trong
LightColorPalette
vàDarkColorPalette
cho các khung cụ thể. Bạn không cần phải chỉ định tất cả ô màu. - Ứng dụng của bạn có thể chọn Bật nhanh giao diện tối, nghĩa là hệ thống sẽ triển khai giao diện tối cho bạn. Tuy nhiên, để cải thiện trải nghiệm người dùng, tốt nhất bạn nên triển khai cả giao diện tối để có toàn quyền kiểm soát đối với giao diện của ứng dụng.
- Shape.kt là nơi bạn xác định các hình dạng của ứng dụng. Có 3 kích thước hình dạng (nhỏ, trung bình, lớn) và bạn có thể chỉ định cách bo tròn các góc.
- Hình dạng thường chi phối sự chú ý, cách xác định các thành phần, trạng thái giao tiếp và thể hiện thương hiệu.
- Types.kt là nơi bạn khởi tạo phông chữ và gán
fontFamily
,fontWeight
vàfontSize
cho kiểu chữ trong Material Design. - Kiểu chữ trong Material Design bao gồm nhiều kiểu tương phản hỗ trợ nhu cầu của ứng dụng và nội dung của ứng dụng. Kiểu chữ là tổ hợp 15 kiểu mà hệ thống hỗ trợ.