📱 접근성이란
모바일 접근성이란 웹 접근성과 같은 개념으로 장애를 가진 사람이든 일반인이든 구애를 받지 않고 동등하게 정보에 접근할 수 있도록 설계하고 디자인/개발하는 것을 의미한다. 즉, 모두에게 유용하게 사용되는 앱이 되기 위해서는 접근성 처리가 필요하다.
안드로이드에서는 “TalkBack”, “시인성 향상”, “청각보조” 등 다양한 방법으로 접근성을 제공하고 있는데
이번 글에서는 그 중에서도 대체 텍스트(talkback)를 통해 음성 피드백을 제공할 수 있는 방법을 알아보겠다. ꈍꈊꈍ
🗣 TalkBack이란?
TalkBack은 Android 기기에 포함된 Google 스크린 리더입니다. TalkBack을 사용하면 기기를 보지 않고도 제어할 수 있습니다.
출처: https://support.google.com/accessibility/android/answer/6283677?hl=ko
즉, 시력이 좋지 않은 사람들을 위해 글자나 화면을 음성으로 설명해주는 기능이다.
TalkBack(스크린 리더)은 기본적인 텍스트는 읽지만 이미지, 토글 등의 유틸을 읽을 수는 없다. 그래서 이런 유틸들에 대하여 대체 텍스트를 지정해줘야 한다.
🧚♀️ 대체 텍스트를 지정해보자!
contentDescription
contentDescription을 사용하면 ImageView, ImageButton, CheckBox 또는 시각적으로 정보를 전달하는 View를 사용할 때 해당 View에 대한 설명을 제공할 수 있다.
예를 들어 현재 내 위치로 이동하는 ImageButton(GPS 아이콘)이 있다면, “현재 내 위치로 이동” 이런 식으로 contentDescription을 설정할 수 있다. 그러면 버튼이기 때문에 talkback은 “현재 내 위치로 이동, 버튼, 활성화 하려면 두 번 탭하세요.”라고 읽어주게 된다.
깨알 Tips
- Button과 ClickListener가 붙은 ImageView에는 시스템에서 자동적으로 “${contentDescription}, 버튼”이라고 읽어준다.
- 그 외 View에서 액션이 이루어지는 상황이라면 명시적으로 “버튼”이나 액션을 설명하는 문구를 추가할 수 있다.
- 안드로이드 API 22부터는 AccessibilityNodeInfoCompat을 통해 “활성화하려면 두 번 탭하세요” 같은 추가 설명을 쉽게 전달할 수도 있다.
텍스트를 읽게 하고싶지 않다면
importantForAccessibility=”no”를 적용하면 talkback이 해당 뷰를 읽지 않는다.
만약 읽고 싶다면 importantForAccessibility=”yes”를 사용하면 된다.
특정 레이아웃들을 한 번에 묶어서 읽고자 한다면
따로 그린 View를 한 번에 읽도록 하고 싶다면 그 View들을 별도의 레이아웃으로 감싸줘야 한다.
예를 들어서 [시민들이 추천한 장소][n] 이렇게 두개의 TextView로 구성된 ui인데 한 번에 읽고 싶다면 [[시민들이 추천한 장소][n]] 이렇게 감싸준 후 바깥의 레이아웃에 importantForAccessibility=”yes”(또는 focusable=”true”)와 contentDescription을 지정해주면 된다.
// <string name="map_place_count_description">시민들이 추천한 장소 %s개</string>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/placeCountLayout"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:focusable="true"
android:contentDescription="@{@string/map_place_count_description(item.placeCount)}">
<TextView
text="시민들이 추천한 장소"
...
<TextView
text="@{item.placeCount}"
...
</androidx.constraintlayout.widget.ConstraintLayout>
포커스를 이동/해제해야 한다면
// 포커스 이동(선택)
binding.____.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
// 포커스 제거
binding.____.performAccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
AccessibilityEvent를 주고 싶다면
이런 상황이 생길 수 있다.
사용자가 글을 쓰다가 글자 수 제한을 넘겼을 경우, 시력이 잘 보이는 사용자에게는 ui로 “100자 이하로 작성해주세요.”라고 안내할 수 있지만 talkback을 사용하는 사람에게는 음성으로 안내를 해줘야 한다.
그럴 때 쓸 수 있는 방법이 sendAccessibilityEvent이다.
아래처럼 사용할 수 있는데 text length를 체크하다가 sendAccessibilityEvent(”100자 이하로 작성해주세요.”) 이렇게 실행하면 talkback음성이 메시지를 읽어준다.
fun sendAccessibilityEvent(message: String) {
val event = AccessibilityEvent.obtain()
event.eventType = AccessibilityEvent.TYPE_ANNOUNCEMENT
event.className = this.javaClass.name
event.packageName = this.packageName
event.text.add(message)
try {
val manager: AccessibilityManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
manager.sendAccessibilityEvent(event)
} catch (e: Exception) {
...
}
}
컨텐츠를 읽어주는 순서를 변경하고 싶다면
talkback은 기본적으로 위→아래, 왼쪽→오른쪽 순서로 전개되지만 기획상의 이유나, 사정으로 순서를 변경하고 싶어질 수도 있다.
이럴 때 사용할 수 있는 방법이 "android:accessibilityTraversalAfter"과 "android:accessibilityTraversalBefore"이다.
- android:accessibilityTraversalAfter = @id/___ : 이 속성이 적용된 레이아웃은 지정한 id 다음에 읽어짐
- android:accessibilityTraversalBefore = @id/___ : 이 속성이 적용된 레이아웃은 지정한 id 전에 읽어짐
만약 1 - 2 - 3 - 4 - 5 순서대로 읽어지는 ui라고 했을 때
binding.second.accessibilityTraversalAfter = binding.third.id
이렇게 설정하면 2가 3 후(after)에 실행된다.
즉 순서가 1 - 3 - 2 - 4 - 5 이렇게 실행된다.
binding.third.accessibilityTraversalBefore = binding.second.id
이렇게 설정하면 3이 2 전에 실행되기 때문에 똑같이 1 - 3 - 2 - 4 - 5 순으로 실행된다.
android:accessibilityTraversalAfter과 android:accessibilityTraversalBefore의 문제적 상황…
accessibilityTraversalAfter와 accessibilityTraversalBefore에 대상을 id로 지정하는데 리사이클러뷰 안에 있는 ViewHolder 같은 경우는 뷰의 아이디가 같을 수도 있어서, 이럴 경우 내 의도대로 동작하지 않는 상황이 생길 수도 있다.
이럴 때는 id가 아닌 View 자체를 대상으로 지정할 수 있다.
setAccessibilityTravelBefore(binding.cardArea, binding.deleteButton)
ViewCompat.setAccessibilityDelegate(binding.cardArea, object: AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
info.setTraversalBefore(binding.deleteButton)
super.onInitializeAccessibilityNodeInfo(host, info)
}
})
⚙️ talkback 작업하면서 유용/편리했던 설정들
1. 접근성 버튼 talkback으로 적용하기
- 설정 > 접근성 > 고급설정 > 접근성버튼
2. 자막 켜기
- 접근성 > talkback > 설정 > 고급설정 > 개발자 설정 > 음성출력표시
☕️ 마무리
.
'Android' 카테고리의 다른 글
DataStore는 flow를 “어떻게” 발행할까? (0) | 2024.07.03 |
---|---|
Firebase 핸드폰 번호 인증 관련 GooglePlay 대응기 (0) | 2023.03.16 |
새로운 버전의 로그캣 등장, 5가지 꿀기능을 알아보자! (0) | 2023.02.01 |
Firebase Crashlytics 적용하기 (1) | 2022.12.08 |
안드로이드 저장소 권한의 변화와 새롭게 등장한 Photo Picker! (2) | 2022.10.27 |