Các danh sách có Compose cho Wear OS


Danh sách cho phép người dùng chọn một mục trong một tập hợp các lựa chọn trên thiết bị Wear OS.

Nhiều thiết bị Wear OS sử dụng màn hình tròn, điều này khiến bạn khó xem được các mục trong danh sách xuất hiện ở gần đầu và cuối màn hình. Vì lý do này, Compose cho Wear OS thêm một phiên bản của lớp LazyColumn có tên là TransformingLazyColumn. Phiên bản này hỗ trợ các hiệu ứng chuyển tỉ lệ và biến đổi. Khi các mục di chuyển ra rìa, chúng sẽ nhỏ hơn và mờ dần.

Cách áp dụng hiệu ứng thu phóng và cuộn được đề xuất:

  1. Sử dụng Modifier.transformedHeight để cho phép Compose tính toán sự thay đổi chiều cao khi mục cuộn qua màn hình.
  2. Sử dụng transformation = SurfaceTransformation(transformationSpec) để áp dụng các hiệu ứng hình ảnh, bao gồm cả việc thu nhỏ nội dung của mục.
  3. Sử dụng TransformationSpec tuỳ chỉnh cho các thành phần không lấy transformation làm tham số, chẳng hạn như Text.

Ảnh động sau đây cho thấy cách một phần tử danh sách chuyển tỉ lệ và thay đổi hình dạng khi tiến đến đầu và cuối màn hình:

Đoạn mã sau đây cho biết cách tạo danh sách bằng bố cục TransformingLazyColumn để tạo nội dung hiển thị đẹp mắt trên nhiều kích thước màn hình Wear OS.

Đoạn mã này cũng minh hoạ cách sử dụng đối tượng sửa đổi minimumVerticalContentPadding mà bạn nên đặt trên các mục trong danh sách để áp dụng khoảng đệm chính xác ở đầu và cuối danh sách.

Để hiện chỉ báo cuộn, hãy chia sẻ columnState giữa ScreenScaffoldTransformingLazyColumn:

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(
    scrollState = columnState
) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding
    ) {
        item {
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text(text = "Header")
            }
        }
        // ... other items
        item {
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec),
                onClick = { /* ... */ },
                icon = {
                    Icon(
                        imageVector = Icons.Default.Build,
                        contentDescription = "build",
                    )
                },
            ) {
                Text(
                    text = "Build",
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
    }
}

Thêm hiệu ứng chụp nhanh và hất

Tính năng căn chỉnh đảm bảo rằng khi người dùng hoàn tất cử chỉ cuộn hoặc hất, danh sách sẽ được đặt với một mục được định vị chính xác tại một điểm cụ thể, thường là ở giữa màn hình. Trên màn hình tròn, nơi các mục được điều chỉnh tỷ lệ và biến đổi khi di chuyển ra xa tâm, tính năng căn chỉnh đặc biệt hữu ích để đảm bảo mục phù hợp nhất vẫn hiển thị đầy đủ và dễ đọc trong vùng xem tối ưu.

Để thêm hành vi hất và di chuyển nhanh, hãy đặt tham số flingBehavior thành TransformingLazyColumnDefaults.snapFlingBehavior(columnState). Đặt rotaryScrollableBehavior cho phù hợp bằng cách sử dụng RotaryScrollableDefaults.snapBehavior(columnState) để có trải nghiệm nhất quán khi dùng núm vặn hoặc viền vật lý.

val columnState = rememberTransformingLazyColumnState()
ScreenScaffold(scrollState = columnState) {
    TransformingLazyColumn(
        state = columnState,
        flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState),
        rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState)
    ) {
        // ...
        // ...
    }
}

Bố cục đảo ngược

Theo mặc định, danh sách có thể cuộn sẽ được cố định vào cạnh trên cùng. Nếu người dùng đã cuộn xuống cuối danh sách tiêu chuẩn và một mục mới được thêm vào cuối, thì danh sách sẽ duy trì chế độ xem của người dùng đối với mục hiện tại. Ví dụ: nếu người dùng đang xem mục 10 ở cuối màn hình và mục 11 được thêm vào, thì khung hiển thị vẫn tập trung vào mục 10 và mục 11 sẽ xuất hiện ngoài màn hình bên dưới khung hiển thị hiện tại.

Đối với các trường hợp sử dụng như ứng dụng nhắn tin hoặc nhật ký trực tiếp, bạn thường không muốn hành vi này. Khi có mục mới, người dùng thường muốn xem ngay nội dung mới nhất nếu họ đã ở cuối danh sách. Nếu nhiều mục đến cùng lúc, danh sách sẽ bỏ qua để hiển thị mục mới nhất ở dưới cùng (nghĩa là một số mục trung gian có thể không hiển thị trừ phi người dùng cuộn lên).

Để hỗ trợ các trường hợp sử dụng này, TransformingLazyColumn cho phép bạn đảo ngược bố cục bằng cách đặt reverseLayout = true. Thao tác này sẽ thay đổi điểm neo của danh sách từ cạnh trên thành cạnh dưới.

Để thuận tiện, việc đặt reverseLayout = true cũng đảo ngược thứ tự hiển thị của các mục và hướng của cử chỉ cuộn:

  • Các mục được tạo từ dưới lên, tức là chỉ mục 0 xuất hiện ở cuối màn hình.
  • Khi bạn di chuyển lên, các mục có chỉ mục cao hơn sẽ xuất hiện.

Để thêm hành vi hất và trượt cùng với bố cục đảo ngược, bạn có thể kết hợp flingBehaviorrotaryScrollableBehavior như minh hoạ trong đoạn mã sau:

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(scrollState = columnState) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding,
        reverseLayout = true,
        modifier = Modifier.fillMaxWidth()
    ) {
        items(10) { index ->
            Button(
                label = {
                    Text(
                        text = "Item ${index + 1}"
                    )
                },
                onClick = {},
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            )
        }
        item {
            // With reverseLayout = true, the last item declared appears at the top.
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text("Header")
            }
        }
    }
}

Các hình ảnh sau đây cho thấy sự khác biệt giữa danh sách thông thường và danh sách đảo ngược:

Một TransformingLazyColumn có bố cục thông thường, hiển thị Mục 1 ở trên cùng và các mục theo thứ tự tăng dần.
Hình 1. Một bố cục danh sách tiêu chuẩn, trong đó nội dung sẽ lấp đầy từ trên xuống dưới.
Một TransformingLazyColumn có bố cục đảo ngược, cho thấy Mục 1 ở dưới cùng và các mục theo thứ tự giảm dần lên trên cùng.
Hình 2. Bố cục danh sách đảo ngược, trong đó nội dung lấp đầy từ dưới lên trên.