안녕하세요. Simple& Happy Dev입니다.
앞서 접근성과 접근성 개선을 위한 검토방법에 대해서 알아보았습니다.
접근성에 대한 개요는 아랫 글을 참조하시길 바랍니다.
2018/11/27 - 시각장애인 또는 약시 사용자를 위한 접근성(Accessibility)에 대하여
아래 좌측과 같이 임의로 만든 "접근성 테스트" 앱에서 접근성 검사기를 사용해서 검토하고 개선해보도록 하겠습니다.
먼저, "접근성 검사기" 서비스를 활성화해서 아래 우측에 있는 것처럼 "접근성 스캔 시작 버튼"을 나오도록 만듭니다.
기억이 안 나시는 분들은 아랫 글을 참고하시면 됩니다.
2018/12/17 - 접근성 지원을 위한 개발 시 검토 항목 / 사전 출시 보고서(접근성) / 접근성 검사기
![](https://t1.daumcdn.net/cfile/tistory/9921FA335C4F2C871E)
접근성 테스트 소스 (개선 전)
AccessibilityTest_before.zip
"접근성 테스트 앱"에 대해서 접근성 검사기로 스캔을 시작해보니 아래와 같이 결과가 나왔습니다.
![](https://t1.daumcdn.net/cfile/tistory/99BD9D3A5C4F2CE610)
![](https://t1.daumcdn.net/cfile/tistory/998077355C4F2D231C)
![](https://t1.daumcdn.net/cfile/tistory/99A8EA3B5C4F2D510B)
검토된 결과 총 21개의 제안사항이 있으며, "텍스트 대비", "터치할 대상", "항목 설명", "이미지 대비", "항목 라벨" 까지 총 5종류로 분류됩니다.
"텍스트 대비" 항목 검토 및 개선
"빨강"이라는 글자와 나무 질감의 배경 간의 대비율(Contrast ratio)이 4.5 이상되어야 하는데 3.72라서 검출되었습니다.
참고로 글자의 크기가 큰 경우에 기준 대비율이 3.0이 될 수 있습니다.
약시 사용자에게는 글자 인식에 문제가 될 수 있기 때문입니다.
대비율은 https://contrast-ratio.com/ 사이트에서 확인할 수 있습니다.
여기에서 대비율이 4.5 이상 나오도록 Text color 값 변경해봅니다. #979797 이상만 넣어주면 4.5가 넘어갑니다.
![](https://t1.daumcdn.net/cfile/tistory/99DFA8355C4F2D8F11)
color_item.xml에 있는 id가 tvName인 TextView의 textColor를 #979797 이상이 되는 값으로 넣어줍니다.
여기서는 white(#ffffffff)로 변경했습니다.
수정 전 <TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="16dp"
android:textColor="#ff888888"
app:layout_constraintStart_toEndOf="@+id/ivColor"
app:layout_constraintTop_toTopOf="@+id/ivColor" />
수정 후
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="16dp"
android:textColor="#ffffffff"
app:layout_constraintStart_toEndOf="@+id/ivColor"
app:layout_constraintTop_toTopOf="@+id/ivColor" />
"텍스트 대비" 검토 항목을 개선 후 접근성 검사기로 다시 스캔해보면 제안사항이 15개로 줄어든 것을 확인할 수 있습니다.
수정한 것은 하나인데, 갑자기 제안사항 개수가 많이 줄어든 것은 리스트 뷰에서 공용으로 사용하는 아이템 레이아웃을 수정했기 때문입니다.
"터치할 대상" 항목 검토 및 개선
버튼의 경우 너비나 높이가 48dp 이상이 되어야 합니다.
이 크기는 아이콘과 패딩 크기를 포함한 것입니다. 현재 우측 레이아웃 디자인의 패딩 크기는 0입니다.
이 스위치 버튼의 아이콘 크기가 47dpx27dp이고, 패딩 포함한 전체 크기도 47dpx27dp라서 검출이 되었습니다.
![](https://t1.daumcdn.net/cfile/tistory/99F7E93D5C4F2E0214)
두 가지 방법으로 개선할 수 있습니다.
아이콘을 48dp 이상인 아이콘 이미지로 교체하는 방법과 기존의 아이콘을 사용하면서 패딩크기를 조절하는 방법이 있습니다. 여기서는 편의상 후자로 수정을 해보도록 하겠습니다.
아래처럼 start/end 패딩 크기를 각각 1dp 이상, top/bottom 패딩 크기를 각각 11dp 이상 추가해주어도 됩니다.
android:paddingStart="1dp"
android:paddingTop="11dp"
android:paddingEnd="1dp"
android:paddingBottom="11dp"
그러나, 여기서는 아래처럼 minWidth와 minHeight를 48dp로 변경해주도록 하겠습니다.
결과적으로 비슷하게 보입니다만, 혹시 아이콘이 변경되더라도 스위치 버튼의 크기가 48dp 이상이 되도록 만들어줍니다.
수정 전
<Switch
android:id="@+id/swUse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:focusable="false"
android:focusableInTouchMode="false"
app:layout_constraintBottom_toBottomOf="@+id/ivColor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/ivColor" />
수정 후
<Switch
android:id="@+id/swUse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="@+id/ivColor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/ivColor" />
앞선 레이아웃 디자인과 비교해보면 패딩 크기가 늘어난 것을 확인하실 수 있습니다.
![](https://t1.daumcdn.net/cfile/tistory/99E21B355C4F2E5B1A)
"터치할 대상" 검토 항목을 개선 후 접근성 검사기로 다시 스캔해보면 제안사항이 8개로 줄어든 것을 확인할 수 있습니다.
![](https://t1.daumcdn.net/cfile/tistory/99F2843A5C4F2E7917)
"항목 설명" 항목 검토 및 개선
스크린 리더는 위젯에 설정된 "콘텐츠 라벨"을 사용자에게 읽어줍니다.
그러면, 스크린 리더가 텍스트 뷰, 일반 버튼, 스위치 버튼의 "콘텐츠 라벨"을 어떻게 읽는지부터 설명하겠습니다.
텍스트 뷰의 경우 별도의 콘텐츠 라벨이 필요 없습니다. 텍스트 그 자체를 읽기 때문입니다. 위에서 텍스트 뷰의 "색상 이름"은 콘텐츠 라벨 없이도 "빨강", "주황", "노랑" 등으로 읽혀집니다.
일반 버튼의 경우 스크린 리더는 설정된 "콘텐츠 라벨"+"버튼"이라고 읽습니다. 버튼의 콘텐츠 라벨에 "시작"으로 되어 있으면 스크린 리더는 "시작 버튼"이라고 읽게 됩니다. 만약, 콘텐츠 라벨을 "시작 버튼"으로 해두면 "시작 버튼 버튼"으로 읽기 때문에 문제로 검출됩니다.
스위치 버튼의 경우 스크린 리더는 스위치 상태(On/Off)에 따라서 "사용 스위치" 또는 "해제 스위치"라고 읽습니다.
여기 문제가 된 항목은 리스트 뷰의 경우 아이템이 여러 개 존재하지만, 아이템 레이아웃이 하나만 존재하기 때문에 이 레이아웃에 포함된 스위치 버튼이 다른 아이템들에서도 "콘텐츠 라벨"이 동일하다고 검출하는 것입니다.
즉, 동일한 레이아웃을 계속 사용하기 때문에 리스트 뷰의 각 아이템을 스크린 리더가 읽을 때 스위치 상태가 On인 경우 모두 "사용 스위치"라고 읽고, 스위치가 Off인 경우 모두 "해제 스위치"라고 읽게 됩니다. 이렇게 되면 어떤 아이템의 스위치인지 알 수 없게 됩니다. 아래 "개선 전 동영상"을 보시면 알 수 있습니다.
![](https://t1.daumcdn.net/cfile/tistory/99820B355C4F2EB31D)
이 경우에 수정을 하려면 레이아웃이 아닌 Java 코드 쪽 수정을 해야합니다.
View를 반환하는 getView에서 스위치 버튼의 "콘텐츠 라벨"을 지정할 수 있도록 setContentDescription 메서드에 아이템의 "색상 이름"을 넣어줍니다. 이렇게 되면 스크린 리더는 아이템에서 스위치 버튼을 "콘텐츠 라벨"+"사용 스위치"라고 읽게 되며, 여기서 "콘텐츠 라벨"이 아이템마다 해당하는 "색상 이름"이 들어가서 "빨강 사용 스위치", "주황 사용 스위치"라고 읽게 됩니다.즉, 각 아이템의 스위치 버튼마다 "콘텐츠 라벨"이 달라져서 구분이 됩니다.
다만, 아이템의 텍스트 뷰(id: tvName)의 텍스트도 "색상 이름"이기에 뒤에 "-"를 추가해주어서 "콘텐츠 라벨"이 텍스트 뷰와 같지 않도록 만들었습니다. "-"를 추가해도 스크린 리더가 읽지는 않습니다.
수정 전
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ColorItemView colorView;
if (convertView == null) {
colorView = new ColorItemView(mContext);
} else {
colorView = (ColorItemView) convertView;
}
colorView.setColorValue(mItems.get(position).getColorValue());
colorView.setColorName(mItems.get(position).getColorName());
colorView.setColorUse(mItems.get(position).getColorUse());
Switch swUse = colorView.findViewById(R.id.swUse);
swUse.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mItems.get(position).setColorUse(isChecked);
}
});
return colorView;
}
수정 후
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ColorItemView colorView;
if (convertView == null) {
colorView = new ColorItemView(mContext);
} else {
colorView = (ColorItemView) convertView;
}
colorView.setColorValue(mItems.get(position).getColorValue());
colorView.setColorName(mItems.get(position).getColorName());
colorView.setColorUse(mItems.get(position).getColorUse());
Switch swUse = colorView.findViewById(R.id.swUse);
swUse.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mItems.get(position).setColorUse(isChecked);
}
});
swUse.setContentDescription(mItems.get(position).getColorName()+"-");
return colorView;
}
"항목 설명" 검토 항목을 개선 후 접근성 검사기로 다시 스캔해보면 제안사항이 6개로 줄어든 것을 확인할 수 있습니다.
![](https://t1.daumcdn.net/cfile/tistory/99AEA9365C4F2ECE10)
개선 전/후 동영상을 확인해보세요. 스크린 리더가 동작하기에 소리를 키우시고 보세요.
[개선 전]
[개선 후]
"항목 라벨" 항목 검토 및 개선
앞서 버튼에 대해서 스크린 리더는 설정된 "콘텐츠 라벨"+"버튼"이라고 읽습니다. 여기서 "스크린이 읽을 수 있는 라벨이 없다"는 말은 해당 버튼에 "콘텐츠 라벨"이 지정되지 않았다는 의미입니다. 그리고, 스크린 리더는 "버튼 레이블 없음"이라고 읽게 됩니다. 이 경우에 시각장애인은 이 버튼이 어떤 용도인지 알 수 없게 됩니다.
![](https://t1.daumcdn.net/cfile/tistory/999D52375C4F2EF611)
두 가지 방법으로 수정할 수 있습니다.
- 자바에서 수정방법: setContentDescription 메서드에 "콘텐츠 라벨" 입력
- xml에서 수정방법: android:contentDescription에 "콘텐츠 라벨" 입력
여기서는 후자의 방법으로 수정하도록 하겠습니다.
수정 전
<ImageButton
android:id="@+id/ibPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:src="@android:drawable/ic_media_previous"
app:layout_constraintEnd_toStartOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
<ImageButton
android:id="@+id/ibPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:src="@android:drawable/ic_media_play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lvColor" />
<ImageButton
android:id="@+id/ibNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:src="@android:drawable/ic_media_next"
app:layout_constraintStart_toEndOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
수정 후
<ImageButton
android:id="@+id/ibPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:src="@android:drawable/ic_media_previous"
android:contentDescription="@string/previous_song"
app:layout_constraintEnd_toStartOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
<ImageButton
android:id="@+id/ibPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:src="@android:drawable/ic_media_play"
android:contentDescription="@string/play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lvColor" />
<ImageButton
android:id="@+id/ibNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:src="@android:drawable/ic_media_next"
android:contentDescription="@string/next_song"
app:layout_constraintStart_toEndOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
"항목 라벨" 검토 항목을 개선 후 접근성 검사기로 다시 스캔해보면 제안사항이 3개로 줄어든 것을 확인할 수 있습니다.
![](https://t1.daumcdn.net/cfile/tistory/996BEA3F5C4F2F1901)
"이미지 대비" 항목 검토 및 개선
앞서 "텍스트 대비"와 유사하게 전면 이미지와 배경 간의 대비율(Contrast ratio)이 3.0 이상 되어야 하는데, 이에 미치지 못해서 검출되었습니다.
![](https://t1.daumcdn.net/cfile/tistory/993FAC3F5C4F2F3C02)
여기서는 색조를 조정해서 버튼과 나무 질감의 배경 간의 대비율을 높이도록 해보겠습니다.
android:backgroundTint에 @color/colorPrimary(=#3F51B5 " ")를 추가했습니다.
수정 전
<ImageButton
android:id="@+id/ibPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:src="@android:drawable/ic_media_previous"
android:contentDescription="@string/previous_song"
app:layout_constraintEnd_toStartOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
<ImageButton
android:id="@+id/ibPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:src="@android:drawable/ic_media_play"
android:contentDescription="@string/play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lvColor" />
<ImageButton
android:id="@+id/ibNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:src="@android:drawable/ic_media_next"
android:contentDescription="@string/next_song"
app:layout_constraintStart_toEndOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
수정 후
<ImageButton
android:id="@+id/ibPrev"
android:backgroundTint="@color/colorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:src="@android:drawable/ic_media_previous"
android:contentDescription="@string/previous_song"
app:layout_constraintEnd_toStartOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
<ImageButton
android:id="@+id/ibPlay"
android:backgroundTint="@color/colorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:src="@android:drawable/ic_media_play"
android:contentDescription="@string/play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lvColor" />
<ImageButton
android:id="@+id/ibNext"
android:backgroundTint="@color/colorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:src="@android:drawable/ic_media_next"
android:contentDescription="@string/next_song"
app:layout_constraintStart_toEndOf="@+id/ibPlay"
app:layout_constraintTop_toTopOf="@+id/ibPlay" />
아래와 같이 이미지와 배경 간의 구분이 좋아졌습니다.
"이미지 대비" 검토 항목까지 개선 후 접근성 검사기로 다시 스캔해보면 "제안사항이 없음"으로 모두 수정된 것을 확인할 수 있습니다.
![](https://t1.daumcdn.net/cfile/tistory/999F283E5C4F2F7123)
리스트 뷰의 아이템을 누르게 되면 아래 왼쪽과 같이 팝업(AlertDialog)이 발생하게 되어있습니다.
이것도 "접근성 검사기"로 스캔을 해보면 아래 오른쪽처럼 개선해야 할 1건이 확인됩니다.
![](https://t1.daumcdn.net/cfile/tistory/997E00345C4F2FAC16)
세부내용을 확인해보면, 제일 처음에 다루었던 "텍스트 대비"에 대한 문제점입니다.
![](https://t1.daumcdn.net/cfile/tistory/99C26F335C4F2FC401)
그런데, 이 팝업(AlertDialog)은 레이아웃으로 만든 게 아니어서, 테마를 적용해서 스타일에서 텍스트의 색상을 조절하도록 수정하였습니다.
MainActivity.java
수정 전
ListView colorListView = findViewById(R.id.lvColor);
colorListView.setAdapter(mColorListAdapter);
colorListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ColorItem selectedItem = (ColorItem) mColorListAdapter.getItem(position);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
AlertDialog alert = builder.setMessage(selectedItem.getColorName() + getResources().getString(R.string.dialog_msg) +
(selectedItem.getColorUse() ? getResources().getString(R.string.yes) : getResources().getString(R.string.no)))
.setPositiveButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
}).create();
alert.show();
}
});
수정 후
ListView colorListView = findViewById(R.id.lvColor);
colorListView.setAdapter(mColorListAdapter);
colorListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ColorItem selectedItem = (ColorItem) mColorListAdapter.getItem(position);
AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(MainActivity.this, R.style.AlertDialogCustom));
AlertDialog alert = builder.setMessage(selectedItem.getColorName() + getResources().getString(R.string.dialog_msg) +
(selectedItem.getColorUse() ? getResources().getString(R.string.yes) : getResources().getString(R.string.no)))
.setPositiveButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
}).create();
alert.show();
}
});
styles.xml
수정 전
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style></resources>
수정 후
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AlertDialogCustom" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:textColor">#000000</item>
<item name="android:typeface">normal</item>
<item name="android:textSize">20dp</item>
</style>
</resources>
수정된 팝업은 아래와 같이 나옵니다. "확인" 버튼의 색상이 검정으로 변경되었고, 크기도 조금 더 커졌습니다.
![](https://t1.daumcdn.net/cfile/tistory/99E190385C4F302D1A)
접근성 테스트 소스 (개선 후)
AccessibilityTest_after.zip
결론
접근성 개선을 위한 도구인 "접근성 검사기"를 이용해서 접근성 개선하는 예제를 확인해보았습니다.
때로는 너무 접근성 개선을 위해서 수정을 하다가 보면 많이 복잡해질 뿐만 아니라 접근성이 필요 없는 사람에게는 높은 대비로 인하여 시각적인 불편함을 느낄 수도 있습니다.
이와 관련해서 최초 앱 진입 시 접근성이 활성화 되어 있는지를 체크해서 이에 따라서 레이아웃을 다르게 보이도록 처리하는 방법도 하나의 대안이 될 수 있을 것 같습니다.
활성화 여부 체크 관련한 내용은 아래 글에 포함되어 있으니 참조하시길 바랍니다.
2018/12/15 - 앱 진입시 스크린 리더(TalkBack, Voice Assistant) 감지하기
긴 글 읽어주셔서 감사합니다.