스마트폰 네이티브 앱 하드웨어 키보드 접근성 구현하기 2부: 안드로이드 시스템 포커스
안녕하세요, 엔비전스 입니다.
지난 아티클에서는 안드로이드 하드웨어 키보드에서의 일반 초점과 접근성 초점을 중점적으로 다루었습니다. 이번 아티클에서는 시스템 포커스에 대해 다루도록 하겠습니다.
초점을 보낼 수 있는 요소
기본적으로 웹, 응용프로그램 등 모든 플랫폼에서 시스템 포커스는 키보드로 상호 작용이 가능한 요소에만 초점이 이동됩니다. 웹페이지에서는 키보드와 상호 작용이 가능한 컨트롤(버튼, 링크, 체크박스) 등을 사용하면 탭 키를 눌러 각 요소에 초점을 보낼 수 있습니다. 또한 라디오 버튼을 사용하면 탭 키를 눌렀을 때는 라디오 하위 요소가 6개 이상이라 하더라도 단 하나의 요소에만 초점이 이동됩니다.
그렇다면 안드로이드에서는 어떤 곳에 초점이 이동되며 시스템에서 키보드로 상호 작용이 가능하다는 것을 어떤 기준으로 판단할까요?
- 누르기(onClick), 길게 누르기(onLongClick) 동작이 가능한 요소에는 컨트롤 유형과 상관없이 키보드 초점을 보낼 수 있게 되며, Enter나 Space를 눌러 실행할 수 있습니다. 예를 들어 버튼을 사용하면 버튼이라는 컨트롤 유형 자체가 상호 작용이 가능하므로 당연히 키보드 초점이 제공됩니다. 그런데 TextView는 웹에서는 p 요소와 유사하여 텍스트를 표시할 때 사용하는 요소이기 때문에 기본적으로는 키보드 초점이 제공되지 않습니다. 하지만 TextView에 클릭 속성을 추가하는 순간 상호 작용이 가능한 요소로 시스템에서 판단하므로 사용자가 초점을 보낼 수 있으며 실행 또한 가능합니다.
- Button, android.R.layout.simple_list_item_1과 같은 요소들은 onClickListener 혹은 onLongClickListener 이벤트로 동작을 추가하지 않더라도 시스템에서 이미 클릭 이벤트를 가지고 있기 때문에 XML 상에 마크업만 하여도 키보드 초점이 제공됩니다. 따라서 앱 로고를 버튼으로 사용하거나 클릭 이벤트를 기본적으로 가지고 있는 요소에 단순한 텍스트 요소만 구현하는 경우에는 android:enabled = “false”를 통하여 키보드에서 초점이 제공되지 않도록 구현하거나 기본 누르기 동작을 제거해야 합니다. 이것은 하드웨어 키보드뿐만 아니라 Talkback에서 출력하는 힌트 메시지에도 영향을 주므로 단순한 로고를 사용할 때 버튼과 같이 액션을 기본적으로 가지고 있는 컨트롤을 사용하는 것은 올바른 방법이 아닙니다.
Tab과 화살표 키
Tab, Shift + Tab은 웹 페이지와 마찬가지로 키보드와 상호 작용이 가능한 요소를 순차 탐색하는 역할을 합니다. 그러나 웹이나 윈도 응용프로그램 등과 달리 라디오 버튼, 탭 레이아웃 등에서도 하위 요소들이 순차 탐색됩니다. 이것은 안드로이드 플랫폼 자체의 특성입니다.
반대로 화살표 키는 스마트폰에 배치된 컨트롤의 레이아웃을 기준으로 초점이 이동됩니다. 즉 10행 2열로 배치된 레이아웃에서 모둔 요소가 초점이 가능하다면 왼쪽, 오른쪽 화살표는 각 행에 배치된 두 요소 사이를 가로로 이동하고 위 또는 아래 화살표는 아래 혹은 위의 요소, 세로로 이동합니다. 따라서 화살표는 편집 요소를 제외하고는 컨트롤 유형의 영향을 받지 않으며 컨트롤 이 배치되어 있는 레이아웃 컨테이너의 영향을 받습니다.
초점 순서 변경하기
Tab, Shift + Tab을 눌렀을 때 초점이 논리적으로 이동되지 않을 때는 nextFocusForward 속성을 통하여 요소의 순서를 바꾸지 않고, 키보드가 초점을 보내는 순서를 임의적으로 조정할 수 있습니다.
예를 들어 각각 A, B, C라는 세 개의 버튼이 있고, 시각적으로는 알파벳 순서대로 나열하였으나, 소스 코드 상에서는 A, C, B 순서로 작성되었다고 가정해 봅시다. 스크린리더는 코드가 작성된 순서대로 탐색하기 때문에 A, C, B 버튼 순서로 초점이 보내질 겁니다. 이것을 A, B, C 순서, 알파벳순으로 이동하도록 하려면 a 버튼에 android:nextFocusForward = “@id/Butto_B” 와 같이 XML을 작성하면 Tab키를 눌렀을 때 A 다음에 B 버튼으로 초점을 보내게 됩니다.
화살표 키를 눌렀을 때 화면에 보이는 레이아웃과 다르게 이동되는 경우에는 nextFocusLeft, right, up, down 속성을 사용하여 방향에 따른 초점 순서를 임의적으로 설정할 수 있습니다.
특정 요소로 초점 이동시키기
한 화면에서 레이어가 표시되거나 레이어가 사라지는 경우 초점을 지정해 주지 않으면 초점을 잃어버리거나 레이어까지 Tab 키나 화살표 키 등을 여러 번 눌러야 하는 번거로움이 있습니다. 초점을 지정해 주는 메소드는 onRequestFocus() 입니다. 초점을 보내주어야 할 시점에 layerButton.requestFocus() 등과 같이 사용할 수 있으며 이렇게 하면 지정된 시점에 시스템 포커스가 해당 요소로 이동됩니다.
참고:
- 해당 메소드는 하드웨어 키보드로 조작을 하는 경우에만 동작합니다. 따라서 스크린 리더 사용자를 위해서는 onRequestFocus 메소드와 동시에 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) 메소드를 적용해 주어야 합니다.
- 웹페이지에서 초점을 받지 않는 요소로 초점을 이동시키는 경우에는 tabindex =”-1” 속성을 사용해야 하는 것처럼 안드로이드에서 초점이 제공되지 않는 곳으로 초점을 이동시키려면 반드시 focusable 속성을 true로 설정해 주어야 합니다. (웹페이지에서는 시스템 초점만을 이동시켜주기 위한 tabindex 속성이 별도로 존재하지만 안드로이드에는 이러한 속성은 없습니다.)
딤드 레이어 콘텐츠에 초점 보내지 않기
웹페이지와 마찬가지로 안드로이드에서도 레이어가 표시되었을 때 기존 화면에 딤드 되는 경우에는 항상 접근성 문제를 야기합니다. 스크린 리더 상에서는 딤드 처리된 콘텐츠에 importantForAccessibility noHideDescendants 메소드를 통해서 딤드된 콘텐츠를 숨길 수 있지만(웹페이지에서의 aria-hidden 속성과 유사함) 키보드 초점을 숨기려면 dimmedView.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS 메소드를 사용합니다. 이렇게 하면 지정한 요소 하위의 모든 포커스 가능한 요소를 키보드 초점에서 제외합니다. 단 레이어가 사라지고 딤드 된 콘텐츠가 메인 콘텐츠가 될 때에는 반드시 dimmedView.descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS 메소드를 통해 초점을 이동할 수 있도록 해 주어야 합니다.참고: ViewGroup.FOCUS_BLOCK_DESCENDANTS 를 사용하면 앞에서 설명한 바와 같이 해당 컨테이너는 제외한 하위의 모든 포커스 가능한 요소만 숨깁니다. 따라서 RecyclerView, ListView 와 같이 자체 초점을 가지고 있는 컨테이너의 경우에는 컨테이너 자체에 focusable false 속성을 지정해 주어야 합니다.
키코드 사용하기
웹페이지에서도 커스텀 컨트롤 구현 시 키코드를 사용하여 키보드 접근성을 구현합니다. 안드로이드에서도 상황에 따라 setOnKeyListener 메소드를 통해 키코드 사용이 가능합니다.
예시:contentText.setOnKeyListener { v, keyCode, event ->
if(keyCode == KeyEvent.KEYCODE_DPAD_DOWN && event.action == KeyEvent.ACTION_DOWN){수행할 메소드}
지금까지 안드로이드 플랫폼에서 하드웨어 키보드에서의 접근성 구현을 위한 여러 가지 방법에 대해 살펴보았습니다.
아래는 몇 가지 추가 참고 사항입니다.
1. ListView, GridView, RecyclerView 하위의 콘텐츠가 화면이 스크롤될 만큼 많을 경우 Tab 키나 화살표 키를 누르면 각 하위 항목에 포커스 되지 못하고 화면만 스크롤되는 현상이 발생되고 있습니다. 이것은 시스템의 버그로 보입니다. 참고로 스크롤이 더 이상 없을 경우 가장 마지막의 몇 개의 콘텐츠는 제대로 포커스 및 실행이 가능합니다.
2. longClick 속성을 가진 콘텐츠에서는 Enter를 길게 눌러 길게 누르기 동작을 수행할 수 있습니다.
3. onCreateOptionsMenu 메소드를 사용하여 옵션 더 보기 메뉴를 구현하는 경우에는 해당 메뉴 버튼에는 초점은 제공되지 않으며 CTRL + ESC 키를 눌러 메뉴를 실행할 수 있습니다.