Android Accessibility
Ascii @ KKBOXhttps://www.facebook.com/asciiss
Accessibility
Screen reader (TalkBack)
Braille support (BrailleBack)
放大手勢 (連敲 3 下)
輕觸並按住的延遲時間 (for 技體障礙)
more ...
TalkBack
起始畫面Android 4.0 用手指畫矩型Android 4.1 以上雙指長按
設定協助工具 -> TalkBack
快速啟動 TalkBack
步驟一:按住電源鍵直到聽見音效步驟二:雙指觸碰螢幕直至聽見音效
TalkBack 常用手勢
選擇 (Hover):單擊
開啟 (Click):雙擊
捲動:雙指向上、下、左、右滑動
選擇上 or 下一項:單指向上、下、左、右滑動
回主畫面:單指上滑 + 左滑
返回:單指下滑 + 左滑
最近畫面:單指左滑 + 上滑
Notification:單指右滑 + 下滑
Labeling User Interface Elements
android:contentDescriptionTextView ( contentDescription > text > hint )
ImageView
ImageButton
CheckBox
android:hint
EditText ( text > hint > contentDescription )
Enabling view focus
setFocusable()
isFocusable()
requestFocus()
android:nextFocusDown
android:nextFocusLeft
android:nextFocusRight
Common Issues Demo
https://github.com/AsciiHuang/AndroidAccessibilityPractices
ArrayAdapter
listView.setAdapter(new ArrayAdapter<String>(
getActionBar().getThemedContext(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
new String[] {
“Custom View”,
“Bad List”,
“Grid And Navigation”,
}));
// How to set android:contentDescription attribute ?
BaseAdapter
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(
android.R.layout.simple_list_item_activated_1,
parent, false);
}
((TextView) convertView).setText(drawerItems[position]);
convertView.setContentDescription(drawerItemDescriptions[position]);
return convertView;
}
Basic Accessibility
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = new ViewHolder();
getViewHolder(convertView);
viewHolder.title.setText(Constant.Titles[position]);
viewHolder.subTitle.setText(Constant.SubTitles[position]);
String strDescription = String.format("%s, %s, %s",
Constant.Titles[position],
context.getString(R.string.artist), // 歌手 or Artist
Constant.SubTitles[position]);
convertView.setContentDescription(strDescription);
return convertView;
}
AccessibilityNodeInfosetFocusable
setScrollable
setCheckable
AccessibilityEventsetChecked
setItemCount
setFromIndex
Custom View
Custom View
AccessibilityDelegatesendAccessibilityEvent
dispatchPopulateAccessibilityEvent
onPopulateAccessibilityEvent
onInitializeAccessibilityNodeInfo
onInitializeAccessibilityEvent
Custom View
public class CustomView extends View {
…
...
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
}
…
...
}
Custom Viewpublic class CustomView extends View {
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
setAccessibilityDelegate(new CustomDelegate());
}
}
public class CustomDelegate extends View.AccessibilityDelegate {
@Override
public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
return super.dispatchPopulateAccessibilityEvent(host, event);
}
}
AccessibilityDelegate
private AccessibilityDelegate accessibilityDelegate = new AccessibilityDelegate() {
@Override
public void sendAccessibilityEvent(View host, int eventType) {
// 計算 currentIndex
}
@Override
public boolean dispatchPopulateAccessibilityEvent(View, AccessibilityEvent) {
int type = event.getEventType();
if (type == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
event.getText().add("總共 10 項, 目前顯示第 " + (currentIndex + 1) +
" 項.");
}
return super.dispatchPopulateAccessibilityEvent(host, event);
}
};
AccessibilityNodeInfoprotected int currentIndex = 0;
public void sendAccessibilityEvent(View host, int eventType) {
if (eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
currentIndex = mHScrollView.getScrollX() / 900;
}
super.sendAccessibilityEvent(host, eventType);
}
public boolean dispatchPopulateAccessibilityEvent(View host,AccessibilityEvent event) {
if (event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
event.getText().add("向左或向右滑動可選擇不同圖片");
}
return super.dispatchPopulateAccessibilityEvent(host, event);
}
AccessibilityNodeInfopublic void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
event.setScrollable(true);
event.setItemCount(10);
event.setFromIndex(currentIndex);
super.onInitializeAccessibilityEvent(host, event);
}
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
info.setScrollable(true);
super.onInitializeAccessibilityNodeInfo(host, info);
}
AccessibilityNodeInfoprivate boolean mChecked = false;
public View onCreateView(LayoutInflater, ViewGroup, Bundle) {
btn.setOnClickListener(onChangeContentBClicked);
btn.setAccessibilityDelegate(buttonAccessibilityDelegate);
}
private View.OnClickListener onChangeContentBClicked = new View.OnClickListener() {
@Override
public void onClick(View v) {
mChecked = !mChecked;
}
};
AccessibilityNodeInfoprivate AccessibilityDelegate buttonAccessibilityDelegate = new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setChecked(mChecked);
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setCheckable(true);
info.setChecked(mChecked);
}
};
announceForAccessibility
public boolean onHoverEvent(MotionEvent event) {
int currentPos = getBlock(event.getX(), event.getY());
if (currentPos != prePos) {
prePos = currentPos;
if (currentPos == 0) {
announceForAccessibility("左上方"); // API 16
} else if (currentPos == 1) {
announceForAccessibility("左下方");
} ...
}
return super.onHoverEvent(event);
}
Reference
https://github.com/aduggin/android-accessibility
https://www.youtube.com/watch?v=BPXqsPeCneA
https://www.youtube.com/watch?v=q3HliaMjL38
https://developer.android.com/guide/topics/ui/accessibility/index.html
http://developer.android.com/design/patterns/accessibility.html
https://github.com/googlesamples/android-BasicAccessibility
Top Related