Kiểm soát thứ tự duyệt qua

Theo mặc định, hành vi của trình đọc màn hình hỗ trợ tiếp cận trong ứng dụng Compose được triển khai theo thứ tự đọc dự kiến, thường là từ trái sang phải, sau đó từ trên xuống dưới. Tuy nhiên, có một số loại bố cục ứng dụng mà thuật toán không thể xác định thứ tự đọc thực tế mà không cần thêm gợi ý. Trong ứng dụng dựa trên chế độ xem, bạn có thể hãy khắc phục những vấn đề đó bằng cách sử dụng thuộc tính traversalBeforetraversalAfter. Kể từ Compose 1.5, Compose cung cấp một API linh hoạt không kém, nhưng với một mô hình khái niệm mới.

isTraversalGrouptraversalIndex là các thuộc tính ngữ nghĩa cho phép bạn kiểm soát khả năng hỗ trợ tiếp cận và thứ tự lấy nét TalkBack trong các trường hợp mà thuật toán sắp xếp mặc định là không phù hợp. isTraversalGroup nhận dạng các nhóm quan trọng về mặt ngữ nghĩa, trong khi traversalIndex điều chỉnh thứ tự của từng yếu tố riêng lẻ trong các nhóm đó. Bạn có thể sử dụng riêng isTraversalGroup, hoặc bằng traversalIndex để tuỳ chỉnh thêm.

Sử dụng isTraversalGrouptraversalIndex trong để kiểm soát thứ tự duyệt qua trình đọc màn hình.

Nhóm các phần tử bằng isTraversalGroup

isTraversalGroup là thuộc tính boolean xác định liệu ngữ nghĩa nút là một nhóm truyền tải. Đây là loại nút có chức năng phân phát làm ranh giới hoặc đường viền để tổ chức các nút con của nút.

Việc đặt isTraversalGroup = true trên một nút có nghĩa là tất cả phần tử con của nút đó được truy cập trước khi chuyển sang các phần tử khác. Bạn có thể đặt isTraversalGroup trên các nút không thể làm tâm điểm của trình đọc màn hình, chẳng hạn như Cột, Hàng hoặc Hộp.

Ví dụ sau đây sử dụng isTraversalGroup. Ứng dụng này phát hành 4 thành phần văn bản. Chiến lược phát hành đĩa đơn hai phần tử bên trái thuộc về một phần tử CardBox, còn hai phần tử bên phải thuộc về một phần tử CardBox khác:

// CardBox() function takes in top and bottom sample text.
@Composable
fun CardBox(
    topSampleText: String,
    bottomSampleText: String,
    modifier: Modifier = Modifier
) {
    Box(modifier) {
        Column {
            Text(topSampleText)
            Text(bottomSampleText)
        }
    }
}

@Composable
fun TraversalGroupDemo() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is "
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
            topSampleText1,
            bottomSampleText1
        )
        CardBox(
            topSampleText2,
            bottomSampleText2
        )
    }
}

Mã này tạo ra kết quả tương tự như sau:

Bố cục có hai cột văn bản, với cột bên trái ghi là "This
  câu nằm ở cột bên trái và cột bên phải ghi là 'Câu này nằm ở bên phải'.
Hình 1. Bố cục có hai câu (một câu ở bên trái và một ở cột bên phải).

Vì chưa có ngữ nghĩa nào được thiết lập nên hành vi mặc định của trình đọc màn hình là để di chuyển các phần tử từ trái sang phải và từ trên xuống dưới. Vì lý do này Theo mặc định, TalkBack sẽ đọc các đoạn câu theo thứ tự không chính xác:

"Câu này bằng" → "Câu này là" → "cột bên trái". → "trên đúng vậy".

Để sắp xếp các mảnh một cách chính xác, hãy sửa đổi đoạn mã gốc để đặt isTraversalGroup thành true:

@Composable
fun TraversalGroupDemo2() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is"
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
//      1,
            topSampleText1,
            bottomSampleText1,
            Modifier.semantics { isTraversalGroup = true }
        )
        CardBox(
//      2,
            topSampleText2,
            bottomSampleText2,
            Modifier.semantics { isTraversalGroup = true }
        )
    }
}

isTraversalGroup được thiết lập riêng trên từng CardBox, nên CardBox áp dụng các ranh giới khi sắp xếp các phần tử. Trong trường hợp này, bên trái CardBox được đọc trước, sau đó là CardBox bên phải.

Giờ đây, TalkBack sẽ đọc các mảnh câu theo đúng thứ tự:

"Câu này bằng" → "cột bên trái". → "Câu này là" → "trên đúng vậy".

Tuỳ chỉnh thêm thứ tự duyệt qua

traversalIndex là một thuộc tính độ chính xác đơn cho phép bạn tuỳ chỉnh TalkBack thứ tự duyệt qua. Nếu TalkBack không đủ khả năng để nhóm các phần tử lại với nhau hoạt động chính xác, hãy sử dụng traversalIndex cùng với isTraversalGroup để tuỳ chỉnh thêm thứ tự của trình đọc màn hình.

Thuộc tính traversalIndex có các đặc điểm sau:

  • Các phần tử có giá trị traversalIndex thấp hơn sẽ được ưu tiên trước.
  • Có thể là tích cực hoặc tiêu cực.
  • Giá trị mặc định là 0f.
  • Chỉ ảnh hưởng đến các nút có thể làm tâm điểm của trình đọc màn hình, chẳng hạn như các phần tử trên màn hình như văn bản hoặc nút. Ví dụ: chỉ đặt traversalIndex trên một cột sẽ không có hiệu lực, trừ phi cột này cũng đã đặt isTraversalGroup.

Ví dụ sau đây trình bày cách sử dụng traversalIndexisTraversalGroup cùng nhau.

Ví dụ: Mặt đồng hồ dạng ngang

Mặt đồng hồ là trường hợp phổ biến khi thứ tự truyền tải tiêu chuẩn không cơ quan. Ví dụ trong phần này là bộ chọn giờ mà người dùng có thể chuyển tải thông qua các số trên mặt đồng hồ và chọn các chữ số cho giờ và phút vị trí.

Mặt đồng hồ có bộ chọn giờ ở phía trên.
Hình 2. Hình ảnh mặt đồng hồ.

Trong đoạn mã đơn giản sau, có một CircularLayout trong đó 12 các số được vẽ, bắt đầu từ số 12 và di chuyển theo chiều kim đồng hồ quanh vòng tròn:

@Composable
fun ClockFaceDemo() {
    CircularLayout {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier) {
        Text((if (value == 0) 12 else value).toString())
    }
}

Do mặt đồng hồ không được đọc hợp lý theo mặc định từ trái sang phải và từ trên xuống dưới, TalkBack sẽ đọc các số theo thứ tự. Để khắc phục này, hãy sử dụng giá trị bộ đếm tăng dần như trong đoạn mã sau:

@Composable
fun ClockFaceDemo() {
    CircularLayout(Modifier.semantics { isTraversalGroup = true }) {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier.semantics { this.traversalIndex = value.toFloat() }) {
        Text((if (value == 0) 12 else value).toString())
    }
}

Để thiết lập đúng thứ tự duyệt qua, trước tiên, hãy đặt CircularLayout làm nhóm truyền tải và đặt isTraversalGroup = true. Sau đó, khi mỗi văn bản trên đồng hồ được vẽ vào bố cục, đặt traversalIndex tương ứng của nó thành bộ đếm giá trị.

Do giá trị bộ đếm liên tục tăng, nên mỗi giá trị xung nhịp traversalIndex lớn hơn khi các số được thêm vào màn hình – giá trị đồng hồ 0 có traversalIndex là 0 và giá trị đồng hồ 1 có traversalIndex là 1. Theo cách này, thứ tự mà TalkBack đọc các âm thanh đó sẽ được đặt. Giờ đây, các con số bên trong CircularLayout sẽ được đọc theo thứ tự dự kiến.

traversalIndexes đã được đặt chỉ liên quan đến các giá trị khác chỉ mục trong cùng một nhóm, phần còn lại của thứ tự màn hình sẽ được không bị ảnh hưởng. Nói cách khác, những thay đổi về ngữ nghĩa được thể hiện trong mã trước đó đoạn mã chỉ sửa đổi thứ tự trong mặt đồng hồ có Đã đặt isTraversalGroup = true.

Lưu ý rằng nếu không đặt ngữ nghĩa CircularLayout's thành isTraversalGroup = true, các thay đổi đối với traversalIndex vẫn được áp dụng. Tuy nhiên, nếu không có CircularLayout để ràng buộc các thành phần này, 12 chữ số của mặt đồng hồ sẽ được đọc là sau khi tất cả các thành phần khác trên màn hình đã được truy cập. Điều này xảy ra vì tất cả các phần tử khác đều có traversalIndex mặc định là 0f và các phần tử văn bản đồng hồ sẽ được đọc sau tất cả các phần tử 0f khác.

Ví dụ: Tuỳ chỉnh thứ tự duyệt qua cho nút hành động nổi

Trong ví dụ này, traversalIndexisTraversalGroup kiểm soát thứ tự duyệt của nút hành động nổi trong Material Design (FAB). Cơ sở của ví dụ này là bố cục sau:

Bố cục có thanh ứng dụng trên cùng, văn bản mẫu, nút hành động nổi và
  thanh ứng dụng ở dưới cùng.
Hình 3. Bố cục có thanh ứng dụng trên cùng, văn bản mẫu, nút hành động nổi, và một thanh ứng dụng ở dưới cùng.

Theo mặc định, bố cục trong ví dụ này có thứ tự TalkBack sau:

Thanh ứng dụng trên cùng → Văn bản mẫu từ 0 đến 6 → nút hành động nổi (FAB) → Dưới cùng Thanh ứng dụng

Trước tiên, bạn nên đặt trình đọc màn hình tập trung vào nút hành động nổi. Để đặt một traversalIndex trên một phần tử Material như FAB, hãy làm như sau:

@Composable
fun FloatingBox() {
    Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) {
        FloatingActionButton(onClick = {}) {
            Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon")
        }
    }
}

Trong đoạn mã này, việc tạo một hộp có Đã đặt isTraversalGroup thành true và đặt traversalIndex trên cùng một hộp (-1f thấp hơn giá trị mặc định của 0f) có nghĩa là hộp thả nổi xuất hiện trước mọi thành phần khác trên màn hình.

Tiếp theo, bạn có thể đặt hộp nổi và các thành phần khác vào một scaffold (giàn giáo). triển khai bố cục Material Design:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColumnWithFABFirstDemo() {
    Scaffold(
        topBar = { TopAppBar(title = { Text("Top App Bar") }) },
        floatingActionButtonPosition = FabPosition.End,
        floatingActionButton = { FloatingBox() },
        content = { padding -> ContentColumn(padding = padding) },
        bottomBar = { BottomAppBar { Text("Bottom App Bar") } }
    )
}

TalkBack tương tác với các phần tử theo thứ tự sau:

FAB → Thanh ứng dụng trên cùng → Văn bản mẫu từ 0 đến 6 → Thanh ứng dụng dưới cùng

Tài nguyên khác