Lớp học lập trình về Compose for Wear OS

1. Giới thiệu

4d28d16f3f514083.png

Compose for Wear OS giúp bạn vận dụng vào thiết bị đeo kiến thức đã học về cách xây dựng ứng dụng bằng Jetpack Compose.

Với tính năng hỗ trợ tích hợp sẵn dành cho Material You, Compose for Wear OS sẽ đơn giản hoá và tăng tốc độ phát triển giao diện người dùng, đồng thời giúp bạn tạo các ứng dụng đẹp mắt mà không cần lập trình nhiều.

Đối với lớp học lập trình này, chúng tôi mong rằng bạn sẽ thu hoạch được một số kiến thức về Compose, mặc dù không nhất thiết phải thành thạo.

Bạn sẽ tạo một số thành phần kết hợp (composable) dành riêng cho Wear (cả đơn giản và phức tạp) và cuối cùng, bạn có thể bắt đầu viết ứng dụng của riêng mình dành cho Wear OS. Hãy bắt đầu!

Kiến thức bạn sẽ học được

  • Những điểm tương đồng/khác biệt giữa trải nghiệm trước đây của bạn với Compose
  • Các thành phần kết hợp đơn giản và cách các thành phần này hoạt động trên Wear OS
  • Các thành phần kết hợp dành riêng cho Wear OS
  • LazyColumn của Wear OS (ScalingLazyColumn)
  • Phiên bản Scaffold của Wear OS

Sản phẩm bạn sẽ tạo ra

Bạn sẽ tạo một ứng dụng đơn giản cho thấy một danh sách dạng cuộn bao gồm các thành phần kết hợp đã tối ưu hoá dành cho Wear OS.

Vì sẽ sử dụng Scaffold, nên bạn cũng sẽ tạo một đoạn văn bản cong cho thấy thời gian ở trên cùng, một hiệu ứng làm mờ nét ảnh (vignette) và cuối cùng là chỉ báo cuộn ở cạnh thiết bị.

Sau đây là hình minh hoạ sản phẩm của bạn sau khi hoàn tất lớp học lập trình này:

20971cc341c73076.gif

Điều kiện tiên quyết

2. Bắt đầu thiết lập

Ở bước này, bạn sẽ thiết lập môi trường và tải dự án bắt đầu xuống.

Bạn cần có

  • Phiên bản ổn định mới nhất của Android Studio
  • Thiết bị hoặc trình mô phỏng Wear OS (Bạn còn bỡ ngỡ? Đây là cách thiết lập.)

Tải mã xuống

Nếu đã cài đặt git, bạn chỉ cần chạy lệnh dưới đây để sao chép mã nguồn từ kho lưu trữ này. Để kiểm tra xem git đã được cài đặt hay chưa, hãy nhập git --version vào dòng lệnh hoặc cửa sổ dòng lệnh và xác minh rằng git thực thi đúng cách.

git clone https://github.com/googlecodelabs/compose-for-wear-os.git
cd compose-for-wear-os

Nếu không có git, bạn có thể nhấp vào nút sau đây để tải tất cả mã dành cho lớp học lập trình này:

Bạn có thể chạy một trong hai mô-đun trong Android Studio bất cứ lúc nào bằng cách thay đổi cấu hình chạy trên thanh công cụ.

b059413b0cf9113a.png

Mở dự án trong Android Studio

  1. Trên cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy chọn biểu tượng c01826594f360d94.png Open an Existing Project (Mở một dự án hiện có).
  2. Chọn thư mục [Download Location]
  3. Khi Android Studio đã nhập dự án, hãy kiểm tra để chắc chắn rằng bạn có thể chạy các mô-đun startfinished trên một trình mô phỏng hoặc thiết bị Wear OS thực tế.
  4. Mô-đun start sẽ có dạng như ảnh chụp màn hình dưới đây. Đây là nơi bạn sẽ làm mọi việc.

d6d4b92ac53d9b3e.png

Khám phá mã bắt đầu

  • build.gradle chứa cấu hình ứng dụng cơ bản. build.gradle bao gồm các phần phụ thuộc cần thiết để tạo một ứng dụng Wear OS có thành phần kết hợp (composable). Chúng ta sẽ thảo luận về điểm giống và khác nhau giữa Jetpack Compose và phiên bản dành cho Wear OS.
  • main > AndroidManifest.xml bao gồm các phần tử cần thiết để tạo một ứng dụng Wear OS. Ứng dụng không phải Compose cũng tương tự và giống với ứng dụng di động, vì vậy chúng ta sẽ không xem xét đến.
  • Thư mục main > theme/ chứa các tệp Color, TypeTheme mà Compose dùng cho giao diện (theme).
  • main > MainActivity.kt có chứa mẫu tạo sẵn để tạo ứng dụng bằng Compose. MainActivity.kt cũng chứa các thành phần kết hợp cấp cao nhất (như ScaffoldScalingLazyList) cho ứng dụng của chúng ta.
  • main > ReusableComponents.kt chứa các hàm cho hầu hết thành phần kết hợp dành riêng cho Wear mà chúng ta sẽ tạo. Chúng ta sẽ làm rất nhiều việc trong tệp này.

3. Xem xét phần phụ thuộc

Hầu hết thay đổi về phần phụ thuộc liên quan đến Wear mà bạn thực hiện sẽ nằm ở các lớp kiến trúc (architectural layer) cấp cao (đánh dấu bằng màu đỏ trong hình dưới đây).

d92519e0b932f964.png

Tức là nhiều phần phụ thuộc mà bạn từng sử dụng trong Jetpack Compose sẽ không thay đổi khi nhắm đến Wear OS. Ví dụ: các phần phụ thuộc UI (Giao diện người dùng), Runtime (Thời gian chạy), Compiler (Trình biên dịch) và Animation (Ảnh động) sẽ vẫn giữ nguyên.

Tuy nhiên, bạn sẽ cần phải sử dụng các thư viện Wear OS Material (Chất liệu), Foundation (Nền tảng) và Navigation (Điều hướng) phù hợp. Các thư viện này khác với các thư viện bạn từng sử dụng.

Dưới đây là phần so sánh để giúp làm rõ sự khác biệt:

Phần phụ thuộc Wear OS(androidx.wear.*)

So sánh

Phần phụ thuộc tiêu chuẩn(androidx.*)

androidx.wear.compose:compose-material

thay cho

androidx.compose.material:material

androidx.wear.compose:compose-navigation

thay cho

androidx.navigation:navigation-compose

androidx.wear.compose:compose-foundation

bổ sung cho

androidx.compose.foundation:foundation

1. Nhà phát triển có thể tiếp tục sử dụng các thư viện material khác (chẳng hạn như thư viện material ripple và biểu tượng material mở rộng) trong thư viện Wear Compose Material.

Mở build.gradle, tìm kiếm "TODO: Review Dependencies" trong mô-đun start của bạn. (Bước này chỉ để xem xét phần phụ thuộc và bạn sẽ không thêm mã nào.)

start/build.gradle:

// TODO: Review Dependencies
// General Compose dependencies
implementation "androidx.activity:activity-compose:$activity_compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation "androidx.compose.material:material-icons-extended:$compose_version"

// Compose for Wear OS Dependencies
implementation "androidx.wear.compose:compose-material:$wear_compose_version"

// Foundation is additive, so you can use the standard version in your Wear OS app.
implementation "androidx.wear.compose:compose-foundation:$wear_compose_version"

Bạn sẽ nhận ra nhiều phần phụ thuộc chung của Compose, vì vậy chúng ta sẽ không trình bày các phần đó.

Hãy chuyển đến các phần phụ thuộc dành cho Wear OS.

Như đã nêu trước đó, chỉ phiên bản dành riêng cho Wear OS của material (androidx.wear.compose:compose-material) mới được đưa vào. Nghĩa là, bạn sẽ không thấy hoặc không đưa androidx.compose.material:material vào dự án.

Quan trọng là bạn phải nhớ rằng có thể sử dụng các thư viện material khác cho Wear Material. Chúng ta sẽ làm như vậy trong lớp học lập trình này bằng cách đưa androidx.compose.material:material-icons-extended vào dự án.

Cuối cùng, chúng ta đưa thư viện Wear foundation vào Compose (androidx.wear.compose:compose-foundation) . Đây là tính năng bổ sung, vì vậy, bạn có thể sử dụng thư viện này với foundation tiêu chuẩn bạn từng sử dụng. Trên thực tế, có thể bạn đã nhận ra là chúng ta đã đưa thư viện này vào các phần phụ thuộc Compose chung!

Giờ đây khi đã hiểu các phần phụ thuộc, hãy cùng xem ứng dụng chính.

4. Xem xét MainActivity

Chúng ta sẽ thực hiện mọi công việc trên

start

mô-đun, nên hãy đảm bảo rằng mọi tệp bạn mở đều nằm trong đó.

Hãy bắt đầu bằng cách mở MainActivity trong mô-đun start.

Đây là một lớp (class) khá đơn giản, mở rộng ComponentActivity và sử dụng setContent { WearApp() } để tạo giao diện người dùng.

Dựa trên kiến thức trước đây của bạn về Compose, có lẽ việc này không có gì lạ. Chúng ta chỉ đang thiết lập giao diện người dùng.

Cuộn xuống hàm thành phần kết hợp WearApp(). Trước khi chúng ta nói về đoạn mã đó, bạn sẽ thấy rất nhiều mục "TODO" ("CẦN LÀM") rải rác trong suốt đoạn mã. Mỗi mục này đại diện cho các bước trong lớp học lập trình này. Hiện giờ, bạn có thể bỏ qua.

Đoạn mã sẽ trông giống như sau:

Mã thú vị trong WearApp():

WearAppTheme {
    // TODO: Swap to ScalingLazyListState
    val listState = rememberLazyListState()

    /* *************************** Part 4: Wear OS Scaffold *************************** */
    // TODO (Start): Create a Scaffold (Wear Version)

        // Modifiers used by our Wear composables.
        val contentModifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)
        val iconModifier = Modifier.size(24.dp).wrapContentSize(align = Alignment.Center)

        /* *************************** Part 3: ScalingLazyColumn *************************** */
        // TODO: Create a ScalingLazyColumn (Wear's version of LazyColumn)
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            contentPadding = PaddingValues(
                top = 32.dp,
                start = 8.dp,
                end = 8.dp,
                bottom = 32.dp
            ),
            verticalArrangement = Arrangement.Center,
            state = listState
        ) {

            // TODO: Remove item; for beginning only.
            item { StartOnlyTextComposables() }

            /* ******************* Part 1: Simple composables ******************* */
            item { ButtonExample(contentModifier, iconModifier) }
            item { TextExample(contentModifier) }
            item { CardExample(contentModifier, iconModifier) }

            /* ********************* Part 2: Wear unique composables ********************* */
            item { ChipExample(contentModifier, iconModifier) }
            item { ToggleChipExample(contentModifier) }
        }

    // TODO (End): Create a Scaffold (Wear Version)

}

Chúng ta bắt đầu bằng việc thiết lập theme (chủ đề), WearAppTheme { }. Việc này giống hệt như cách bạn từng viết mã, tức là bạn đặt một MaterialTheme cho màu sắc, kiểu chữ và hình dạng.

Tuy nhiên, trong trường hợp của Wear OS, thường thì chúng tôi khuyên bạn sử dụng các hình dạng Material Wear mặc định được tối ưu hoá cho thiết bị hình tròn và không tròn. Vì vậy, nếu bạn đi sâu vào theme/Theme.kt của chúng tôi, bạn có thể thấy chúng tôi không ghi đè các hình dạng.

Nếu muốn, bạn có thể mở theme/Theme.kt để khám phá thêm, nhưng xin nhắc lại, tệp này vẫn giống như trước.

Tiếp theo, chúng ta tạo một số hệ số sửa đổi (modifier) dành cho các thành phần kết hợp Wear chúng ta sẽ tạo. Làm như vậy, chúng ta không cần chỉ định các thành phần này từng lần. Chủ yếu là việc căn giữa cho nội dung và thêm một số khoảng đệm.

Sau đó, chúng ta sẽ tạo một LazyColumn dùng để tạo một danh sách cuộn theo chiều dọc dành cho nhiều mục (giống như bạn từng làm trước đây).

Mã:

item { StartOnlyTextComposables() }

/* ******************* Part 1: Simple composables ******************* */
item { ButtonExample(contentModifier, iconModifier) }
item { TextExample(contentModifier) }
item { CardExample(contentModifier, iconModifier) }

/* ********************* Part 2: Wear unique composables ********************* */
item { ChipExample(contentModifier, iconModifier) }
item { ToggleChipExample(contentModifier) }

Đối với các mục này, chỉ có StartOnlyTextComposables() tạo ra giao diện người dùng. (Chúng ta sẽ điền phần còn lại trong suốt lớp học lập trình này.)

Các hàm này thực ra nằm trong tệp ReusableComponents.kt mà chúng ta sẽ xem trong phần tiếp theo.

Hãy bắt đầu dùng Compose for Wear OS!

5. Thêm các thành phần kết hợp đơn giản

Chúng ta sẽ bắt đầu với ba thành phần kết hợp (Button, TextCard) mà bạn có thể đã quen.

Trước tiên, chúng ta sẽ xoá thành phần kết hợp "hello world" ("xin chào thế giới").

Tìm "TODO: Remove item" rồi xoá cả nhận xét (comment) và dòng bên dưới:

Bước 1

// TODO: Remove item; for beginning only.
item { StartOnlyTextComposables() }

Tiếp theo, hãy thêm thành phần kết hợp đầu tiên.

Tạo thành phần kết hợp Button

Mở ReusableComponents.kt trong mô-đun start rồi tìm "TODO: Create a Button Composable" và thay thế phương pháp thành phần kết hợp hiện tại bằng mã này.

Bước 2

// TODO: Create a Button Composable (with a Row to center)
@Composable
fun ButtonExample(
    modifier: Modifier = Modifier,
    iconModifier: Modifier = Modifier
) {
    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.Center
    ) {
        // Button
        Button(
            modifier = Modifier.size(ButtonDefaults.LargeButtonSize),
            onClick = { /* ... */ },
        ) {
            Icon(
                imageVector = Icons.Rounded.Phone,
                contentDescription = "triggers phone action",
                modifier = iconModifier
            )
        }
    }
}

Hàm thành phần kết hợp ButtonExample() (nơi mã này tồn tại) giờ đây sẽ tạo ra một nút (button) được căn giữa.

Hãy cùng xem qua mã này.

Row chỉ được sử dụng ở đây để căn giữa thành phần kết hợp Button trên màn hình tròn. Bạn có thể thấy rằng chúng ta đang làm việc đó bằng cách áp dụng hệ số sửa đổi đã tạo trong MainActivity rồi chuyển hệ số sửa đổi đó vào hàm này. Sau này, khi cuộn trên một màn hình tròn, chúng ta muốn đảm bảo rằng nội dung của mình không bị cắt bỏ (đó là lý do nội dung đó được căn giữa).

Tiếp theo, chúng ta sẽ tạo riêng Button. Mã này giống như mã dùng cho Button trước đó, nhưng trong trường hợp này, chúng ta sử dụng ButtonDefault.LargeButtonSize. Đây là các kích thước đặt sẵn được tối ưu hoá dành cho các thiết bị Wear OS, vì vậy, hãy đảm bảo rằng bạn sử dụng đúng các kích thước này!

Sau đó, chúng ta đặt sự kiện nhấp vào một lambda trống. Trong trường hợp này, các thành phần kết hợp này chỉ để minh hoạ, vì vậy chúng ta không cần làm điều đó. Tuy nhiên, trong ứng dụng thực tế, có thể chúng ta sẽ giao tiếp với một thành phần, chẳng hạn như ViewModel, để thực hiện logic kinh doanh.

Sau đó, chúng ta đặt một Icon (Biểu tượng) bên trong nút. Mã này giống như mã bạn đã từng thấy đối với Icon trước đó. Chúng ta cũng đang lấy biểu tượng qua thư viện androidx.compose.material:material-icons-extended.

Cuối cùng, chúng ta đặt hệ số sửa đổi đã đặt trước đó cho Icon (Biểu tượng).

Nếu chạy ứng dụng, bạn sẽ thấy như sau:

881cfe2dcdef5687.png

Đây là mã có thể bạn từng viết (nếu vậy thì thật tuyệt). Điểm khác biệt là giờ đây bạn có thể sử dụng nút (button) được tối ưu hoá cho Wear OS.

Khá đơn giản, hãy xem qua một phần khác.

Tạo thành phần kết hợp Text

Trong ReusableComponents.kt, hãy tìm "TODO: Create a Text Composable" rồi thay thế phương thức thành phần kết hợp hiện tại bằng mã này.

Bước 3

// TODO: Create a Text Composable
@Composable
fun TextExample(modifier: Modifier = Modifier) {
    Text(
        modifier = modifier,
        textAlign = TextAlign.Center,
        color = MaterialTheme.colors.primary,
        text = stringResource(R.string.device_shape)
    )
}

Chúng ta tạo thành phần kết hợp Text, đặt hệ số sửa đổi, căn chỉnh văn bản, đặt màu sắc và cuối cùng là tách riêng văn bản với tài nguyên chuỗi (string resource).

Các thành phần kết hợp văn bản (text) có thể trông rất quen thuộc với nhà phát triển ứng dụng Compose và mã này thực sự giống với mã bạn từng dùng trước đây.

Hãy cùng xem:

b4f0e65e666cf3eb.png

Hàm thành phần kết hợp TextExample() (nơi chúng ta đặt mã của mình) nay tạo ra thành phần kết hợp văn bản (Text composable) trong màu material chính.

Chuỗi này được lấy từ tệp res/values/strings.xml. Trên thực tế, nếu nhìn trong thư mục res/values, bạn sẽ thấy hai tệp tài nguyên strings.xml.

Wear OS cung cấp tài nguyên chuỗi cho thiết bị hình tròn và không tròn, vì vậy nếu chúng ta chạy trên trình mô phỏng hình vuông, chuỗi sẽ thay đổi:

9e133aea7cde78a6.png

Đến giờ thì mọi thứ vẫn ổn! Hãy xem thành phần kết hợp tương tự gần đây nhất của chúng ta, Card.

Tạo thành phần kết hợp Card

Trong ReusableComponents.kt, hãy tìm "TODO: Create a Card" rồi thay thế phương thức thành phần kết hợp hiện tại bằng mã này.

Bước 4

// TODO: Create a Card (specifically, an AppCard) Composable
@Composable
fun CardExample(
    modifier: Modifier = Modifier,
    iconModifier: Modifier = Modifier
) {
    AppCard(
        modifier = modifier,
        appImage = {
            Icon(
                imageVector = Icons.Rounded.Message,
                contentDescription = "triggers open message action",
                modifier = iconModifier
            )
        },
        appName = { Text("Messages") },
        time = { Text("12m") },
        title = { Text("Kim Green") },
        onClick = { /* ... */ }
    ) {
        Text("On my way!")
    }
}

Wear có một chút khác biệt ở chỗ chúng ta có hai thẻ (card) chính là AppCardTitleCard.

Trong trường hợp này, chúng ta muốn sử dụng Icon trong thẻ (card), vì vậy, chúng ta sẽ sử dụng AppCard. (TitleCard có ít chỗ hơn, hãy xem hướng dẫn về Thẻ để biết thêm thông tin.)

Chúng ta tạo thành phần kết hợp AppCard, đặt hệ số sửa đổi, thêm Icon, thêm vài tham số thành phần kết hợp Text (mỗi tham số cho một khoảng trống riêng trên thẻ) và cuối cùng là đặt văn bản nội dung chính ở cuối.

Hãy cùng xem:

430eaf85d8ee5883.png

Tại thời điểm này, có lẽ bạn nhận ra đối với các thành phần kết hợp này, mã Compose gần như giống với cách bạn từng sử dụng trước đây. Điều này tuyệt phải không? Bạn có thể sử dụng lại tất cả kiến thức mình đã có!

Hãy xem xét một số thành phần kết hợp mới.

6. Thêm các thành phần kết hợp độc đáo cho Wear

Trong phần này, chúng ta sẽ khám phá các thành phần kết hợp ChipToggleChip.

Tạo thành phần kết hợp Chip

Chip thực sự được đề cập đến trong hướng dẫn về material, nhưng không có hàm thành phần kết hợp thực tế trong thư viện material tiêu chuẩn.

Đây được coi là một thao tác nhanh, chỉ với một lần nhấn. Điều này đặc biệt phù hợp với thiết bị Wear có màn hình bị hạn chế.

Sau đây là một vài biến thể của hàm thành phần kết hợp Chip để gợi ý cho bạn về những gì bạn có thể tạo:

Hãy viết một vài mã

Trong ReusableComponents.kt, hãy tìm "TODO: Create a Chip" rồi thay thế phương thức thành phần kết hợp hiện tại bằng mã này.

Bước 5

// TODO: Create a Chip Composable
@Composable
fun ChipExample(
    modifier: Modifier = Modifier,
    iconModifier: Modifier = Modifier
) {
    Chip(
        modifier = modifier,
        onClick = { /* ... */ },
        label = {
            Text(
                text = "5 minute Meditation",
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        },
        icon = {
            Icon(
                imageVector = Icons.Rounded.SelfImprovement,
                contentDescription = "triggers meditation action",
                modifier = iconModifier
            )
        },
    )
}

Thành phần kết hợp Chip có thể sử dụng nhiều tham số giống như các tham số bạn từng dùng với các thành phần kết hợp khác (hệ số sửa đổi và onClick). Do đó, chúng ta không cần xem xét các tham số đó.

Thành phần kết hợp này cũng cần có nhãn (mà chúng ta sẽ thực hiện bằng cách tạo thành phần kết hợp Text) và biểu tượng.

Icon phải giống hoàn toàn với mã mà bạn thấy trong các thành phần kết hợp khác. Tuy nhiên, đối với mã này, chúng ta đang kéo biểu tượng Self Improvement qua thư viện androidx.compose.material:material-icons-extended.

Hãy xem thành quả trông như thế nào (hãy nhớ cuộn xuống):

bd178a52438cfcbc.png

Được rồi, hãy xem biến thể trên Toggle, thành phần kết hợp ToggleChip.

Tạo thành phần kết hợp ToggleChip

ToggleChip chỉ giống như một Chip nhưng cho phép người dùng tương tác với nút chọn (radio button), nút bật/tắt (toggle) hoặc hộp đánh dấu (checkbox).

Trong ReusableComponents.kt, hãy tìm "TODO: Create a ToggleChip" rồi thay thế phương thức thành phần kết hợp hiện tại bằng mã này.

Bước 6

// TODO: Create a ToggleChip Composable
@Composable
fun ToggleChipExample(modifier: Modifier = Modifier) {
    var checked by remember { mutableStateOf(true) }
    ToggleChip(
        modifier = modifier,
        checked = checked,
        toggleControl = {
            Icon(
                imageVector = ToggleChipDefaults.switchIcon(checked = checked),
                contentDescription = if (checked) "On" else "Off"
            )
        },
        onCheckedChange = {
            checked = it
        },
        label = {
            Text(
                text = "Sound",
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        }
    )
}

Bây giờ hàm thành phần kết hợp ToggleChipExample() (nơi mã này tồn tại) tạo ra một ToggleChip sử dụng nút bật/tắt (thay vì hộp đánh dấu hoặc nút chọn).

Trước tiên, chúng ta sẽ tạo một MutableState. Chúng ta chưa thực hiện việc này trong các hàm khác vì chúng ta chủ yếu làm việc trên bản minh hoạ giao diện người dùng để bạn có thể xem các tính năng của Wear.

Trong một ứng dụng thông thường, bạn nên truyền vào trạng thái đã đánh dấu và lambda để xử lý thao tác nhấn, do đó thành phần kết hợp có thể không có trạng thái ( xem thêm thông tin tại đây).

Trong trường hợp này, chúng ta chỉ đơn giản là cho thấy ToggleChip trông như thế nào khi dùng nút bật/tắt đang hoạt động (mặc dù chúng ta không làm gì với trạng thái này).

Tiếp theo, chúng ta đặt hệ số sửa đổi, trạng thái đánh dấu và biểu tượng bật/tắt để xây dựng nút chuyển theo như mong muốn. (Trong trường hợp này, chúng ta sử dụng các giá trị mặc định.)

Sau đó, chúng ta tạo một lambda để thay đổi trạng thái và cuối cùng đặt nhãn bằng thành phần kết hợp Text (và một số tham số cơ bản).

Hãy cùng xem:

452080e3dbb4f3f0.png

Hiện tại bạn đã thấy nhiều thành phần kết hợp dành riêng cho Wear OS, và như đã nói trước đây, hầu hết mã gần giống như những mã bạn từng viết trước đây.

Hãy xem xét khía cạnh nào đó nâng cao hơn một chút.

7. Di chuyển sang ScalingLazyColumn

Có thể bạn đã sử dụng LazyColumn trong ứng dụng di động để tạo danh sách cuộn theo chiều dọc.

Vì thiết bị hình tròn nhỏ hơn ở phía trên cùng và dưới cùng nên không gian để hiện các mục sẽ nhỏ hơn. Do đó, Wear OS có phiên bản LazyColumn riêng để hỗ trợ hiệu quả hơn cho các thiết bị hình tròn như vậy.

ScalingLazyColumn mở rộng LazyColumn để hỗ trợ cả tính năng chia tỷ lệ và độ trong suốt ở đầu và cuối màn hình để người dùng có thể đọc được nội dung dễ dàng hơn.

Sau đây là một bản minh hoạ:

198ee8e8fa799f08.gif

Hãy lưu ý cách một mục mở rộng ra kích thước đầy đủ khi tiến lại gần trung tâm và giảm kích thước khi di chuyển đi (cùng với việc tăng độ trong suốt).

Sau đây là ví dụ cụ thể hơn trong một ứng dụng:

d6e02f406f96c664.gif

Chúng ta thấy điều này thực sự khiến giao diện dễ đọc hơn.

Sau khi bạn thấy ScalingLazyColumn hoạt động, hãy bắt đầu chuyển đổi LazyColumn.

Chuyển đổi sang ScalingLazyListState

Trong MainActivity.kt, hãy tìm "TODO: Swap to ScalingLazyListState" rồi thay thế nhận xét (comment) đó cũng như dòng bên dưới bằng mã này.

Bước 7

// TODO: Swap to ScalingLazyListState
val listState = rememberScalingLazyListState()

Các tên gần như giống hệt nhau, trừ phần "Scaling" ("Tỷ lệ") Cũng giống như LazyListState xử lý trạng thái cho LazyColumn, ScalingLazyListState xử lý trạng thái cho ScalingLazyColumn.

Chuyển đổi sang ScalingLazyColumn

Tiếp theo, chúng ta hoán đổi trong ScalingLazyColumn.

Trong MainActivity.kt, hãy tìm "TODO: Swap a ScalingLazyColumn". Trước tiên, hãy thay LazyColumn bằng ScalingLazyColumn.

Sau đó, xóa hoàn toàn contentPaddingverticalArrangementScalingLazyColumn đã cung cấp các chế độ cài đặt mặc định đảm bảo hiệu ứng hình ảnh mặc định tốt hơn vì hầu hết khung nhìn sẽ được lấp đầy bằng các mục trong danh sách. Trong nhiều trường hợp, tham số mặc định là đủ, nếu bạn có tiêu đề ở trên cùng, thì bạn nên đặt tiêu đề đó vào ListHeader làm mục đầu tiên. Nếu không, hãy cân nhắc đặt autoCentering với itemIndex là 0 để cung cấp đủ khoảng đệm cho mục đầu tiên.

Bước 8

// TODO: Swap a ScalingLazyColumn (Wear's version of LazyColumn)
ScalingLazyColumn(
    modifier = Modifier.fillMaxSize(),
    autoCentering = AutoCenteringParams(itemIndex = 0),
    state = listState

Vậy là xong! Hãy cùng xem:

442700f212089fd0.png

Bạn có thể thấy nội dung được chia tỷ lệ và độ trong suốt được điều chỉnh ở đầu và cuối màn hình khi bạn cuộn với rất ít thao tác để di chuyển!

Bạn có thể thực sự nhận thấy điều này với các thành phần kết hợp "meditation", khi bạn di chuyển lên và xuống.

Bây giờ, hãy chuyển sang chủ đề cuối cùng, Scaffold của Wear OS.

8. Thêm Scaffold

Scaffold cung cấp cấu trúc bố cục giúp bạn sắp xếp màn hình theo các mẫu chung, giống như cách sắp xếp trên thiết bị di động, nhưng thay vì Thanh ứng dụng, FAB, Ngăn kéo, hoặc các phần tử cụ thể khác dành cho thiết bị di động, nó hỗ trợ bốn bố cục dành riêng cho Wear với các thành phần cấp cao: thời gian (time), hiệu ứng làm mờ nét ảnh (vignette), chỉ báo cuộn/vị trí (scroll/position indicator). và chỉ báo trang (page indicator).

Nó cũng xử lý cả thiết bị tròn và không tròn.

Các thành phần đó sẽ có dạng như sau:

TimeText

Vignette

PositionIndicator

PageIndicator

Chúng ta sẽ tìm hiểu chi tiết ba thành phần đầu tiên, nhưng trước tiên, hãy đặt scaffold vào vị trí.

Thêm Scaffold

Hãy thêm ngay mẫu soạn sẵn dành cho Scaffold.

Tìm "TODO (Start): Create a Scaffold (Wear Version)" rồi thêm mã ở bên dưới.

Bước 9

// TODO (Start): Create a Scaffold (Wear Version)
Scaffold(
    timeText = { },
    vignette = { },
    positionIndicator = { }
) {

Chúng ta sẽ lần lượt giải thích từng tham số này ở các bước sau. Hiện tại, chúng ta không tạo ra giao diện người dùng nào.

Tiếp theo, hãy đảm bảo rằng bạn thêm các dấu đóng ngoặc vào đúng vị trí. Tìm "TODO (End): Create a Scaffold (Wear Version)" rồi thêm dấu ngoặc vào đó:

Bước 10

// TODO (End): Create a Scaffold (Wear Version)
}

Hãy chạy ứng dụng trước. Bạn sẽ thấy như sau:

c2cb637a495bc977.png

Hãy lưu ý, kể từ khi chúng ta thêm Scaffold, ứng dụng này thực sự không hoạt động khác so với trước đây. Nhưng điều này sẽ thay đổi sau khi chúng ta bắt đầu triển khai các thành phần.

Hãy bắt đầu bằng tham số đầu tiên trong ba tham số, đó là TimeText.

TimeText

TimeText sử dụng văn bản cong giúp nhà phát triển dễ dàng cho thấy thời gian mà không cần đặt thành phần kết hợp hoặc không cần dùng đến lớp (class) nào liên quan đến thời gian.

Hướng dẫn Material đòi hỏi bạn phải hiện thời gian ở mọi phần đầu màn hình trong mọi lớp phủ (hoặc ứng dụng). Sau đây là một ví dụ về hình thức của giao diện:

d7a321e97b87b579.png

Việc thêm TimeText thực sự khá đơn giản.

Hãy tìm "timeText = { }," rồi thay thế bằng mã dưới đây:

Bước 11

timeText = {
    if (!listState.isScrollInProgress) {
        TimeText()
    }
},

Chúng ta đang thêm một chút logic để ẩn thời gian nếu người dùng bắt đầu cuộn.

Sau đó, chúng ta chỉ tạo một thành phần kết hợp TimeText. Chúng ta có thể thêm các tham số bổ sung để thêm văn bản vào trước và/hoặc sau thời gian, nhưng chúng ta nên duy trì sự đơn giản ở đây.

Hãy thử chạy. Giờ đây, bạn sẽ nhìn thấy thời gian, nhưng nếu bạn cuộn, thời gian sẽ biến mất.

75c1964e19457962.png

Tiếp theo, chúng ta sẽ đề cập đến Vignette.

Thêm Vignette

Vignette (hiệu ứng làm mờ nét ảnh) sẽ làm mờ các cạnh trên và cạnh dưới của màn hình thiết bị đeo khi màn hình có khả năng cuộn xuất hiện.

Nhà phát triển có thể chỉ định để làm mờ phần trên cùng, dưới cùng hoặc cả hai tuỳ thuộc vào trường hợp sử dụng.

Sau đây là ví dụ:

b9b37010632bf52b.png

Chúng ta chỉ có một màn hình (là màn hình có thể cuộn), vì vậy, chúng ta nên hiển thị hiệu ứng làm mờ viền để dễ đọc hơn. Hãy thực hiện ngay.

Hãy tìm "vignette = { }," rồi thay thế bằng mã dưới đây:

Bước 12

vignette = {
    // Only show a Vignette for scrollable screens. This code lab only has one screen,
    // which is scrollable, so we show it all the time.
    Vignette(vignettePosition = VignettePosition.TopAndBottom)
},

Hãy đọc nhận xét (comment) để biết thêm chi tiết về thời điểm nên và không nên sử dụng hiệu ứng làm mờ nét ảnh. Trong trường hợp này, chúng ta muốn hiệu ứng làm mờ viền luôn xuất hiện và chúng ta muốn làm mờ cả phía trên cùng và dưới cùng của màn hình.

2c1ca979165bbe25.png

Nếu nhìn vào phần trên cùng hoặc dưới cùng (đặc biệt là trên các thành phần kết hợp màu tím), bạn sẽ thấy hiệu ứng.

Hãy thảo luận về tham số cuối cùng của chúng ta về Scaffold, đó là PositionIndicator.

Thêm PositionIndicator

PositionIndicator (còn gọi là Chỉ báo cuộn) là một chỉ báo ở bên phải màn hình dùng để cho thấy vị trí chỉ báo hiện tại dựa trên loại đối tượng trạng thái bạn chuyển vào. Trong trường hợp này, đó sẽ là ScalingLazyListState.

Sau đây là ví dụ:

b4b13badcfdc919e.png

Bạn có thể thắc mắc tại sao chỉ báo vị trí lại cần phải tăng ở cấp Scaffold mà không phải là cấp ScalingLazyColumn.

Do độ cong của màn hình, chỉ báo vị trí cần được căn giữa trên đồng hồ (Scaffold), chứ không phải chỉ căn giữa khung nhìn (ScalingLazyColumn). Nếu không, nội dung nói trên có thể bị cắt bớt.

Ví dụ: trong ứng dụng dưới đây, chúng ta có thể giả định rằng thành phần kết hợp "Playlist" ("Danh sách nghe") không nằm trong khu vực cuộn được. Chỉ báo vị trí được căn giữa trên ScalingLazyColumn nhưng không chiếm toàn bộ màn hình. Do đó, bạn có thể thấy phần lớn nội dung trên chỉ báo đã bị cắt bỏ.

f14296881a63650f.png

Tuy nhiên, nếu căn giữa chỉ báo vị trí thay vì căn giữa toàn bộ phần xem được (mà Scaffold cung cấp cho bạn), thì giờ đây bạn có thể thấy rõ chỉ báo đó.

22dd9544d8dafcd5.png

Tức là PositionIndicator đòi hỏi ScalingLazyListState (cho vị trí trong danh sách cuộn) phải cao hơn Scaffold.

Nếu có khả năng quan sát đặc biệt tốt, có thể bạn sẽ nhận thấy chúng ta đã nâng ScalingLazyListState lên trên Scaffold để ẩn hoặc hiển thị thời gian. Vì vậy, trong trường hợp của chúng ta thì công việc đã hoàn tất.

Giờ đây khi đã tìm hiểu lý do bạn nên đặt chỉ báo vị trí trong scaffold, hãy thêm chỉ báo này vào ứng dụng.

Hãy tìm "positionIndicator = { }" rồi thay thế bằng mã dưới đây:

Bước 12

positionIndicator = {
    PositionIndicator(
        scalingLazyListState = listState
    )
}

Điều này khá đơn giản, PositionIndicator đòi hỏi trạng thái cuộn phải tự hiển thị chính xác và giờ đây nó thực sự có thể tự động hiển thị!

Và còn một tính năng tuyệt vời khác là tự ẩn khi người dùng không cuộn.

Chúng ta đang sử dụng ScalingLazyListState nhưng PositionIndicator còn có nhiều tuỳ chọn cuộn khác, ví dụ: ScrollState, LazyListState và thậm chí có thể xử lý nút xoay ở cạnh (rotating side button) hoặc

mép xoay (rotating bezel). Để xem tất cả tuỳ chọn, hãy tham khảo KDocs cho từng phiên bản.

Được rồi, hãy xem thành quả của chúng ta:

1b6fa8091b920587.png

Hãy thử cuộn lên và xuống. Bạn sẽ chỉ thấy chỉ báo cuộn xuất hiện khi đang cuộn.

Tuyệt vời, bạn đã hoàn tất phần minh hoạ giao diện người dùng của hầu hết thành phần kết hợp Wear OS!

9. Xin chúc mừng

Xin chúc mừng! Bạn đã tìm hiểu các kiến thức cơ bản về việc sử dụng Compose on Wear OS!

Giờ đây, bạn có thể vận dụng tất cả kiến thức về Compose để tạo ra các ứng dụng Wear OS tuyệt đẹp!

Tiếp theo là gì?

Hãy tham khảo các lớp học lập trình khác về Wear OS:

Tài liệu đọc thêm

Ý kiến phản hồi

Chúng tôi rất mong được biết cảm nhận của bạn về trải nghiệm khi sử dụng tính năng Compose cho Wear OS và những gì bạn đã có thể xây dựng! Tham gia thảo luận trong kênh Kotlin Slack #compose-wear và vui lòng cung cấp ý kiến phản hồi vềCông cụ theo dõi lỗi.

Chúc bạn lập trình vui vẻ!