순회 순서는 접근성 서비스가 UI 요소를 탐색하는 순서입니다. Compose 앱에서는 요소가 예상되는 읽기 순서(일반적으로 왼쪽에서 오른쪽, 위에서 아래로)로 정렬됩니다. 그러나 Compose에서 올바른 읽기 순서를 결정하기 위해 추가 힌트가 필요한 시나리오도 있습니다.
isTraversalGroup
및 traversalIndex
는 Compose의 기본 정렬 알고리즘이 충분하지 않은 시나리오에서 접근성 서비스의 탐색 순서에 영향을 줄 수 있는 시맨틱 속성입니다. isTraversalGroup
는 맞춤설정이 필요한 의미론적으로 중요한 그룹을 식별하고 traversalIndex
는 이러한 그룹 내 개별 요소의 순서를 조정합니다.
isTraversalGroup
만 사용하여 그룹 내의 모든 요소를 함께 선택해야 함을 나타내거나 traversalIndex
와 함께 사용하여 추가로 맞춤설정할 수 있습니다.
앱에서 isTraversalGroup
및 traversalIndex
를 사용하여 스크린 리더 탐색 순서를 제어합니다.
탐색을 위한 요소 그룹화
isTraversalGroup
는 시맨틱 노드가 탐색 그룹인지 여부를 정의하는 불리언 속성입니다. 이 유형의 노드는 노드의 하위 요소를 구성할 때 경계 또는 테두리 역할을 하는 기능을 합니다.
노드에 isTraversalGroup = true
를 설정하면 다른 요소로 이동하기 전에 해당 노드의 모든 하위 요소가 방문됩니다. 열, 행, 상자와 같이 스크린 리더에서 포커스를 설정할 수 없는 노드에 isTraversalGroup
를 설정할 수 있습니다.
다음 예에서는 isTraversalGroup
를 사용합니다. 4개의 텍스트 요소를 내보냅니다. 왼쪽 두 요소는 하나의 CardBox
요소에 속하고 오른쪽 두 요소는 다른 CardBox
요소에 속합니다.
// 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 ) } }
이 코드는 다음과 비슷한 출력을 생성합니다.

시맨틱이 설정되지 않았으므로 스크린 리더의 기본 동작은 요소를 왼쪽에서 오른쪽으로, 위에서 아래로 탐색하는 것입니다. 이 기본값으로 인해 TalkBack은 문장 조각을 잘못된 순서로 읽습니다.
'이 문장은' → '이 문장은' → '왼쪽 열에 있습니다.' → '오른쪽'
프래그먼트 순서를 올바르게 지정하려면 원본 스니펫을 수정하여 isTraversalGroup
를 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
는 각 CardBox
에 구체적으로 설정되므로 요소를 정렬할 때 CardBox
경계가 적용됩니다. 이 경우 왼쪽 CardBox
가 먼저 읽힌 다음 오른쪽 CardBox
가 읽힙니다.
이제 TalkBack에서 문장 프래그먼트를 올바른 순서로 읽어줍니다.
'이 문장은' → '왼쪽 열에 있습니다.' → '이 문장은' → '옳습니다.'
순회 순서 맞춤설정
traversalIndex
는 TalkBack 탐색 순서를 맞춤설정할 수 있는 부동 소수점 속성입니다. 요소를 그룹화하는 것만으로는 TalkBack이 올바르게 작동하지 않는 경우 traversalIndex
를 isTraversalGroup
와 함께 사용하여 스크린 리더 순서를 추가로 맞춤설정합니다.
traversalIndex
속성의 특성은 다음과 같습니다.
traversalIndex
값이 낮은 요소가 우선순위가 높습니다.- 양수 또는 음수일 수 있습니다.
- 기본값은
0f
입니다. - 탐색 색인이 탐색 동작에 영향을 주려면 텍스트나 버튼과 같은 화면 요소와 같이 접근성 서비스에서 선택하고 포커스를 설정할 수 있는 구성요소에 설정해야 합니다.
- 예를 들어
Column
에traversalIndex
만 설정하면 열에isTraversalGroup
도 설정되어 있지 않으면 아무런 효과가 없습니다.
- 예를 들어
다음 예는 traversalIndex
와 isTraversalGroup
를 함께 사용하는 방법을 보여줍니다.
시계 화면은 표준 순회 순서가 작동하지 않는 일반적인 시나리오입니다. 이 섹션의 예는 사용자가 시계 화면의 숫자를 탐색하고 시간 및 분 슬롯의 숫자를 선택할 수 있는 시간 선택 도구입니다.

다음의 단순화된 스니펫에는 12부터 시작하여 원을 시계 방향으로 이동하면서 12개의 숫자가 그려진 CircularLayout
가 있습니다.
@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()) } }
시계 화면은 기본 왼쪽에서 오른쪽, 위에서 아래로 순서대로 논리적으로 읽히지 않으므로 TalkBack에서는 숫자를 순서대로 읽지 않습니다. 이 문제를 해결하려면 다음 스니펫과 같이 증가하는 카운터 값을 사용하세요.
@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()) } }
탐색 순서를 올바르게 설정하려면 먼저 CircularLayout
를 탐색 그룹으로 만들고 isTraversalGroup = true
를 설정합니다. 그런 다음 각 시계 텍스트가 레이아웃에 그려질 때 해당 traversalIndex
를 카운터 값으로 설정합니다.
카운터 값은 계속 증가하므로 숫자가 화면에 추가될수록 각 시계 값의 traversalIndex
가 더 커집니다. 시계 값 0의 traversalIndex
는 0이고 시계 값 1의 traversalIndex
는 1입니다.
이렇게 하면 TalkBack에서 읽는 순서가 설정됩니다. 이제 CircularLayout
내의 숫자가 예상 순서로 읽힙니다.
설정된 traversalIndexes
는 동일한 그룹 내의 다른 색인에만 상대적이므로 나머지 화면 순서는 유지되었습니다. 즉, 위의 코드 스니펫에 표시된 시맨틱 변경사항은 isTraversalGroup = true
가 설정된 시계 화면 내에서의 순서만 수정합니다.
CircularLayout's
시맨틱스를 isTraversalGroup =
true
로 설정하지 않아도 traversalIndex
변경사항은 계속 적용됩니다. 그러나 이를 결합할 CircularLayout
가 없으면 시계 화면의 12자리 숫자가 화면의 다른 모든 요소가 방문된 후에 마지막으로 읽힙니다. 이는 다른 모든 요소의 기본 traversalIndex
가 0f
이고 시계 텍스트 요소가 다른 모든 0f
요소 뒤에 읽히기 때문에 발생합니다.
API 고려사항
탐색 API를 사용할 때는 다음을 고려하세요.
isTraversalGroup = true
는 그룹화된 요소가 포함된 상위 요소에 설정해야 합니다.traversalIndex
는 시맨틱을 포함하고 접근성 서비스에서 선택할 하위 구성요소에 설정해야 합니다.- 조사하는 모든 요소가 동일한
zIndex
수준에 있는지 확인합니다. 이는 의미론과 탐색 순서에도 영향을 미치기 때문입니다. - 불필요하게 의미론이 병합되지 않도록 합니다. 이는 구성요소 탐색 색인이 적용되는 위치에 영향을 줄 수 있습니다.
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- Compose의 접근성
- [Compose의 Material Design 2][19]
- Compose 레이아웃 테스트