Slide 1

Slide 1 text

Accessibility in Android 신현성(Lucas)

Slide 2

Slide 2 text

목차 ❏ 접근성이란 ❏ 안드로이드 접근성 서비스 ❏ 안드로이드 접근성 가이드 ❏ 자주 발생하는 접근성 문제 해결

Slide 3

Slide 3 text

이미지를 보시면 어떤 생각이 드시나요? reference : GPT-4

Slide 4

Slide 4 text

접근성이란? ❏ 모든 사람을 포용할 수 있는 제품, 기기, 서비스 또는 환경을 만드는 것을 의미해요

Slide 5

Slide 5 text

접근성이란? reference : https://commonlook.com/5-fitness-apps-gadgets-blind-can-use-to-stay-healthy/

Slide 6

Slide 6 text

접근성이란? reference : https://designpositive.medium.com/why-web-accessibility-22ca880b2963

Slide 7

Slide 7 text

접근성의 중요성 ❏ Increase your app’s reach (더 많은 사용자들이 앱을 이용)

Slide 8

Slide 8 text

❏ 장애인차별금지법 ❏ 2021년 7월 27일 개정, 2023년 1월 28일 시행 ❏ <1단계:23.7.28> 공공·교육·의료기관, 이동·교통시설 등 ❏ <2단계:24.1.28> 복지시설, 상시 100인 이상 사업주 ❏ <3단계:24.7.28> 문화·예술․관광사업자, 상시 100인 미만 사업주 접근성의 중요성

Slide 9

Slide 9 text

안드로이드 접근성 서비스

Slide 10

Slide 10 text

다양한 안드로이드 접근성 서비스 ❏ Vision ❏ Lookout(Assisted vision), Reading mode, Talkback, Low vision tools ❏ Audio ❏ Live Transcribe & Notification, Live Caption, Hearing aids(보청기), Sound Amplifier ❏ Mobility ❏ Switch(Camera) Access, Project Activate, Voice Access, Action Blocks

Slide 11

Slide 11 text

Vision - Talkback ❏ 화면 콘텐츠를 음성으로 읽어주는 보조 기술이에요. ❏ 다양한 제스처를 통해 화면 요소간의 이동, 선택, 클릭, 스크롤, 컨텍스트 메뉴 등을 제공해요.

Slide 12

Slide 12 text

Vision - Talkback 영상 링크

Slide 13

Slide 13 text

Switch Access ❏ 신체적 불편함이 있는 사용자가 화면을 터치하지 않고도, 하드웨어를 통해 안드로이드 장치를 제어할 수 있어요

Slide 14

Slide 14 text

Switch Access 영상 링크

Slide 15

Slide 15 text

안드로이드 접근성 가이드 라인

Slide 16

Slide 16 text

안드로이드 접근성 가이드 라인 1. Increase text visibility(텍스트 가시성 향상) 2. Use Large, simple controls(더 큰 터치 영역 사용) 3. Describe each UI element (UI 요소 설명) 4. 기타 개선 사항

Slide 17

Slide 17 text

텍스트 가시성 향상

Slide 18

Slide 18 text

텍스트 가시성 향상 ❏ 색상 대비 높이기 ❏ 텍스트 색상과 배경 색상 간의 밝기 차이를 수치로 나타낸 것이에요 ❏ ColorUtils.calculateContrast(fgColor, bgColor)

Slide 19

Slide 19 text

텍스트 가시성 향상 Do Caution 3:1 대비 이상 권장

Slide 20

Slide 20 text

텍스트 가시성 향상 Do Caution 4.5:1 대비 이상 권장

Slide 21

Slide 21 text

텍스트 가시성 향상 Text type Color contrast ratio Large text (anything 18sp or larger, or 14sp and larger when bold) At least 3:1 color contrast ratio All other text At least 4.5 color contrast ratio

Slide 22

Slide 22 text

텍스트 가시성 향상 ❏ 고대비 테마

Slide 23

Slide 23 text

텍스트 가시성 향상 ❏ 고대비 테마

Slide 24

Slide 24 text

더 큰 터치 영역

Slide 25

Slide 25 text

더 큰 터치 영역 ❏ minWidth + paddingRight + paddingLeft >= 48dp ❏ minHeight + paddingTop + paddingBottom >= 48dp

Slide 26

Slide 26 text

더 큰 터치 영역 reference : material icon guide

Slide 27

Slide 27 text

더 큰 터치 영역

Slide 28

Slide 28 text

더 큰 터치 영역

Slide 29

Slide 29 text

더 큰 터치 영역 ❏ 터치 사이즈를 강제하는 컴포넌트

Slide 30

Slide 30 text

더 큰 터치 영역 ❏ 48dp(약 7 ~ 10 mm)보다 작으면 IDE 에서 Warning 이 발생해요

Slide 31

Slide 31 text

더 큰 터치 영역 ❏ Consider making this touch target 32dp wide and 32dp high or larger

Slide 32

Slide 32 text

더 큰 터치 영역 ❏ 48dp x 48 dp 가 아닌 32dp x 32dp 를 권장하는 이유는 ? // TouchTargetSizeCheck.kt /** * Minimum height and width are set according to * * With the modification that targets against the edge of the screen may be narrower. */ private static final int TOUCH_TARGET_MIN_HEIGHT = 48; private static final int TOUCH_TARGET_MIN_WIDTH = 48; private static final int TOUCH_TARGET_MIN_HEIGHT_ON_EDGE = 32; private static final int TOUCH_TARGET_MIN_WIDTH_ON_EDGE = 32; private static final int TOUCH_TARGET_MIN_HEIGHT_IME_CONTAINER = 32; private static final int TOUCH_TARGET_MIN_WIDTH_IME_CONTAINER = 32;

Slide 33

Slide 33 text

더 큰 터치 영역 ❏ 48dp x 48 dp 가 아닌 32dp x 32dp 를 권장하는 이유는 ? // TouchTargetSizeCheck.kt /** * Minimum height and width are set according to * * With the modification that targets against the edge of the screen may be narrower. */ private static final int TOUCH_TARGET_MIN_HEIGHT = 48; private static final int TOUCH_TARGET_MIN_WIDTH = 48; private static final int TOUCH_TARGET_MIN_HEIGHT_ON_EDGE = 32; private static final int TOUCH_TARGET_MIN_WIDTH_ON_EDGE = 32; private static final int TOUCH_TARGET_MIN_HEIGHT_IME_CONTAINER = 32; private static final int TOUCH_TARGET_MIN_WIDTH_IME_CONTAINER = 32;

Slide 34

Slide 34 text

더 큰 터치 영역 ❏ 48dp x 48 dp 가 아닌 32dp x 32dp 를 권장하는 이유는 ? // TouchTargetSizeCheck.kt /** * Minimum height and width are set according to * * With the modification that targets against the edge of the screen may be narrower. */ private static final int TOUCH_TARGET_MIN_HEIGHT = 48; private static final int TOUCH_TARGET_MIN_WIDTH = 48; private static final int TOUCH_TARGET_MIN_HEIGHT_ON_EDGE = 32; private static final int TOUCH_TARGET_MIN_WIDTH_ON_EDGE = 32; private static final int TOUCH_TARGET_MIN_HEIGHT_IME_CONTAINER = 32; private static final int TOUCH_TARGET_MIN_WIDTH_IME_CONTAINER = 32;

Slide 35

Slide 35 text

더 큰 터치 영역 ❏ layout_margin 을 주게 되면 ?

Slide 36

Slide 36 text

❏ layout_margin 을 주게 되면 ? 더 큰 터치 영역

Slide 37

Slide 37 text

더 큰 터치 영역 ❏ 뷰의 사이즈를 변경해서 터치 영역을 개선하려는데, ❏ 이미 배포 되어 레이아웃을 수정하기 어렵거나 혹은 ❏ 복잡한 계산으로 커스텀 뷰의 사이즈 직접 조절이 어려운 경우

Slide 38

Slide 38 text

더 큰 터치 영역 ❏ ViewSystem(XML) 경우 TouchDelegate 이용해서 뷰의 사이즈 변경 없이 터치 영역 개선이 가능해요 ❏ 터치 영역을 너무 크게 설정하면, 다른 터치 가능한 UI 요소와 겹칠 수 있기 때문에 주의 필요

Slide 39

Slide 39 text

더 큰 터치 영역 // 예시 코드 private fun View.expandTouchArea(size: Int) { // 1. 부모 뷰를 가져와요 val parent = this.parent as? View parent?.touchDelegate = TouchDelegate( Rect().apply { // 2. 현재 뷰의 터치 영역을 가져와요 getHitRect(this) // 3. inset 함수를 사용하여 터치 영역을 확장해요 inset(size, size) }, this ) }

Slide 40

Slide 40 text

더 큰 터치 영역 // 예시 코드 private fun View.expandTouchArea(size: Int) { // 1. 부모 뷰를 가져와요 val parent = this.parent as? View parent?.touchDelegate = TouchDelegate( Rect().apply { // 2. 현재 뷰의 터치 영역을 가져와요 getHitRect(this) // 3. inset 함수를 사용하여 터치 영역을 확장해요 inset(size, size) }, this ) }

Slide 41

Slide 41 text

더 큰 터치 영역 // 예시 코드 private fun View.expandTouchArea(size: Int) { // 1. 부모 뷰를 가져와요 val parent = this.parent as? View parent?.touchDelegate = TouchDelegate( Rect().apply { // 2. 현재 뷰의 터치 영역을 가져와요 getHitRect(this) // 3. inset 함수를 사용하여 터치 영역을 확장해요 inset(size, size) }, this ) }

Slide 42

Slide 42 text

더 큰 터치 영역 ❏ Compose 의 경우 TouchDelegate 제공 X, 커스텀 처리가 필요해요 ❏ Compose 에서는 터치 사이즈를 강제하는 Material Component 가 존재해서 주의가 필요해요 ❏ Slider, Checkbox, Switch etc..

Slide 43

Slide 43 text

더 큰 터치 영역 // Slider.kt Layout( // 생략... , modifier = modifier // 사이즈를 지정한 modifier .minimumInteractiveComponentSize() .requiredSizeIn( minWidth = SliderTokens.HandleWidth, minHeight = SliderTokens.HandleHeight ) .sliderSemantics(state, enabled) .focusable(enabled, interactionSource) .then(press) .then(drag) )

Slide 44

Slide 44 text

더 큰 터치 영역 // Slider.kt Layout( // 생략... , modifier = modifier // 사이즈를 지정한 modifier .minimumInteractiveComponentSize() // 터치 가능한 컴포넌트가 가져야 하는 최소 크기 .requiredSizeIn( minWidth = SliderTokens.HandleWidth, minHeight = SliderTokens.HandleHeight ) .sliderSemantics(state, enabled) .focusable(enabled, interactionSource) .then(press) .then(drag) )

Slide 45

Slide 45 text

더 큰 터치 영역 // Slider.kt Layout( // 생략... , modifier = modifier // 사이즈를 지정한 modifier .minimumInteractiveComponentSize() // 터치 가능한 컴포넌트가 가져야 하는 최소 크기 .requiredSizeIn( // 슬라이더 핸들 최소 사이즈 보장 minWidth = SliderTokens.HandleWidth, minHeight = SliderTokens.HandleHeight ) .sliderSemantics(state, enabled) .focusable(enabled, interactionSource) .then(press) .then(drag) )

Slide 46

Slide 46 text

더 큰 터치 영역 ❏ minimumInteractiveComponentSize() ? ❏ 상호작용 가능한 컴포넌트가 가져야 하는 최소 크기 강제화

Slide 47

Slide 47 text

// InterativeComponentSize.kt override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val size = minimumInteractiveComponentSize val placeable = measurable.measure(constraints) val enforcement = isAttached && currentValueOf(LocalMinimumInteractiveComponentEnforcement) // 코드 생략… } 더 큰 터치 영역

Slide 48

Slide 48 text

// InterativeComponentSize.kt override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val size = minimumInteractiveComponentSize val placeable = measurable.measure(constraints) val enforcement = isAttached && currentValueOf(LocalMinimumInteractiveComponentEnforcement) // 코드 생략… } private val minimumInteractiveComponentSize: DpSize = DpSize(48.dp, 48.dp) 더 큰 터치 영역

Slide 49

Slide 49 text

더 큰 터치 영역 // InterativeComponentSize.kt override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val size = minimumInteractiveComponentSize val placeable = measurable.measure(constraints) val enforcement = isAttached && currentValueOf(LocalMinimumInteractiveComponentEnforcement) // 코드 생략… }

Slide 50

Slide 50 text

더 큰 터치 영역 ❏ LocalMinimumInteractiveComponentEnforcement 를 이용해서 최소 크기를 완화할 수 있어요 // 예시 코드 CompositionLocalProvider( LocalMinimumInteractiveComponentEnforcement provides false ) { // UI 처리 }

Slide 51

Slide 51 text

UI 요소 설명

Slide 52

Slide 52 text

❏ 그래픽 요소들에는 콘텐츠 라벨 제공 ❏ 콘텐츠 라벨 : 화면에 보이는 UI 를 설명하는 텍스트 ❏ contentDescription, hint, lableFor etc.. 포함해요 UI 요소 설명

Slide 53

Slide 53 text

UI 요소 설명 영상 링크

Slide 54

Slide 54 text

UI 요소 설명 ❏ android:labelFor ❏ 텍스트 레이블을 특정 UI 요소에 연결해서 콘텐츠를 설명하는데 도움을 줍니다

Slide 55

Slide 55 text

UI 요소 설명

Slide 56

Slide 56 text

UI 요소 설명

Slide 57

Slide 57 text

❏ 시스템 UI 컴포넌트에는 Role(Button, TextView, Image etc) 이 설정 되어 있기 때문에, 콘텐츠 설명에는 Role 을 포함할 필요가 없어요 ❏ 상태 변경를 지원하기 때문에, 접근성을 더욱 높여줘요 UI 요소 설명

Slide 58

Slide 58 text

UI 요소 설명 영상 링크

Slide 59

Slide 59 text

기타 개선사항 ❏ 색상 이외의 단서 활용하기 ❏ 빨간색을 부정적, 초록색을 긍정의 의미로 사용하면 시각적인 불편함(적록색맹)이 있는 분들은 이해하기 어려워요

Slide 60

Slide 60 text

기타 개선사항

Slide 61

Slide 61 text

접근성 검사 도구

Slide 62

Slide 62 text

접근성 검사 도구 ❏ Accessibility Scanner (접근성 검사기) ❏ Espresso 테스트

Slide 63

Slide 63 text

Accessibility Scanner ❏ 스냅샷, 녹화 검사를 제공해요 ❏ 검사 가능 목록 ❏ 콘텐츠 설명이 없는 상호작용 가능한 그래픽 요소 ❏ 작은 터치, 텍스트 사이즈 ❏ 텍스트, 이미지 낮은 대비 ❏ SP 대신 DP 사용 ❏ etc...

Slide 64

Slide 64 text

Accessibility Scanner

Slide 65

Slide 65 text

Espresso 테스트

Slide 66

Slide 66 text

Espresso 테스트

Slide 67

Slide 67 text

Espresso 테스트

Slide 68

Slide 68 text

Espresso 테스트 @RunWith(AndroidJUnit4::class) class CounterInstrumentedTest { @Rule @JvmField var activityTestRule = ActivityScenarioRule(MainActivity::class.java) @Test fun testIncrement() { Espresso.onView(withId(R.id.add_button)).perform(ViewActions.click()) Espresso.onView(withId(R.id.countTV)).check(matches(withText("1"))) } companion object { @BeforeClass @JvmStatic fun enableAccessibilityChecks() { AccessibilityChecks.enable().setRunChecksFromRootView(true) } } }

Slide 69

Slide 69 text

Espresso 테스트 @RunWith(AndroidJUnit4::class) class CounterInstrumentedTest { @Rule @JvmField var activityTestRule = ActivityScenarioRule(MainActivity::class.java) @Test fun testIncrement() { Espresso.onView(withId(R.id.add_button)).perform(ViewActions.click()) Espresso.onView(withId(R.id.countTV)).check(matches(withText("1"))) } companion object { @BeforeClass @JvmStatic fun enableAccessibilityChecks() { AccessibilityChecks.enable().setRunChecksFromRootView(true) } } }

Slide 70

Slide 70 text

Espresso 테스트 ❏ 테스트 결과에서 접근성 관련 문제 확인 ❏ 자동화 테스트 용이

Slide 71

Slide 71 text

❏ Suppress 도 가능 Espresso 테스트 fun enableAccessibilityChecks() { AccessibilityChecks.enable() .setRunChecksFromRootView(true) .setSuppressingResultMatcher( matchesCheckNames(`is`("TouchTargetSizeCheck")) ) }

Slide 72

Slide 72 text

Espresso 테스트 ❏ Suppress 가능한 리스트 ❏ ATF(Accessibility Test Framework)’s AccessibilityCheckResultUtils.java private static final ImmutableBiMap> VIEW_CHECK_ALIASES = ImmutableBiMap.>builder() .put("ClickableSpanViewCheck", ClickableSpanCheck.class) .put("DuplicateClickableBoundsViewCheck", DuplicateClickableBoundsCheck.class) .put("DuplicateSpeakableTextViewHierarchyCheck", DuplicateSpeakableTextCheck.class) .put("EditableContentDescViewCheck", EditableContentDescCheck.class) .put("RedundantContentDescViewCheck", RedundantDescriptionCheck.class) .put("SpeakableTextPresentViewCheck", SpeakableTextPresentCheck.class) .put("TextContrastViewCheck", TextContrastCheck.class) .put("TouchTargetSizeViewCheck", TouchTargetSizeCheck.class) .buildOrThrow();

Slide 73

Slide 73 text

자주 발생할 수 있는 접근성 문제 해결하기

Slide 74

Slide 74 text

Grouping ❏ 연관된 콘텐츠를 그룹화하여 효율적으로 정보를 전달할 수 있어요 ❏ 중요한 UI 요소까지 포커스를 여러 번 불필요하게 이동하는 경우 ❏ 동일한 콘텐츠가 여러 번 포커스 되어 내용이 반복되는 문제 해결

Slide 75

Slide 75 text

Grouping Before : 2번 이동 After : 1번 이동

Slide 76

Slide 76 text

Grouping Before : 2번 이동 After : 1번 이동

Slide 77

Slide 77 text

Grouping(ViewSystem)

Slide 78

Slide 78 text

Grouping(Compose) Box( modifier = Modifier .fillMaxWidth() .wrapContentHeight() .semantics { contentDescription = stringResource(id = R.string.settings_do_not_disturb) } ) { Text( text = stringResource(id = R.string.settings_do_not_disturb), modifier = Modifier .align(Alignment.CenterStart) .clearAndSetSemantics { } // 설정되어 있는 semantics 정보 제거 ) Switch( checked = false, onCheckedChange = {}, modifier = Modifier.align(Alignment.CenterEnd) ) }

Slide 79

Slide 79 text

LiveRegion ❏ 스크린 리더 포커스 이동 없이 실시간으로 변화하는 UI 요소를 설명하기 적합해요 ❏ 알림, 에러 상태, 채팅, 게임 스코어, 주식 가격 등 ❏ 네비게이션 - 전방에 과속 카메라 알림

Slide 80

Slide 80 text

LiveRegion ❏ Polite ❏ TalkBack 이 다른 내용을 읽고 있을 때는 중단하지 않고, 현재 읽기를 완료한 후에 알림을 읽어요 ❏ Assertive ❏ TalkBack 이 즉시 현재 읽기를 중단하고, 알림을 읽어요

Slide 81

Slide 81 text

LiveRegion(ViewSystem)

Slide 82

Slide 82 text

LiveRegion(Compose) @Composable fun ErrorContainer(errorText: String) { Row( modifier = Modifier .fillMaxWidth() .semantics { liveRegion = LiveRegionMode.Polite } // or LiveRegionMode.Assertive ) { Image( imageVector = Icons.Default.Info, contentDescription = null, // 중요하지 않은 그래픽 요소 ) Text( text = errorText, color = Color.Red, ) } }

Slide 83

Slide 83 text

해결 영상 영상 링크

Slide 84

Slide 84 text

CustomView ❏ Canvas 에 직접 그리는 경우, 접근성 처리는 개발자 몫이에요. ❏ 오픈소스 사용시 주의해야해요.

Slide 85

Slide 85 text

CustomView - 문제 상황 영상 링크

Slide 86

Slide 86 text

CustomView ❏ 스크린 리더에 포커스 되도록 처리하기 init { // 초기화 로직들.. setAccessibility() } private fun setAccessibility() { isFocusable = true isClickable = true importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES }

Slide 87

Slide 87 text

CustomView ❏ Accessibility Action 정의하기 override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(info) info?.addAction(ACTION_SET_PROGRESS) // Progress 와 연관 있는 Action 을 설정해요 val rangeInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { AccessibilityNodeInfo.RangeInfo( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT, min, max, value ) } else { AccessibilityNodeInfo.RangeInfo.obtain( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT, min, max, value ) } info?.rangeInfo = rangeInfo }

Slide 88

Slide 88 text

CustomView ❏ Accessibility Action 정의하기 override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(info) info?.addAction(ACTION_SET_PROGRESS) // Progress 와 연관 있는 Action 을 설정해요 val rangeInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { AccessibilityNodeInfo.RangeInfo( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT, min, max, value ) } else { AccessibilityNodeInfo.RangeInfo.obtain( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT, min, max, value ) } // Progress 의 min, max 그리고 현재 값을 설정해요 info?.rangeInfo = rangeInfo }

Slide 89

Slide 89 text

CustomView ❏ Accessibility Action 추가하기 override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(info) // 이전 코드 생략 info?.rangeInfo = rangeInfo if (isEnabled) { info?.addAction( AccessibilityNodeInfo.AccessibilityAction( AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, "Increase" ) ) info?.addAction( AccessibilityNodeInfo.AccessibilityAction( AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, "Decrease" ) ) } // 접근성 노드에 스와이프 액션 추가 }

Slide 90

Slide 90 text

CustomView ❏ Accessibility Action 처리하기 override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean { if (!isEnabled) return false when (action) { AccessibilityNodeInfo.ACTION_SCROLL_FORWARD -> { // 추가한 액션인 경우 incrementSliderValue() announceForAccessibility(newValue.toString()) return true } AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD -> { // 추가한 액션인 경우 decrementSliderValue() announceForAccessibility(newValue.toString()) return true } } return super.performAccessibilityAction(action, arguments) }

Slide 91

Slide 91 text

CustomView ❏ Accessibility Action 처리하기 override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean { if (!isEnabled) return false when (action) { AccessibilityNodeInfo.ACTION_SCROLL_FORWARD -> { incrementSliderValue() // 슬라이더 값 증가 announceForAccessibility(newValue.toString()) return true } AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD -> { decrementSliderValue() // 슬라이더 값 감소 announceForAccessibility(newValue.toString()) return true } } return super.performAccessibilityAction(action, arguments) }

Slide 92

Slide 92 text

CustomView ❏ Accessibility Action 처리하기 override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean { if (!isEnabled) return false when (action) { AccessibilityNodeInfo.ACTION_SCROLL_FORWARD -> { incrementSliderValue() announceForAccessibility(newValue.toString()) // 스크린 리더를 통해 사용자에게 알려줘요. return true } AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD -> { decrementSliderValue() announceForAccessibility(newValue.toString()) // 스크린 리더를 통해 사용자에게 알려줘요. return true } } return super.performAccessibilityAction(action, arguments) }

Slide 93

Slide 93 text

해결 영상 영상 링크

Slide 94

Slide 94 text

오늘 우리는, ❏ 접근성의 중요성을 이해하고, 안드로이드 접근성 서비스에 대해 알아봤어요 ❏ 접근성 가이드라인에는 텍스트 가시성 향상, 더 큰 터치 영역 사용, UI 요소 설명이 있어요 ❏ 접근성 검사 도구로는 Accessibility Scanner, Espresso 테스트가 있어요 ❏ 발생할 수 있는 문제는 커스텀 뷰의 접근성 처리, Grouping, LiveRegion 관련 문제가 있어요

Slide 95

Slide 95 text

마지막으로, 접근성 디자인은, 장애가 있는 사람뿐만 아니라 장애가 없는 사람에게도 유익합니다 그리고 접근성은 장벽을 제거하고 모든 사람이 기술의 혜택을 누릴 수 있도록 하는 것입니다 Steve Ballmer

Slide 96

Slide 96 text

감사합니다