自定义EditText的无障碍描述(不读hint)
问题
我们一般给一个控件设置描述时,会这样:
1 | xxxView.setContentDescription("xxx"); |
但是,当你给EditText设置这个时,会发现毫无卵用。为什么呢?
搜了下EditText和其直接父类TextView,没有重写setContentDescription方法,那应该不是setXXX时发生改变,而是getXXX的问题。
果然,在TextView中发现:
1 | /** |
所以EditText在获取到无障碍焦点时,只会朗读hint文本,而不是contentDescription。其实这个设计是没有问题的,可编辑控件,在没有输入内容时,就应该朗读hint。
但是,某些自定义控件是长这样的:
label是自定义View画上去的,没做特殊处理的情况下Talkback识别不到,最好的体验是把左边的label也跟着读出来(比如读成:“列车车次,例:G1”),这可咋办?
解决
很显然,不能直接去改hint,否则UI显示不对。
尝试一:获取焦点时我自己读一串文本行不行
我们知道,可以通过:
1 | xxxView.announceForAccessibility("xxx"); |
来进行无障碍朗读,但是并没有一个类似setOnFocusChangeListener的方法来专门监听无障碍焦点,所以这个不好搞。
尝试二:Read the fucking code
其实无障碍开发中还有一些关键方法,且Talkback这些无障碍辅助工具最终其实也会触发这些方法的:
1 | xxxView.requestAccessibilityFocus(); // 获取无障碍焦点,自动朗读已设置的描述 |
深入后可以跟踪到:
1 | public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { |
这个onInitializeAccessibilityEvent的源码注释写得很明白,就是控件获取到无障碍事件时会触发,但通过event参数我们貌似做不了什么。
在Textview中我们发现,与onInitializeAccessibilityEventInternal相邻有一个内部方法 onInitializeAccessibilityNodeInfoInternal:
1 | public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { |
这个 info.setText(getTextForAccessibility()); 就是关键了,它其实才是最终朗读出来的那个文本。
公共方法onInitializeAccessibilityNodeInfo的注释也说明了:Initializes an {@link AccessibilityNodeInfo} with information about this view. 此方法初始化一些View的无障碍基本信息。
最终解决:
在自定义的EditText类中重写方法,覆盖文本,这样在朗读时就是自己想要的了:
1 |
|
Tips:其实这里为了API统一,我是直接 info.setText(getContentDescription()); 方便很多。