Compose에서 포커스

사용자는 앱과 상호작용할 때 화면의 요소를 터치하여 이뤄지는 경우가 많습니다. 그러나 이것이 유일한 상호작용 형식은 아닙니다. 다른 상호작용 양식은 다음과 같습니다.

  • ChromeOS 사용자는 물리적 키보드의 화살표 키를 사용하여 화면을 탐색할 수 있습니다.
  • 게임을 플레이하는 사용자는 연결된 게임 컨트롤러를 사용하여 게임의 메뉴를 탐색할 수 있습니다.
  • 모바일 앱 사용자는 터치 키보드를 사용하여 요소를 순환할 수 있습니다.

이러한 경우 특정 시점에 어떤 구성요소가 활성 상태인지 추적하는 것이 중요하며 이를 포커스라고 합니다. 화면의 요소에는 논리적 순서로 포커스가 있어야 합니다. Jetpack Compose에는 대부분의 경우 올바른 포커스를 처리하는 기본 방법이 있습니다. 그러나 경우에 따라 이 기본 동작을 수정해야 할 수도 있습니다.

다음 페이지에서는 앱에서 포커스를 사용하는 방법을 설명합니다.

  • 포커스 순회 순서 변경: 기본 포커스 순서를 변경하고 포커스 그룹을 추가하며 컴포저블의 포커스를 사용 중지하는 방법을 설명합니다.
  • 포커스 동작 변경: 포커스를 요청, 캡처, 해제하는 방법과 화면에 들어갈 때 포커스를 리디렉션하는 방법을 설명합니다.
  • 포커스에 반응: 포커스 변경에 반응하고 요소에 시각적 신호를 추가하며 요소의 포커스 상태를 이해하는 방법을 설명합니다.

기본 포커스 순회 순서

포커스 검색의 기본 동작을 살펴보기 전에 계층 구조에서 수준의 개념을 이해하는 것이 중요합니다. 일반적으로 말하자면 두 개의 Composables는 동위 요소일 때 동일한 수준에 있다고, 즉 상위 요소가 동일함을 의미합니다. 예를 들어 Column 내부의 요소는 같은 수준에 있습니다. 레벨을 높이는 것은 하위 요소에서 Composable 상위 요소로 이동하거나 동일한 예를 유지하여 항목에서 해당 항목을 포함하는 Column로 돌아가는 것을 의미합니다. 한 레벨 아래로 내려가는 것은 Column 상위 요소에서 포함된 항목으로의 반대입니다. 이 개념은 다른 Composables를 포함할 수 있는 모든 Composable에 적용할 수 있습니다.

UI 탐색은 여러 가지 방법으로 발생할 수 있으며 대부분의 사용자는 이미 다음을 알고 있을 것입니다.

  • 탭: 앞으로 또는 뒤로 이동하는 1차원 탐색 기능입니다. 탭 탐색은 계층 구조의 다음 또는 이전 요소로 포커스를 이동합니다. 기본적으로 Compose는 Composables 선언을 따릅니다. 한 방향 탐색은 키보드의 tab 키 또는 시계의 로터리 베젤을 통해 달성할 수 있으며 이러한 유형의 포커스 검색은 화면의 각 요소를 방문합니다.
  • 화살표 키: 2차원 탐색(왼쪽, 오른쪽, 위쪽, 아래로 이동) 2차원 탐색은 TV의 D패드나 키보드의 화살표 키를 통해 실행할 수 있으며 순회 순서는 지정된 수준의 요소만 방문합니다. D패드 중앙과 뒤로 버튼을 사용하여 아래로 내려갔다가 다른 수준으로 위로 올라갈 수 있습니다.

아래 스크린샷을 예로 들어보겠습니다. 버튼이 4개 아래에 하나씩 있고, 모양 순서대로 모두 돌아가고자 하는 경우입니다. Jetpack Compose는 이 동작을 즉시 제공합니다. 도구 키트를 사용하면 tab 키를 사용하여 각 컴포저블을 위에서 아래로 세로 순서로 순환하거나 위쪽 또는 아래쪽 화살표를 눌러 포커스를 이동할 수 있습니다.

작은 폼 팩터에서 세로로 나란히 배치된 버튼 목록의 스크린샷
그림 1. 작은 폼 팩터로 표시되는 버튼 목록

다른 종류의 레이아웃으로 전환하면 상황이 약간 변경됩니다. 아래 레이아웃과 같이 레이아웃에 열이 두 개 이상 있는 경우 Jetpack Compose를 사용하면 코드를 추가하지 않고도 열을 탐색할 수 있습니다. tab 키를 누르면 Jetpack Compose는 선언 순서에 따라 항목을 1번째부터 4번째까지 자동으로 강조표시합니다. 키보드의 화살표 키를 사용하면 2D 공간에서 원하는 방향으로 선택할 수 있습니다.

Column {
    Row {
        TextButton({ }) { Text("First field") }
        TextButton({ }) { Text("Second field") }
    }
    Row {
        TextButton({ }) { Text("Third field") }
        TextButton({ }) { Text("Fourth field") }
    }
}

Composables는 두 개의 Rows에서 선언되고 포커스 요소가 첫 번째부터 네 번째까지 순서대로 선언됩니다. tab 키를 누르면 다음과 같은 포커스 순서가 생성됩니다.

더 큰 폼 팩터에서 두 열에 나란히 배치된 버튼 목록의 스크린샷
그림 2. 더 큰 폼 팩터에서 두 열에 나란히 배치된 버튼 목록

아래 스니펫에서는 Rows가 아닌 Columns에 항목을 선언합니다.

Row {
    Column {
        TextButton({ }) { Text("First field") }
        TextButton({ }) { Text("Second field") }
    }
    Column {
        TextButton({ }) { Text("Third field") }
        TextButton({ }) { Text("Fourth field") }
    }
}

이 레이아웃은 화면의 시작 지점에서 끝까지 항목을 위에서 아래로 수직으로 순회합니다.

더 큰 폼 팩터에서 두 열에 나란히 배치된 버튼 목록의 스크린샷
그림 3. 더 큰 폼 팩터에서 두 열에 나란히 배치된 버튼 목록

앞의 두 샘플은 단방향 탐색에서는 다르지만 2차원 탐색과 관련하여 동일한 환경을 제공합니다. 이는 일반적으로 화면에 표시된 항목의 지리적 위치가 두 예에서 동일하기 때문입니다. 첫 번째 Column에서 바로 이동하면 포커스가 두 번째로 이동하고 첫 번째 Row에서 아래로 이동하면 포커스가 그 아래의 항목으로 이동합니다.