Sửa đổi thứ tự duyệt qua

Thứ tự duyệt qua là thứ tự mà các dịch vụ hỗ trợ tiếp cận di chuyển qua các thành phần trên giao diện người dùng. Trong ứng dụng Compose, các phần tử được sắp xếp theo thứ tự đọc dự kiến, thường là từ trái sang phải, sau đó là từ trên xuống dưới. Tuy nhiên, có một số trường hợp Compose có thể cần thêm gợi ý để xác định thứ tự đọc chính xác.

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

Sử dụng isTraversalGrouptraversalIndex trong ứng dụng để kiểm soát thứ tự di chuyển của trình đọc màn hình.

Nhóm các phần tử để duyệt qua

isTraversalGroup là một thuộc tính boolean xác định xem nút ngữ nghĩa có phải là một nhóm duyệt qua hay không. Loại nút này có chức năng đóng vai trò là ranh giới hoặc đường viền trong việc sắp xếp các nút con.

Việc đặt isTraversalGroup = true trên một nút có nghĩa là tất cả các phần tử con của nút đó sẽ đượ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ấy tiêu đ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. Phương thức này phát ra 4 phần tử văn bản. Hai phần tử bên trái thuộc về một phần tử CardBox, trong khi 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 sẽ tạo ra kết quả tương tự như sau:

Bố cục có hai cột văn bản, trong đó cột bên trái có nội dung "Câu này ở cột bên trái" và cột bên phải có nội dung "Câu này ở bên phải".
Hình 1. Một bố cục có hai câu (một câu ở cột bên trái và một câu ở cột bên phải).

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

"Câu này nằm trong" → "Câu này là" → "cột bên trái." → "ở bên phải".

Để sắp xếp các mảnh một cách chính xác, hãy sửa đổi đoạn mã ban đầu để đặ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 đặt riêng trên mỗi CardBox, nên các ranh giới CardBox sẽ áp dụng khi sắp xếp các phần tử của chúng. Trong trường hợp này, CardBox bên trái đượ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 nằm trong" → "cột bên trái." → "Câu này là" → "ở bên phải."

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

traversalIndex là một thuộc tính float cho phép bạn tuỳ chỉnh thứ tự duyệt TalkBack. Nếu việc nhóm các phần tử lại với nhau không đủ để TalkBack hoạt động chính xác, hãy sử dụng traversalIndex kết hợp 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à dương hoặc âm.
  • Giá trị mặc định là 0f.
  • Để chỉ mục di chuyển ảnh hưởng đến hành vi di chuyển, bạn phải đặt chỉ mục này trên một thành phần mà các dịch vụ hỗ trợ tiếp cận có thể chọn và lấy tiêu điểm, chẳng hạn như các thành phần trên màn hình như văn bản hoặc nút.
    • Việc chỉ đặt traversalIndex trên, ví dụ: Column sẽ không có hiệu lực, trừ phi cột đó cũng được đặt isTraversalGroup.

Ví dụ sau đây cho thấy cách bạn có thể sử dụng traversalIndexisTraversalGroup cùng nhau.

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

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 đây, có một CircularLayout trong đó 12 số được vẽ, bắt đầu từ 12 và di chuyển theo chiều kim đồng hồ xung 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())
    }
}

Vì mặt đồng hồ không được đọc theo thứ tự mặc định từ trái sang phải và từ trên xuống dưới một cách hợp lý, nên TalkBack đọc các số không theo thứ tự. Để khắc phục vấn đề 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ạo CircularLayout thành một nhóm duyệt qua và thiết lập isTraversalGroup = true. Sau đó, khi mỗi văn bản đồng hồ được vẽ lên bố cục, hãy đặt traversalIndex tương ứng thành giá trị bộ đếm.

Vì giá trị bộ đếm liên tục tăng lên, nên traversalIndex của mỗi giá trị đồng hồ sẽ 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. Bằng cách này, TalkBack sẽ thiết lập thứ tự đọc các mục đó. Bây giờ, các số trong CircularLayout sẽ được đọc theo thứ tự dự kiến.

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

Xin lưu ý rằng nếu không đặt ngữ nghĩa CircularLayout's thành isTraversalGroup = true, các thay đổi về traversalIndex vẫn sẽ áp dụng. Tuy nhiên, nếu không có CircularLayout để liên kết các thành phần này, thì 12 chữ số của mặt đồng hồ sẽ được đọc sau cùng, 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ồ được đọc sau tất cả các phần tử 0f khác.

Những điều cần cân nhắc về API

Hãy cân nhắc những điều sau khi sử dụng API duyệt qua:

  • Bạn nên đặt isTraversalGroup = true trên phần tử mẹ chứa các phần tử được nhóm.
  • Bạn nên đặt traversalIndex trên một thành phần con chứa ngữ nghĩa và sẽ được các dịch vụ hỗ trợ tiếp cận chọn.
  • Đảm bảo rằng tất cả các phần tử bạn đang điều tra đều ở cùng cấp độ zIndex, vì điều đó cũng ảnh hưởng đến ngữ nghĩa và thứ tự truy cập.
  • Đảm bảo không hợp nhất ngữ nghĩa không cần thiết, vì điều này có thể ảnh hưởng đến các thành phần mà chỉ mục duyệt qua được áp dụng.