技术分享 | 如何在Android上进行滚轮选取器无障碍适配

2021年12月24日 804阅读

 

对于滚轮选取器相信大家一定不陌生。平时在各类买买买APP上填写收件地址,或在聊聊聊APP上填写生日,都能见到滚轮选取器的身影。

 

滚轮选取器最早出现于iOS系统上,因其具备交互方便快速、减少用户记忆负担等优点而被广泛使用。

 

但是,你知道吗?如果滚轮选取器没有做好无障碍适配,那对于使用读屏的视障用户来说,滚轮选取器不仅不能提供便利,反而会阻碍他们使用产品的功能和服务。

 

让我们通过一个小视频来体验下视障用户在遇到未经适配的滚轮选取器时的心情。

 

 
 
 
 

 

视频:未经适配的滚轮选取器操作视频

 

当设备上开启读屏软件后,系统便会将屏幕上显示的内容通过文本转语音(TTS)朗读给用户。同时,大家平常惯用的点、滑、拖等手势都会发生变化。

 

此时,如果滚轮选取器上的选项不能被读屏软件获取,则系统将无法朗读出滚轮上的选项。若未做兼容读屏软件的手势适配,则视障用户也将无法选取项目。

 

所以,我们若要对产品中的滚轮选取器进行无障碍适配,则通用思路如下:

 

* 让读屏软件可以聚焦到滚轮选取器上。

* 让读屏软件可以朗读出滚轮选取器中的选项,尤其是当前高亮的选项。

* 让用户可以通过特定手势或按键来选取所需值。

 

按照以上思路进行优化适配,视障用户便也能体验到滚轮选取器的方便快捷啦。让我们再通过一个短视频来体验下优化过后的使用效果。

 

 
 
 
 

 

视频:完成适配的滚轮选取器操作视频

 

前方高能!

 

下面让我们从代码角度来谈谈具体的思路建议。

 

*本例参考项目GitHub地址:https://github.com/Bigkoo/Android-PickerView

 

首先是布局上的优化:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
<com.contrarywind.view.WheelView    android:id="@+id/year"    android:layout_width="0dp"    android:layout_height="match_parent"    android:layout_weight="0.96"    <!--以下保证滚轮项目View可被读屏软件聚焦-->    android:focusable="true"    android:focusableInTouchMode="true"/>

 

当然,也可以在View的构造方法中处理让读屏软件可聚焦:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
public WheelView(Context context, AttributeSet attrs) {    ...    // 设置读屏软件可聚焦    this.setFocusable(true);    this.setFocusableInTouchMode(true);    ...}

 

其次,让读屏软件可以朗读出滚轮上的内容:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Overridepublic void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {    super.onInitializeAccessibilityNodeInfo(info);
    // 当读屏软件聚焦到滚轮选项列表View上时便会朗读出当前高亮的内容    // 以及改变值的操作方法。    info.setContentDescription("" + adapter.getItem(selectedItem).toString() + label);    info.setHintText("按下音量键,或通过翻页手势来调整值");    ...}

 

解决完部分朗读问题后,让我们再来解决手势适配:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
@Overridepublic void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {    super.onInitializeAccessibilityNodeInfo(info);
    // 给滚轮选取器添加读屏特定手势    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);    ...}
private int mA11ySelectedItem;@Overridepublic boolean performAccessibilityAction(int action, Bundle arguments) {    // 拦截在上一步中添加的读屏手势    // 并处理对应手势下的滚轮选取逻辑    switch (action) {        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:            setTotalScrollY(--mA11ySelectedItem * itemHeight);            break;        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:            setTotalScrollY(++mA11ySelectedItem * itemHeight);            break;        default:            return super.performAccessibilityAction(action, arguments);    }
    // 通知重绘视图    invalidate();    // 当滚轮选取器值发生变化时    // 普通用户可以通过动画感知到变化过程    // 同样,视障者也需要感知这个变化过程    // 可调用View.announceForAccessibility(...)方法    // 让读屏软件实时将辛变化的值朗读给用户    this.postDelayed(new Runnable() {        @Override        public void run() {            announceForAccessibility(adapter.getItem(selectedItem).toString() + label);        }    }, 2);// 延迟2ms保证界面已经重绘完成
    return true;}

 

通过上述代码简单展示,大家不难发现适配滚轮选取器这样稍复杂的交互所需要的代码量也是很少的。所以,给产品适配无障碍并没有大家想象中那么复杂。

 

在此,也想呼吁各位程序员小伙伴:在日常编码时可以尽量去优化产品的无障碍体验,你的随手之举,就能帮助许多视障朋友更好地生活。

 

参考文档:

* View:

https://developer.android.com/reference/android/view/View

* AccessibilityNodeInfo:

https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo

 

作者:吴益明

编辑:幽默丝