접근성 기능이 필요한 사용자를 지원할 수 있도록, Android 프레임워크에서는 사용자에게 앱의 콘텐츠를 표시하고 사용자를 대신하여 앱을 조작할 수 있는 접근성 서비스를 만들 수 있습니다.
Android는 다음을 포함하여 여러 시스템 접근성 서비스를 제공합니다.
- TalkBack: 시력이 약하거나 시각 장애가 있는 사용자를 지원합니다. 합성된 음성을 통해 콘텐츠를 알리고 앱에서 사용자 동작에 응답하여 작업을 실행합니다.
- 스위치 제어: 거동 장애가 있는 사용자를 지원합니다. 상호작용 요소를 강조표시하고 사용자가 버튼을 누르면 이에 응답하여 작업을 실행합니다. 1개 또는 2개의 버튼만 사용하여 기기를 제어할 수 있게 합니다.
접근성 기능이 필요한 사용자가 앱을 성공적으로 사용하도록 지원하려면 앱이 이 페이지에 설명된 권장사항을 따라야 합니다. 이 권장사항은 더욱 접근성 높은 앱 만들기에 설명된 가이드라인을 기반으로 합니다.
이어지는 섹션에서 설명하는 각 권장사항은 앱의 접근성을 더욱 향상합니다.
- 요소에 라벨 지정
- 사용자는 앱 내에 있는 상호작용이 가능한 중요한 각 UI 요소의 콘텐츠 및 목적을 이해할 수 있어야 합니다.
- 접근성 작업 추가
- 접근성 작업을 추가하면 접근성 서비스 사용자가 앱에서 중요한 사용자 플로우를 완료하도록 할 수 있습니다.
- 시스템 위젯 확장
- 자체 맞춤 뷰를 만드는 대신 프레임워크에 포함된 뷰 요소를 활용하여 빌드합니다. 프레임워크의 뷰 및 위젯 클래스는 이미 앱에 필요한 대부분의 접근성 기능을 제공합니다.
- 색상 이외의 큐 사용
- 사용자는 UI의 요소 카테고리를 명확하게 구분할 수 있어야 합니다. 카테고리를 구분하려면 패턴 및 위치를 색상과 함께 사용하여 카테고리의 차이를 표현합니다.
- 미디어 콘텐츠의 접근성 높이기
- 앱의 동영상 또는 오디오 콘텐츠를 사용하는 사용자가 시각 또는 청각적인 큐에 전적으로 의존할 필요가 없도록 앱의 동영상 또는 오디오 콘텐츠에 설명을 추가해 보세요.
요소에 라벨 지정
앱에서 상호작용이 가능한 각 UI 요소에 관한 설명이 포함된 유용한 라벨을 사용자에게 제공하는 것이 중요합니다. 각 라벨은 특정 요소의 의미와 목적을 설명해야 합니다. TalkBack과 같은 스크린 리더는 사용자에게 이러한 라벨을 알릴 수 있습니다.
대부분의 경우 이 요소가 포함된 레이아웃 리소스 파일에 UI 요소의 설명을 지정합니다. 더욱 접근성 높은 앱 만들기 가이드에 설명된 것처럼 대개 contentDescription
속성을 사용하여 라벨을 추가하지만 이어지는 섹션에 설명된 몇 가지 라벨 지정 기법에도 유의해야 합니다.
수정 가능한 요소
EditText
객체와 같은 수정 가능한 요소에 라벨을 지정할 때는 이 텍스트 예를 스크린 리더에서 사용할 수 있게 하는 것 외에도 요소 자체에 올바른 입력의 예를 제공하는 텍스트를 표시하는 것이 좋습니다. 이러한 상황에서는 다음 스니펫에 표시된 대로 android:hint
속성을 사용할 수 있습니다.
<!-- The hint text for en-US locale would be "Apartment, suite, or building". --> <EditText android:id="@+id/addressLine2" android:hint="@string/aptSuiteBuilding" ... />
이 상황에서 View
객체의 android:labelFor
속성은 EditText
요소의 ID로 설정되어야 합니다. 자세한 내용은 다음 섹션을 참고하세요.
한 요소가 다른 요소를 설명하는 요소 쌍
일반적으로 특정 EditText
요소에는 상응하는 View
객체가 있어 사용자가 EditText
요소 내에 입력해야 하는 콘텐츠를 설명합니다. View
객체의 android:labelFor
속성을 설정하여 이 관계를 나타낼 수 있습니다.
다음 스니펫에서는 이러한 요소 쌍에 라벨을 지정하는 예를 보여줍니다.
<!-- Label text for en-US locale would be "Username:" --> <TextView android:id="@+id/usernameLabel" ... android:text="@string/username" android:labelFor="@+id/usernameEntry" /> <EditText android:id="@+id/usernameEntry" ... /> <!-- Label text for en-US locale would be "Password:" --> <TextView android:id="@+id/passwordLabel" ... android:text="@string/password android:labelFor="@+id/passwordEntry" /> <EditText android:id="@+id/passwordEntry" android:inputType="textPassword" ... />
컬렉션의 요소
컬렉션의 요소에 라벨을 추가할 때 각 라벨은 고유해야 합니다. 라벨이 고유해야 라벨을 알릴 때 시스템의 접근성 서비스에서 화면에 표시되는 요소를 정확히 하나만 참조할 수 있습니다. 이러한 대응 관계를 통해 사용자가 UI를 순환했을 때 또는 이미 찾은 요소로 포커스를 이동했을 때 이를 알 수 있습니다.
특히 RecyclerView
객체와 같이 재사용된 레이아웃 내 요소에 추가 텍스트 또는 컨텍스트 정보를 포함하여 각 하위 요소가 고유하게 식별되도록 해야 합니다.
이렇게 하려면 다음 코드 스니펫과 같이 어댑터 구현의 일부로 콘텐츠 설명을 설정합니다.
Kotlin
data class MovieRating(val title: String, val starRating: Integer) class MyMovieRatingsAdapter(private val myData: Array<MovieRating>): RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() { class MyRatingViewHolder(val ratingView: ImageView) : RecyclerView.ViewHolder(ratingView) override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) { val ratingData = myData[position] holder.ratingView.contentDescription = "Movie ${position}: " + "${ratingData.title}, ${ratingData.starRating} stars" } }
Java
public class MovieRating { private String title; private int starRating; // ... public String getTitle() { return title; } public int getStarRating() { return starRating; } } public class MyMovieRatingsAdapter extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> { private MovieRating[] myData; public static class MyRatingViewHolder extends RecyclerView.ViewHolder { public ImageView ratingView; public MyRatingViewHolder(ImageView iv) { super(iv); ratingView = iv; } } @Override public void onBindViewHolder(MyRatingViewHolder holder, int position) { MovieRating ratingData = myData[position]; holder.ratingView.setContentDescription("Movie " + position + ": " + ratingData.getTitle() + ", " + ratingData.getStarRating() + " stars") } }
관련 콘텐츠 그룹
앱에서 노래의 세부정보나 메시지의 속성과 같이 자연스러운 그룹을 형성하는 여러 UI 요소를 표시하는 경우 대개 ViewGroup
의 서브클래스인 컨테이너 내에 이러한 요소를 정렬합니다. 컨테이너 객체의 android:screenReaderFocusable
속성을 true
로 설정하고 각 내부 객체의 android:focusable
속성을 false
로 설정합니다. 이렇게 설정하면 접근성 서비스에서 내부 요소의 콘텐츠 설명을 하나의 알림에 차례로 표시할 수 있습니다.
이렇게 관련 요소를 통합하면 사용자가 보조 기술을 사용하여 화면에 표시된 정보를 더 효율적으로 찾을 수 있습니다.
다음 스니펫에는 서로 관련된 콘텐츠 부분이 포함되어 있습니다. 즉, 컨테이너 요소(ConstraintLayout
의 인스턴스)에는 android:screenReaderFocusable
속성이 true
로 설정되어 있으며 내부 TextView
요소에는 각각 android:focusable
속성이 false
로 설정되어 있습니다.
<!-- In response to a single user interaction, accessibility services announce both the title and the artist of the song. --> <ConstraintLayout android:id="@+id/song_data_container" ... android:screenReaderFocusable="true"> <TextView android:id="@+id/song_title" ... android:focusable="false" android:text="@string/my_song_title" /> <TextView android:id="@+id/song_artist" android:focusable="false" android:text="@string/my_songwriter" /> </ConstraintLayout>
접근성 서비스는 내부 요소의 설명을 하나의 표현으로 알려주기 때문에 각 설명을 최대한 짧게 유지하면서 요소의 의미를 전달하는 것이 중요합니다.
참고: 일반적으로 그룹 콘텐츠의 텍스트를 집계하여 그룹의 콘텐츠 설명을 있습니다. 그렇게 하면 그룹의 설명이 불안정해지고 변경하면 그룹의 설명이 더 이상 표시되는 텍스트와 일치하지 않을 수 있습니다.
목록 또는 그리드 컨텍스트에서는 스크린 리더가 목록의 텍스트를 통합하거나 그리드 요소의 하위 텍스트 노드입니다. 이 값은 수정하지 않는 것이 있습니다.
중첩된 그룹
앱 인터페이스에 축제 이벤트의 일별 목록과 같은 다차원 정보가 표시되는 경우 내부 그룹 컨테이너에 android:screenReaderFocusable
속성을 사용합니다. 이러한 라벨 지정 방식은 화면의 콘텐츠를 발견하는 데 필요한 알림의 수와 각 알림의 길이 간에 적절한 균형을 제공합니다.
다음 코드 스니펫에서는 대규모 그룹 내에서 그룹에 라벨을 지정하는 한 가지 방법을 보여줍니다.
<!-- In response to a single user interaction, accessibility services announce the events for a single stage only. --> <ConstraintLayout android:id="@+id/festival_event_table" ... > <ConstraintLayout android:id="@+id/stage_a_event_column" android:screenReaderFocusable="true"> <!-- UI elements that describe the events on Stage A. --> </ConstraintLayout> <ConstraintLayout android:id="@+id/stage_b_event_column" android:screenReaderFocusable="true"> <!-- UI elements that describe the events on Stage B. --> </ConstraintLayout> </ConstraintLayout>
텍스트 내 제목
일부 앱에서는 제목을 사용해 화면에 표시되는 텍스트 그룹을 요약합니다. 특정 View
요소에서 제목을 표현하는 경우 요소의 android:accessibilityHeading
속성을 true
로 설정하여 접근성 서비스의 용도를 나타낼 수 있습니다.
접근성 서비스 사용자는 단락 또는 단어가 아닌 제목 사이를 탐색하도록 선택할 수 있습니다. 이러한 유연성을 통해 텍스트 탐색 환경이 개선됩니다.
접근성 창(pane) 제목
Android 9(API 수준 28) 이상에서는 화면의 창(pane)에 접근성을 고려한 제목을 제공할 수 있습니다. 접근성을 위해 창(pane)은 프래그먼트 콘텐츠와 같이 시각적으로 구분되는 창(window)의 부분입니다. 접근성 서비스에서 창(window)과 유사한 창(pane) 동작을 이해할 수 있도록 앱의 창(pane)에 내용을 잘 나타내는 제목을 제공해야 합니다. 그러면 접근성 서비스에서 창(pane)의 모양이나 콘텐츠가 변경될 때 더 세분화된 정보를 사용자에게 제공할 수 있습니다.
창(pane)의 제목을 지정하려면 다음 스니펫과 같이 android:accessibilityPaneTitle
속성을 사용합니다.
<!-- Accessibility services receive announcements about content changes that are scoped to either the "shopping cart view" section (top) or "browse items" section (bottom) --> <MyShoppingCartView android:id="@+id/shoppingCartContainer" android:accessibilityPaneTitle="@string/shoppingCart" ... /> <MyShoppingBrowseView android:id="@+id/browseItemsContainer" android:accessibilityPaneTitle="@string/browseProducts" ... />
장식 요소
UI의 요소가 시각적 간격용 또는 시각적 표시용으로만 존재하면 android:importantForAccessibility
속성을 "no"
로 설정합니다.
접근성 작업 추가
접근성 서비스 사용자가 앱에서 모든 사용자 플로우를 쉽게 실행할 수 있도록 하는 것이 중요합니다. 예를 들어, 사용자가 목록에서 항목을 스와이프할 수 있다면 이 사용자가 또 다른 방법으로 동일한 사용자 플로우를 완료할 수 있도록 이 작업을 접근성 서비스에 노출할 수 있습니다.
모든 작업의 접근성 향상
TalkBack, 음성 액세스 또는 스위치 제어 사용자는 앱에서 특정 사용자 플로우를 완료할 또 다른 방법이 필요할 수 있습니다. 드래그 앤 드롭, 스와이프와 같은 동작에 연결된 작업의 경우, 접근성 서비스 사용자가 쉽게 사용할 수 있도록 앱에서 이러한 동작을 노출할 수 있습니다.
앱은 접근성 동작을 사용하여 사용자가 특정 작업을 완료할 또 다른 방법을 제공할 수 있습니다.
예를 들어, 앱에서 사용자가 항목을 스와이프할 수 있다면 다음과 같은 사용자설정 접근성 작업을 통해 이 기능을 노출할 수 있습니다.
Kotlin
ViewCompat.addAccessibilityAction( // View to add accessibility action itemView, // Label surfaced to user by an accessibility service getText(R.id.archive) ) { _, _ -> // Same method executed when swiping on itemView archiveItem() true }
Java
ViewCompat.addAccessibilityAction( // View to add accessibility action itemView, // Label surfaced to user by an accessibility service getText(R.id.archive), (view, arguments) -> { // Same method executed when swiping on itemView archiveItem(); return true; } );
With the custom accessibility action implemented, users can access the action through the actions menu.
Make available actions understandable
When a view supports actions such as touch & hold, an accessibility service such as TalkBack announces it as "Double tap and hold to long press."
This generic announcement doesn't give the user any context about what a touch & hold action does.
To make this announcement more descriptive, you can replace the accessibility action’s announcement like so:
Kotlin
ViewCompat.replaceAccessibilityAction( // View that contains touch & hold action itemView, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK, // Announcement read by TalkBack to surface this action getText(R.string.favorite), null )
Java
ViewCompat.replaceAccessibilityAction( // View that contains touch & hold action itemView, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK, // Announcement read by TalkBack to surface this action getText(R.string.favorite), null );
This results in TalkBack announcing "Double tap and hold to favorite," helping users understand the purpose of the action.
Extend system widgets
Note: When you design your app's UI, use or extend
system-provided widgets that are as far down Android's class hierarchy as
possible. System-provided widgets that are far down the hierarchy already
have most of the accessibility capabilities your app needs. It's easier
to extend these system-provided widgets than to create your own from the more
generic View
,
ViewCompat
,
Canvas
, and
CanvasCompat
classes.
If you must extend View
or Canvas
directly, which
might be necessary for a highly customized experience or a game level, see
Make custom views more
accessible.
This section uses the example of implementing a special type of
Switch
called TriSwitch
while following
best practices around extending system widgets. A TriSwitch
object works similarly to a Switch
object, except that each instance of
TriSwitch
allows the user to toggle among three possible states.
Extend from far down the class hierarchy
The Switch
object inherits from several framework UI classes in its hierarchy:
View ↳ TextView ↳ Button ↳ CompoundButton ↳ Switch
새 TriSwitch
클래스는 Switch
클래스에서 직접 확장하는 것이 가장 좋습니다. 이렇게 하면 Android 접근성 프레임워크가 TriSwitch
클래스에 필요한 대부분의 접근성 기능을 제공합니다.
- 접근성 작업: 접근성 서비스가
TriSwitch
객체에서 실행되는 각각의 가능한 사용자 입력을 에뮬레이션하는 방법을 시스템에 알립니다. (View
에서 상속됨) - 접근성 이벤트: 화면을 새로 고치거나 업데이트할 때
TriSwitch
객체의 모양이 변경될 수 있는 모든 가능한 방법에 관해 접근성 서비스에 알립니다. (View
에서 상속됨) - 특성: 표시되는 텍스트의 콘텐츠와 같은 각
TriSwitch
객체에 관한 세부정보입니다. (TextView
에서 상속됨) - 상태 정보: '선택됨' 또는 '선택 해제됨'과 같은
TriSwitch
객체의 현재 상태에 관한 설명입니다. (CompoundButton
에서 상속됨) - 상태에 관한 텍스트 설명: 각 상태가 나타내는 사항에 관한 텍스트 기반 설명입니다. (
Switch
에서 상속됨)
Switch
및 슈퍼클래스의 이러한 동작은 TriSwitch
객체의 동작과 거의 비슷합니다. 따라서 구현 시 가능한 상태 수를 2개에서 3개로 확장하는 데 집중할 수 있습니다.
맞춤 이벤트 정의
시스템 위젯을 확장할 때 사용자가 그 위젯과 상호작용하는 방식의 측면을 변경할 수 있습니다. 접근성 서비스가 마치 사용자가 위젯과 직접 상호작용한 것처럼 앱의 위젯을 업데이트할 수 있도록 이러한 상호작용 변경사항을 정의하는 작업이 중요합니다.
일반적인 가이드라인은 재정의하는 모든 뷰 기반 콜백에 대해 ViewCompat.replaceAccessibilityAction()
을 재정의하여 상응하는 접근성 작업을 다시 정의해야 한다는 것입니다.
앱의 테스트에서 ViewCompat.performAccessibilityAction()
을 호출하여 이러한 재정의된 작업의 동작을 검증할 수 있습니다.
이 원칙이 TriSwitch 객체에서 작동하는 방식
일반적인 Switch
객체와 달리 TriSwitch
객체를 탭하면 3가지의 가능한 상태가 순환됩니다. 따라서 상응하는 ACTION_CLICK
접근성 작업을 다음과 같이 업데이트해야 합니다.
Kotlin
class TriSwitch(context: Context) : Switch(context) { // 0, 1, or 2 var currentState: Int = 0 private set init { updateAccessibilityActions() } private fun updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label) { view, args -> moveToNextState() }) } private fun moveToNextState() { currentState = (currentState + 1) % 3 } }
Java
public class TriSwitch extends Switch { // 0, 1, or 2 private int currentState; public int getCurrentState() { return currentState; } public TriSwitch() { updateAccessibilityActions(); } private void updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label, (view, args) -> moveToNextState()); } private void moveToNextState() { currentState = (currentState + 1) % 3; } }
색상 이외의 큐 사용
색약이 있는 사용자를 지원하려면 색상 이외의 큐를 사용해 앱 화면 내에 있는 UI 요소를 구별하세요. 이러한 기법에는 다양한 모양 또는 크기를 사용하거나 텍스트 또는 시각적 패턴을 제공하거나 요소의 차이점을 표시하는 오디오 기반 또는 터치 기반(햅틱) 반응을 추가하는 것이 포함될 수 있습니다.
그림 1에서는 활동의 두 가지 버전을 보여줍니다. 한 버전에서는 워크플로에서 실행 가능한 두 작업을 구분하는 데 색상만 사용합니다. 다른 버전에서는 두 옵션의 차이를 강조표시하기 위해 색상 외에 도형 및 텍스트를 포함하는 권장사항을 사용합니다.
미디어 콘텐츠의 접근성 높이기
동영상 클립 또는 오디오 녹음과 같은 미디어 콘텐츠가 포함된 앱을 개발한다면 다양한 유형의 접근성이 필요한 사용자가 이 자료를 이해할 수 있도록 지원해 보세요. 특히 다음과 같은 작업을 하는 것이 좋습니다.
- 사용자가 미디어를 일시중지 또는 중지하고 볼륨을 변경하며 자막을 전환할 수 있는 컨트롤을 포함합니다.
- 동영상에서 워크플로를 완료하는 데 필수적인 정보를 표시하는 경우 동일한 콘텐츠를 스크립트와 같은 대체 형식으로 제공합니다.
추가 리소스
앱의 접근성을 높이는 방법을 자세히 알아보려면 다음 추가 리소스를 참조하세요.