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

1. Giới thiệu

2c9dd335c9d65f10.png

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

Với tính năng hỗ trợ tích hợp sẵn dành cho Material Design, Compose cho Wear OS sẽ tinh giản và đẩy nhanh quá trình 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ẽ có đượ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 (TransformingLazyColumn)
  • 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 AppScaffoldScreenScaffold, nên bạn cũng sẽ nhận một thời điểm hiển thị văn bản uốn cong ở trên cùng và một 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:

3162140d003d8e31.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ã 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/android/codelab-compose-for-wear-os.git
cd codelab-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ụ.

51cb2645eece1f20.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 c01826594f360d94.png Open... (Mở)
  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.
  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.

271451f93a57db41.png

Tìm hiểu mã khởi đầ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ư ScaffoldTransformingLazyColumn) 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-material3

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

androidx.wear.compose:compose-ui-tooling

bổ sung cho

androidx.compose.ui:ui-tooling-preview

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) với thư viện Wear Compose Material. Ngoài phiên bản compose-material3, bạn có thể sử dụng androidx.wear.compose:compose-material nhưng không nên kết hợp Material 3 và Material 2.5 trong cùng một ứng dụng. Bạn nên sử dụng material3 vì phiên bản này hỗ trợ thiết kế Material 3 Expressive.

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:

dependencies {
    val composeBom = platform(libs.androidx.compose.bom)

    // General compose dependencies
    implementation(composeBom)
    implementation(libs.androidx.activity.compose)
    implementation(libs.compose.ui.tooling.preview)

    implementation(libs.androidx.material.icons.extended)

    // Compose for Wear OS Dependencies
    implementation(libs.wear.compose.material)

    // Foundation is additive, so you can use the mobile version in your Wear OS app.
    implementation(libs.wear.compose.foundation)

    // Compose preview annotations for Wear OS.
    implementation(libs.androidx.compose.ui.tooling)

    implementation(libs.horologist.compose.layout)

    coreLibraryDesugaring(libs.desugar.jdk.libs)

    debugImplementation(libs.compose.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)
    debugImplementation(composeBom)
}

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-material3) mới được đưa vào. Nghĩa là, bạn sẽ không thấy hoặc không đưa androidx.compose.material:material3 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 rằng chúng ta đã đưa thư viện này vào các phần phụ thuộc chung trong Compose!

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 {
     /* *************************** Part 4: Wear OS Scaffold *************************** */
    // TODO (Start): Create a AppScaffold (Wear Version)

    // TODO: Swap to TransformingLazyColumnState
    val listState = rememberLazyListState()

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

    /* *************************** Part 3: ScalingLazyColumn *************************** */
    // TODO: Swap a TransformingLazyColumn (Wear's version of LazyColumn)
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        state = listState,
    ) {
        // TODO: Remove item; for beginning only.
        item { StartOnlyTextComposables() }

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

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

    // TODO (End): Create a ScreenScaffold (Wear Version)
    // TODO (End): Create a AppScaffold (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 thiết lập 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, chúng tôi thường đề xuất 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ì vậy, nếu tìm hiểu kỹ về 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ên điện thoại.

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() }
item { TextExample() }
item { CardExample() }

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

Đố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 3 thành phần kết hợp (Button, TextCard) mà bạn có thể đã quen thuộc.

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 cho nút biểu tượng

Mở ReusableComponents.kt trong mô-đun start rồi tìm "TODO: Create a Icon 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 Icon Button Composable
@Composable
fun IconButtonExample(
    modifier: Modifier = Modifier,
) {
    FilledIconButton(
        onClick = { /* ... */ },
        modifier = modifier,
    ) {
        Icon(
            imageVector = Icons.Rounded.Phone,
            contentDescription = "triggers phone action",
        )
    }
}

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

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

Chúng tôi sử dụng FilledIconButton, đây là một nút tròn, chỉ có biểu tượng với nền có màu và màu nội dung tương phản. Nút này cung cấp một vị trí duy nhất để chèn biểu tượng hoặc hình ảnh.

Sau đó, chúng ta đặt sự kiện nhấp chuột 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, bạn không cần bận tâm về việc căn giữa nút ở giai đoạn này vì việc này sẽ được tự động xử lý vào lúc khác khi di chuyển từ LazyColumn sang TransformingLazyColumn.

Nếu chạy ứng dụng, bạn sẽ thấy như sau, đừng lo nếu nút chưa nằm ở giữa:

9346dbd2b8bc6a56.png

Có thể bạn đã từng viết mã này rồi (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 đượ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) {
   ListHeader{
      Text(
           modifier = modifier
               .fillMaxWidth(),
           textAlign = TextAlign.Center,
           text = stringResource(R.string.hello_compose_codelab),
       )
   }
}

Chúng ta tạo thành phần kết hợp Text, đặt đối tượng 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. Vì sau này chúng ta sẽ thêm một Danh sách, nên chúng ta sẽ gói văn bản này vào ListHeader để nội dung được thêm vào đầu và cuối.

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 sử 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:

dcad71112a91d706.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 của chúng ta.

Đế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.AutoMirrored.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ẻ, 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 đối tượng 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 giao diện ra sao nhé. Đừng lo lắng về việc thêm khoảng đệm bổ sung ở dưới cùng vì vấn đề này sẽ được khắc phục khi di chuyển từ LazyColumn sang TransformingLazyColumn:

faf2fe359baf0946.png

Tại thời điểm này, có lẽ bạn nhận ra rằng đố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

Khối được coi là một thao tác nhanh, chỉ với một lần nhấn. Thành phần này đặc biệt phù hợp với thiết bị Wear có không gian màn hình có hạn.

Bạn có thể triển khai một khối bằng cách sử dụng Button. Sau đây là một vài biến thể của hàm có khả năng kết hợp Button để gợi ý cho bạn về những gì bạn có thể tạo:

13c7dc5e08b5d2d4.png 9859b3f9880f75af.png

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,
) {
    Button(
        modifier = modifier,
        onClick = { /* ... */ },
        icon = {
            Icon(
                imageVector = Icons.Rounded.SelfImprovement,
                contentDescription = "triggers meditation action",
            )
        },
    ) {
        Text(
            text = "5 minute Meditation",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
    }
}

Thành phần kết hợp Button 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 (đối tượng 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ó Vị trí để chèn nội dung và biểu tượng cho phần nội dung có thể kết hợp hiển thị trên Nút (chúng ta sẽ tạo một thành phần kết hợp Text cho nút này).

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 từ thư viện androidx.compose.material:material-icons-extended.

Hãy cùng xem thành quả nhé (nhớ cuộn xuống). Đừng lo lắng về việc thêm khoảng đệm bổ sung ở dưới cùng vì vấn đề này sẽ được khắc phục khi di chuyển từ LazyColumn sang TransformingLazyColumn:

22afab093efc7fc5.png

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

Tạo một thành phần kết hợp SwitchChip

SwitchButton giống như Button nhưng cho phép người dùng tương tác với một nút chuyển.

eb38c8acb3ac996.png

Trong ReusableComponents.kt, hãy tìm "TODO: Create a SwitchChip" 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 Switch Chip Composable
@Composable
fun SwitchChipExample(modifier: Modifier = Modifier) {
    var checked by remember { mutableStateOf(true) }
    SwitchButton(
        modifier = modifier.fillMaxWidth(),
        label = {
            Text(
                "Sound",
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
                modifier = Modifier.semantics {
                    this.contentDescription = if (checked) "On" else "Off"
                },
            )
        },
        checked = checked,
        onCheckedChange = { checked = it },
        enabled = true,
    )
}

Bây giờ, hàm có khả năng kết hợp SwitchChipExample() (nơi mã này tồn tại) tạo ra một SwitchChip 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 SwitchChip 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 trạng thái đánh dấu và chế độ điều khiển bật/tắt để tạo nút chuyển như mong muốn.

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:

8ef93a35bdb7302a.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 TransformingScalingLazyColumn

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.

Hoạt động biến đổi LazyColumn sẽ mở rộng LazyColumn để hỗ trợ cả tính năng chuyển 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ạ:

c056381ba4a7475d.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:

6dbb1e13f99e5e1f.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 TransformingLazyColumn hoạt động, hãy bắt đầu chuyển đổi LazyColumn.

Chuyển đổi sang TransformingLazyColumnState

Trong MainActivity.kt, hãy tìm "TODO: Swap to TransformingLazyColumnState" rồi thay thế nhận xét đó cũng như dòng bên dưới bằng mã sau đây. Lưu ý cách chúng ta chỉ định những thành phần nào là thành phần đầu tiên và thành phần cuối cùng để sử dụng các giá trị khoảng đệm phù hợp nhất nhằm tránh tình trạng nội dung bị cắt.

Bước 7

// TODO: Swap to TransformingLazyColumnState
val listState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()

Tên gần giống hệt nhau. Cũng giống như LazyListState xử lý trạng thái cho LazyColumn, TransformingLazyColumnState xử lý trạng thái cho TransformingLazyColumn.

Chúng ta cũng chỉ định một transformationSpec tại đây để có thể thêm các phép biến đổi cho các mục khi cuộn trên màn hình.

Chuyển đổi sang TransformingLazyColumn

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

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

Sau đó, xóa hoàn toàn contentPadding, verticalArrangementmodifierTransformingLazyColumn đã 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, chỉ cần tham số mặc định là đủ. Nếu 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.

Bước 8

// TODO: Swap a TransformingLazyColumn (Wear's version of LazyColumn)
TransformingLazyColumn(
    state = listState,
    contentPadding = contentPadding)

Thêm một hiệu ứng biến đổi khi cuộn vào các mục

Bây giờ, chúng ta sẽ thêm hiệu ứng ShapeMorphing vào các phần tử TransformingLazyColumn bằng cách sử dụng ModifierSurfaceTransformation sau đây. Áp dụng cùng một mã cho mọi thành phần (

ngoại trừ IconButtonExample chưa hỗ trợ tính năng này).

Bước 9

TextExample(
   modifier = Modifier.fillMaxWidth().transformedHeight(this, transformationSpec),
   transformation = SurfaceTransformation(transformationSpec),
)

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

4c97e27b6acb83ef.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

AppScaffoldScreenScaffold cung cấp một cấu trúc bố cục giúp bạn sắp xếp màn hình theo các mẫu phổ biến, 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 hoặc các phần tử cụ thể khác dành riêng cho thiết bị di động, Scaffold và ScreenScaffold hỗ trợ 3 bố cục dành riêng cho Wear với các thành phần cấp cao: thời gian, chỉ báo cuộn/vị trí và chỉ báo trang.

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

TimeText

ScrollIndicator

HorizontalPageIndicator

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

Các thành phần của scaffold AppScaffoldScreenScaffold bố trí cấu trúc của một màn hình và điều phối các phép chuyển đổi thành phần ScrollIndicatorTimeText.

AppScaffold cho phép các phần tử tĩnh trên màn hình như TimeText vẫn hiển thị trong quá trình chuyển đổi trong ứng dụng, chẳng hạn như vuốt để đóng. Theo mặc định, ScreenScaffold hiển thị ScrollIndicator ở giữa cuối màn hình và điều phối việc hiện/ẩn TimeTextScrollIndicator

Thêm Scaffold

Hãy thêm ngay mã nguyên mẫu dành cho AppScaffoldScreenScaffold.

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

Bước 9

WearAppTheme {
// TODO (Start): Create a AppScaffold (Wear Version)
AppScaffold {

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

// TODO (Start): Create a ScreenScaffold (Wear Version)
ScreenScaffold( 
    scrollState = listState,
    contentPadding = rememberResponsiveColumnPadding(
       first = ColumnItemType.IconButton,
       last = ColumnItemType.Button,
    ),
){contentPadding ->

Tiếp theo, hãy đảm bảo rằng bạn thêm dấu đóng ngoặc vào đúng vị trí.

Tìm "TODO (End): Create a ScreenScaffold (Wear Version)" rồi thêm dấu đóng ngoặc vào đó:

Bước 10

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

Tìm "TODO (End): Create a AppScaffold (Wear Version)" rồi thêm dấu đóng ngoặc vào đó:

Bước 10

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

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

97e417901b8f8229.png

Lưu ý rằng ứng dụng này bổ sung:

  • Một TimeText sử dụng văn bản cong trong nền giúp nhà phát triển dễ dàng hiện thời gian mà không cần đặt thành phần kết hợp hoặc dùng tới lớp nào liên quan đến thời gian. Ngoài ra, theo Nguyên tắc của Material, bạn nên hiển thị thời gian ở đầu mọi màn hình trong ứng dụng và thời gian sẽ mờ dần khi cuộn.
  • ScrollIndicator là một chỉ báo ở bên phải màn hình, cho biết vị trí chỉ báo hiện tại dựa trên loại đối tượng trạng thái mà bạn truyền vào. Trong trường hợp của chúng ta, đó sẽ là TransformingLazyColumnState.

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

fd00c8fc4f7283ef.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.

Thêm nút nằm sát cạnh

EdgeButton là một loại nút mới có tính biểu cảm có trong bản phát hành M3 Compose Material cho Wear OS. Vùng chứa nằm sát cạnh là một hình dạng mới phù hợp với hình tròn và tận dụng tối đa không gian bên trong kiểu dáng hình tròn.

ScreenScaffold cung cấp một vị trí cho EdgeButton để nút này chiếm hết không gian trống ở bên dưới một danh sách cuộn. Khi người dùng cuộn xuống cuối danh sách, nút này sẽ to dần và rõ dần; còn khi cuộn lên, nó sẽ nhỏ dần và mờ đi. Hãy thêm EdgeButton vào mã của chúng ta:

Bước 11

ScreenScaffold(
  scrollState = listState,
  contentPadding = rememberResponsiveColumnPadding(
    first = ColumnItemType.IconButton,
    last = ColumnItemType.Button,
  ),
/* *************************** Part 11: EdgeButton *************************** */
  // TODO: Add a EdgeButton
   edgeButton = {
     EdgeButton(
      onClick = { /* ... */ },
      buttonSize = EdgeButtonSize.Medium) {
        Text(stringResource(R.string.more))
      }
   }

Bạn có thể chỉ định 4 kích thước khác nhau cho EdgeButton: ExtraSmall, Small, MediumLarge.

Được rồi, hãy cùng xem thành quả nhé:

3a973bbfe4941e67.png

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 trên 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

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 dùng Compose cho Wear OS và sản phẩm bạn có thể xây dựng! Hãy tham gia thảo luận trong kênh Kotlin Slack #compose-wear và 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ẻ!