시배's Android

Compose Docs | Semantics in Compose 본문

Android/Compose Docs

Compose Docs | Semantics in Compose

si8ae 2023. 8. 13. 17:32
 

Compose의 시맨틱  |  Jetpack Compose  |  Android Developers

Compose의 시맨틱 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 컴포지션은 앱의 UI를 설명하고 컴포저블을 실행하여 생성됩니다. 컴포지션은 UI를 설명하는

developer.android.com

만약 당신의 앱이 Compose foundation material 라이브러리에서 제공하는 composables modifiers 구성되어 있다면, Semantics 트리는 자동으로 생성되어 채워집니다. 그러나 사용자 정의 저수준 composables 추가하는 경우에는 수동으로 해당 semantics 제공해야 있습니다. 화면의 요소의 의미를 정확하게 또는 완전히 나타내지 못하는 상황이 발생할 수도 있으며, 이러한 경우에는 트리를 조정할 있습니다.

예시에서 전체 달력은 하나의 저수준 컴포저블로 구현되었으며, Layout 컴포저블을 사용하여 직접 Canvas 그리는 방식으로 처리되었습니다. 아무런 추가 작업을 하지 않는다면, 접근성 서비스는 컴포저블의 내용 사용자가 달력 내에서 선택한 항목에 대한 정보를 충분히 받아오지 못할 것입니다. 예를 들어, 사용자가 17 들어 있는 날을 클릭하는 경우, 접근성 프레임워크는 달력 컨트롤 전체에 대한 설명 정보만을 받게 됩니다. 경우 TalkBack 접근성 서비스는 단순히 "달력"이나, 조금 나은 경우에는 "4 달력"이라고 발음할 것이며 사용자는 어떤 날짜가 선택되었는지 궁금한 채로 남게 것입니다. 컴포저블을 접근 가능하게 만들려면 수동으로 의미 정보를 추가해야 합니다.

Semantics properties

UI 트리의 모든 노드 중 의미 있는 몇 가지 노드는 Semantics 트리에 해당하는 병렬 노드가 있습니다. Semantics 트리의 노드에는 해당 컴포저블의 의미를 전달하는 데 필요한 속성이 포함되어 있습니다. 예를 들어, Text 컴포저블에는 semantic 속성인 텍스트가 포함되어 있습니다. 왜냐하면 그것이 해당 컴포저블의 의미이기 때문입니다. Icon은 (개발자가 설정한 경우) 아이콘의 의미를 텍스트로 전달하는 contentDescription 속성을 포함합니다. Compose foundation 라이브러리를 기반으로 하는 컴포저블과 모디파이어는 이미 관련 속성을 자동으로 설정해줍니다. 선택적으로 semantics 및 clearAndSetSemantics 모디파이어를 사용하여 속성을 직접 설정하거나 덮어쓸 수도 있습니다. 예를 들어, 특정 노드에 사용자 정의 접근성 작업을 추가하거나 전환 가능한 요소에 대한 대체 상태 설명을 제공하거나 특정 텍스트 컴포저블을 제목으로 간주해야 할 수 있습니다.

Semantics 트리를 시각화하기 위해 Layout Inspector 도구를 사용하거나 테스트 내에서 printToLog() 메서드를 사용할 있습니다. 이렇게 하면 현재 Semantics 트리가 Logcat 내에 인쇄됩니다.

class MyComposeTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun MyTest() {
        // Start the app
        composeTestRule.setContent {
            MyTheme {
                Text("Hello world!")
            }
        }
        // Log the full semantics tree
        composeTestRule.onRoot().printToLog("MY TAG")
    }
}
  Printing with useUnmergedTree = 'false'
    Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
     |-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
       Text = '[Hello world!]'
       Actions = [GetTextLayoutResult]

의미를 전달하기 위해 semantics 속성이 어떻게 사용되는지 살펴보기 위해 예제를 살펴봅시다. 스위치(Switch) 대해 생각해보겠습니다. 사용자에게 보이는 모습은 다음과 같습니다:

이 요소의 의미를 설명하기 위해 다음과 같이 말할 수 있습니다: "이것은 토글 가능한 요소인 스위치입니다. 현재 '켜짐' 상태입니다. 클릭하여 상호 작용할 수 있습니다."

이것이 바로 semantics 속성이 사용되는 방식입니다. 이 스위치 요소의 semantics 노드는 다음과 같은 속성을 포함하며, Layout Inspector를 통해 시각화됩니다:

Role은 우리가 보고 있는 요소의 유형을 나타냅니다. StateDescription은 "켜짐" 상태를 어떻게 참조해야 하는지를 설명합니다. 기본적으로 이는 "켜짐"이라는 단어의 로컬화된 버전일 수 있지만, 상황에 따라 더 구체적인 내용("활성화됨"과 같은)으로 변경할 수 있습니다. ToggleableState는 스위치의 현재 상태를 나타냅니다. OnClick 속성은 이 요소와 상호 작용하는 데 사용되는 메서드를 참조합니다. SemanticsProperties 객체에서 가능한 모든 semantics 속성 목록을 확인할 수 있습니다. 또한 SemanticsActions 객체에서 가능한 모든 접근성 작업 목록을 확인할 수 있습니다.

앱의 각 컴포저블의 semantics 속성을 추적하면 강력한 가능성을 개방할 수 있습니다. 일부 예시:

Talkback은 화면에 표시된 내용을 읽어주고 사용자가 원활하게 상호 작용할 수 있도록 속성을 사용합니다. 우리의 스위치의 경우 "켜짐; 스위치; 두 번 탭하여 토글"과 같이 말할 수 있습니다. 사용자는 화면을 두 번 탭하여 스위치를 끌 수 있습니다.

테스트 프레임워크는 속성을 사용하여 노드를 찾고 상호 작용하며 단언을 만듭니다. 스위치에 대한 샘플 테스트는 다음과 같을 있습니다:

val mySwitch = SemanticsMatcher.expectValue(
    SemanticsProperties.Role, Role.Switch
)
composeTestRule.onNode(mySwitch)
    .performClick()
    .assertIsOff()

Merged and unmerged Semantics tree

이전에 언급한 대로, UI 트리의 각 컴포저블은 0개 이상의 semantics 속성을 설정할 수 있습니다. 컴포저블에 semantics 속성이 설정되지 않은 경우, 그 컴포저블은 Semantics 트리의 일부로 포함되지 않습니다. 이렇게 함으로써 Semantics 트리에는 실제 의미를 가진 노드만 포함되게 됩니다. 그러나 때로는 화면에 표시된 내용의 올바른 의미를 전달하기 위해 특정 하위 트리의 노드를 병합하고 하나로 취급하는 것이 유용할 수도 있습니다. 이렇게 함으로써 우리는 각 하위 노드를 개별적으로 처리하는 대신 일련의 노드에 대해 전체적으로 판단할 수 있습니다. 일반적인 지침으로, 이 트리의 각 노드는 접근성 서비스를 사용할 때 포커스 가능한 요소를 나타냅니다.

이러한 컴포저블의 예는 Button입니다. 우리는 버튼을 단일 요소로 판단하고자 하지만 여러 개의 하위 노드를 포함할 있을 있습니다:

Button(onClick = { /*TODO*/ }) {
    Icon(
        imageVector = Icons.Filled.Favorite,
        contentDescription = null
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Like")
}

우리의 Semantics 트리에서 Button 자손들의 속성이 병합되며, Button 트리 내에서 단일 리프 노드로 표시됩니다:

컴포저블과 모디파이어는 Modifier.semantics (mergeDescendants = true) {}를 호출하여 자손의 semantics 속성을 병합하려는 의도를 나타낼 수 있습니다. 이 속성을 true로 설정하면 semantics 속성이 병합되어야 함을 나타냅니다. 예를 들어 우리의 버튼 예시에서 Button 컴포저블은 내부적으로 클릭 가능한 모디파이어를 사용하며 이 모디파이어에 이 semantics 모디파이어가 포함됩니다. 따라서 Button의 자손 노드가 병합됩니다. 컴포저블에서 병합 동작을 언제 변경해야 하는지에 대해 자세히 알아보려면 접근성 문서를 참조하십시오.

Foundation 및 Material Compose 라이브러리의 몇 가지 모디파이어와 컴포저블은 이 속성이 설정되어 있습니다. 예를 들어, clickable 및 toggleable 모디파이어는 자동으로 자손을 병합합니다. 또한 ListItem 컴포저블도 자손을 병합합니다.

Inspecting the trees

Semantics 트리에 대해 이야기할 때 실제로 두 가지 다른 트리에 대해 이야기하고 있는 것입니다. mergeDescendants가 true로 설정된 경우 자손 노드를 병합하는 병합된 Semantics 트리와, 병합을 적용하지 않고 모든 노드를 그대로 유지하는 병합되지 않은 Semantics 트리가 있습니다. 접근성 서비스는 병합되지 않은 트리를 사용하고 자신의 병합 알고리즘을 적용하며 mergeDescendants 속성을 고려합니다. 테스트 프레임워크는 기본적으로 병합된 트리를 사용합니다.

printToLog() 메서드를 사용하여 트리를 모두 검사할 있습니다. 기본적으로 이전 예시와 같이 병합된 트리가 로그됩니다. 대신 병합되지 않은 트리를 인쇄하려면 onRoot() 매처의 useUnmergedTree 매개변수를 true 설정하십시오:

composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")

레이아웃 인스펙터는 선호하는 트리를 보기 필터에서 선택하여 병합된 Semantics 트리와 병합되지 않은 Semantics 트리를 모두 표시할 있도록 합니다:

트리의 노드에 대해 레이아웃 인스펙터는 속성 패널에서 해당 노드에 설정된 병합된 Semantics Semantics 모두 보여줍니다:

기본적으로 테스트 프레임워크의 매처는 병합된 Semantics 트리를 사용합니다. 그래서 버튼 내부에 표시된 텍스트를 일치시켜서 버튼과 상호 작용할 있습니다:

composeTestRule.onNodeWithText("Like").performClick()

동작을 무시하려면 매처의 useUnmergedTree 매개변수를 true 설정하여 이전에 onRoot 매처에서 것과 같은 방식으로 재정의할 있습니다.

Merging behavior

한 컴포저블이 자손을 병합해야 한다고 지정하는 경우, 이 병합은 정확히 어떻게 이루어지나요?

각 semantics 속성은 정의된 병합 전략이 있습니다. 예를 들어, ContentDescription 속성은 모든 하위 ContentDescription 값을 목록에 추가합니다. SemanticsProperties.kt에서 mergePolicy 구현을 확인하여 속성의 병합 전략을 확인할 수 있습니다. 속성은 부모 또는 자식 값을 항상 선택하거나 값을 목록이나 문자열로 병합하거나 병합을 허용하지 않고 대신 예외를 throw하거나 다른 사용자 정의 병합 전략을 선택할 수 있습니다.

중요한 참고 사항은 자손이 자체적으로 mergeDescendants = true를 설정한 경우 병합에 포함되지 않습니다. 예를 들어 다음 예시를 살펴보겠습니다:

여기에는 클릭 가능한 목록 항목이 있습니다. 사용자가 행을 누르면 앱이 기사 상세 페이지로 이동하여 사용자가 기사를 읽을 있게 됩니다. 목록 항목 내에는 기사를 즐겨찾기에 추가하는 버튼이 있습니다. 경우에는 중첩된 클릭 가능한 요소가 있으므로 버튼은 병합된 트리에서 별도로 표시됩니다. 행의 나머지 내용은 병합됩니다:

Adapting the Semantics tree

이전에 언급한 대로 특정 semantics 속성을 무시하거나 지우거나 트리의 병합 동작을 변경할 있습니다. 특히 사용자 정의 구성 요소를 생성할 작업이 중요합니다. 올바른 속성과 병합 동작을 설정하지 않으면 앱의 접근성이 보장되지 않을 있으며, 테스트는 예상과 다르게 작동할 있습니다. Semantics 트리를 조정해야 하는 가지 일반적인 사용 사례에 대해 자세히 알아보려면 접근성 문서를 참조하십시오. 테스트에 대해 알아보려면 테스트 가이드를 확인하십시오.