现在的位置: 首页 > 综合 > 正文

Android 无障碍服务一 让应用具有辅助性服务

2014年08月29日 ⁄ 综合 ⁄ 共 11738字 ⁄ 字号 评论关闭

1.辅助功能

    许多Android用户有不同的能力(限制),这要求他们以不同的方式使用他们的Android设备。这些限制包括视力,肢体或与年龄有关,这些限制阻碍了他们看到或充分使用触摸屏,而用户的听力丧失,让他们可能无法感知声音信息和警报。
    Android提供了辅助功能的特性和服务帮助这些用户更容易的使用他们的设备,这些功能包括语音合成、触觉反馈、手势导航、轨迹球和方向键导航。Android应用程序开发人员可以利用这些服务,使他们的应用程序更贴近用户。
    Android开发者也可以建立自己的辅助功能服务,这些服务可以提供增强的应用性,比如音频提示、物理反馈,和替代导航模式。辅助功能服务可以对所有应用程序,一组应用程序或仅仅一个单一的应用程序提供这些增强的功能。
    接下来说明如何使用Android框架,使应用程序更贴近用户。

2.让应用具有辅助功能

    当这些视觉障碍、物理障碍或与年龄限制的用户在一台Android设备开启了辅助功能,Android应用程序更加的方便用户。即使你没有在代码中做任何的辅助功能,这些服务也会让你的应用程序更贴近用户。而你要做的仅仅是需要完成几个步骤来优化你的应用程序的辅助功能,为所有用户,提供一个愉快的体验。
    确保你的应用程序可以贴近所有的用户,特别是当你使用Android框架提供的组件创建用户界面时,只需要几个步骤。。如果在你的应用程序中只使用了标准组件,这些操作步骤是:
  1. 为你应用中的用户界面控件,通过使用android:contentDescription属性,添加描述性文本, 尤其是ImageButton,ImageView和CheckBox控件。
  2. 确保所有用户界面元素,可以接受输入( 触摸或打字),可以使用方向控制器如:一个轨迹球,方向键(物理或虚拟)或导航手势进行控制。
  3. 确保音频提示总是伴随着另一个视觉提示或通知,协助听力障碍的用户。
  4. 只使用辅助功能的导航服务来测试你的应用程序。通过触摸打开TalkBack 和Explore,然后尝试使用方向控制你的应用。更多信息可访问性测试参考辅助功能开发清单。
    如果你构建自定义控件,扩展视图类,您必须完成一些额外的工作来确保你的组件是有辅助功能的。本文档讨论如何使自定义视图控制兼容的辅助功能服务。
    注:本文档中描述的实现步骤让您的应用程序的适合失明或低视力的用户使用。为了满足听力障碍的用户使用请参考辅助功能开发清单。

2.1 用户界面的标签元素

    许多用户界面控件依赖视觉线索来表示他们的意义和用法。例如,一个记笔记的应用程序可能会使用一个带加号图片的ImageButton表示用户可以添加一条新的笔记。在一个EditText组件旁边可能会有一个标签来说明需要输入的内容。视力较弱的用户看不到我们给出这些提示,这使得这些提示毫无用处。
    你可以使用在XML布局中的android:contentDescription 属性使这些控件更容易理解。 添加了这个属性的文本并不出现在屏幕上,但如果用户打开了提供声音提示的辅助功能服务,那么当用户进行访问控制时,文本会被讲出来。
    出于这个原因,将android:contentDescription属性应用在你应用程序用户界面的每个ImageButton ImageView,CheckBox上,并且在其他输入控件中添加该属性,对于无法看到输入控件的用户,这些额外的信息是很有必要的。
    例如,下面的带加号的ImageButton设置了content description的string资源add_note,在英语界面中可能被定义为“Add note”
<ImageButton
    android:id=”@+id/add_note_button”
    android:src=”@drawable/add_note”
    android:contentDescription=”@string/add_note”/>
 由于添加了这个属性,当用户移动焦点到这个按钮或将鼠标悬停在它上面时,提供口头反馈的辅助功能服务就会发出“Add note”。
    注意:对于EditText控件,提供了一个android:hint属性代替了contentDescription属性,这个属性可以在内容为空时,提示用户应该输入的内容。当输入完成后,TalkBack 读出输入内容给用户,不再是提示的文本内容。

2.2 启用焦点导航

    焦点导航允许残疾用户使用方向控制器完成对用户界面控件的操作。方向控制器可以是物理的,比如一个轨迹球,方向键(摇杆)或箭头键,或是虚拟的,如Eyes-Free Keyboard,在Android 4.1和更高版本中的手势导航模式。方向控制器是许多Android用户的一个主要导航手段。
    确保用户在你的应用程序只使用一个方向控制器,不使用触摸屏就可以验证你应用程序可以到达的所有用户界面(UI)的输入控件。你也可以点击方向控制器的中心按钮(或OK按钮),点击的效果和触摸已经获取焦点的按钮的效果相同。关于测试方向控制的内容,参考测试焦点导航一节。

2.2.1 启用视图焦点

    一个用户界面元素,其android:focusable属性设置为true时,是可以使用方向控制的。此设置在元素获取焦点后,允许用户使用方向控制与之交互。Android框架提供的用户界面控制,在默认情况下都是获取焦点和可见的,通过改变控件的外观来表明当前焦点的存在与否。
    Android提供了几个api允许您控制用户界面控制是否可以获取焦点,是否可以获取一个焦点:
如果一个view是缺省情况下是没有焦点的,可以在你的布局文件中通过设置android:focusable属性为true或通过调用其setFocusable()方法来获取焦点。

2.2.2 控制焦点顺序

    当用户在任何方向使用方向控制导航,焦点会确定的顺序从一个用户界面元素(视图)到另一个。这个顺序是根据一个算法,这个算法在一个给定的方向上寻找最近的邻居。在极端的情况下,该算法可能不匹配这个确定的顺序,可能不符合用户的逻辑。在这些情况下,您可以在你的布局文件使用下面的XML属性显式的指定焦点的顺序:
定义当用户按导航下键时接收焦点的view
android:nextFocusLeft
定义当用户按导航左键时接收焦点的view
android:nextFocusRight
定义当用户按导航右键时接收焦点的view
android:nextFocusUp
定义当用户按导航上键时接收焦点的view
    下面的示例XML布局展示了两个focusable用户界面元素,android:nextFocusDown和android:nextFocusUp属性被显式地设置。TextView在EditText的右边。 然而,由于这些属性的设置,
当焦点在EditText元素上,通过按向下箭头键焦点切换到TextView元素上。
<LinearLayout android:orientation="horizontal"
        ... >
    <EditText android:id="@+id/edit"
        android:nextFocusDown=”@+id/text”
        ... />
    <TextView android:id="@+id/text"
        android:focusable=”true”
        android:text="Hello, I am a focusable TextView"
        android:nextFocusUp=”@id/edit”
        ... />
</LinearLayout>
  当修改焦点的顺序,确保导航在每个用户界面控件中各个方向焦点是按照预期的顺序的切换,在反向(焦点切换到当前控件的上一控件)是按照预期的顺序切换。
    注意:您可以在用户界面组件运行时修改焦点的顺序,使用方法如setNextFocusDownId()和setNextFocusRightId()。

2.3 创建辅助功能的自定义视图

    如果您的应用程序需要一个自定义的视图组件,你必须做一些额外的工作来确保您的自定义视图是支持辅助功能。这些是主要的任务,确保你的视图的辅助功能:
  • 处理方向控制器点击
  • 实现accessibility API 的方法
  • 发送你自定义视图特定的AccessibilityEvent对象
  • 为你自定义视图填充AccessibilityEvent和AccessibilityNodeInfo对象

2.3.1 处理方向控制器点击

    在多数的设备,使用方向控制器点击视图发送一个带KEYCODE_DPAD_CENTER的KeyEvent到当前焦点的视图。所有标准Android的view已经对KEYCODE_DPAD_CENTER适当处理。 当构建一个自定义的视图控制,确保这一事件,与在触摸屏触摸视图具有相同的效果。
    自定义控制也应该使KEYCODE_ENTER对应的输入事件与KEYCODE_DPAD_CENTER一样。这种方法使用户更容易通过全键盘进行交互。

2.3.2 实现accessibility API 的方法

    在应用程序中,辅助功能事件是用户与可视界面组件交互的消息。这些消息是由辅助功能服务处理。辅助功能服务使用在这些事件中的信息产生附加的反馈和提示。Android 4.0(API级别14)和更高版本上,比Android 1.6(API级别4)中介绍的AccessibilityEventSource界面,辅助功能事件已经扩展,可以提供更详细的信息。扩展后的辅助功能方法属于View类的一部分,也是View.AccessibilityDelegate的一部分。方法如下:
    (API级别4)当用户在一个视图操作时调用此方法。事件是按照用户操作类型分类,如TYPE_VIEW_CLICKED。你通常不需要实现该方法,除非你是创建一个自定义视图。
    (API级别4)当调用代码需要直接控制检查辅助功能设备的启用时,调用该方法。(AccessibilityManager.isEnabled())。如果你实现这个方法,不管实际的系统设置,执行调用时,必须假设辅助功能是启用的。一个自定义的视图,通常不需要实现该方法的。
    (API级别4)当你的自定义视图生成一个辅助功能的事件时,系统调用这个方法。在API级别14,为当前视图,默认调用该方法的实现onPopulateAccessibilityEvent(),然后为当前视图的的子视图调用dispatchPopulateAccessibilityEvent()。在Android 4.0之前(API级别14),为了支持辅助功能服务,在自定义视图中,必须重写此方法和用描述性文本填充getText(),这就是所说的辅助功能服务,如TalkBack。
    (API级别14)这个方法在你的视图中设置AccessibilityEvent接口中的语音提示文本。如果当前视图是产生辅助功能事件视图的子视图,也要调用该方法。
    注意:在当前方法的文本上修改附加属性,可能覆盖属性设置的其他方法。虽然您可以修改辅助功能事件方法中的属性,您应该限制这些对文本内容的修改,并使用onInitializeAccessibilityEvent()方法来修改其它事件的属性。
    注意:如果这一事件的实现方法完全重写了输出文本,并且这些文本不允许布局中其它部分修改其内容,这时不必在代码中调用父类的方法的实现。
    (API级别14)系统调用这个方法在当前文本中,获取视图的状态信息。
如果自定义视图在TextView或Button上提供了交互式控制,你应该重写此方法,并需要在这个事件中设置你的视图的状态信息。这些状态信息如密码字段类型,复选框类型或
,提供用户交互或反馈的状态信息。如果你做重写这个方法,您必须调用它父类的实现,然后只修改在超类中没有设置的属性。
    (API级别14)这种方法提供了视图状态信息辅助功能服务。默认视图实现一组标准的视图属性,但如果您的自定义视图在TextView或Button提供了交互式控制服务,您应该重写此方法,并且通过该方法在AccessibilityNodeInfo对象中设置你试图的额外的信息。
    (API级别14)当你视图中的子视图产生了一个AccessibilityEvent,系统调用这个方法。这个步骤允许父视图修改辅助功能的事件和其他信息。如果你的自定义视图有子视图,如果父视图可以对辅助功能服务有用的事件提供上下文信息,此时,必须实现该方法。
    为了支持自定义的视图的这些辅助功能方法,应该采取以下方法:
  • 如果你的应用程序是Android 4.0(API级别14)以上,直接在自定义视图类中覆盖并实现上面列出的辅助功能方法。
  • 如果自定义视图为兼容安卓1.6(API 4级)及以上,对你的项目中加上Android Support Library,版本5或更高。然后,在您的自定义视图类,调用ViewCompat.setAccessibilityDelegate()方法来实现上面的辅助功能方法。 对于这种方法的一个示例,请参阅Android支持库(修订5或更高)示例。
    在这两种情况下,你应该在自定义视图类中实现以下辅助功能方法:
关于这些方法更多的实施信息,请参阅填充辅助功能事件一节

2.3.3 发送辅助功能事件

    根据您的自定义视图的细节,它可能需要在不同时间或系统默认没有实现的事件发送AccessibilityEvent对象。视图类对这些事件类型提供了一个默认实现:
    
    注意:悬停事件与浏览器的触摸属性有关,通常将这些悬停时间作为在用户界面元素提供声音提示的触发器。
    一般来说,每当你的自定义视图的内容变化时,应该发送一个AccessibilityEvent。例如,如果您正在实现一个自定义滚动条,允许用户按左或右箭头选择一个数值,当滑块的值改变时,自定义视图应该发出一个事件类型TYPE_VIEW_TEXT_CHANGED。下面的示例代码演示了如何使用该sendAccessibilityEvent()方法来报告这个事件。
    @Override
public boolean onKeyUp (int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
        mCurrentValue--;
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
        return true;
    }
    ...
}

2.3.4 填充辅助功能事件

    每个AccessibilityEvent有一组必需的描述当前状态的视图的属性。这些属性包括诸如视图的类名称、内容描述和检查状态。在AccessibilityEvent参考文档中描述了每个事件类型的特定属性要求。视图的实现提供了这些属性的默认值。这些值中包括包括类名和事件的时间戳。如果你正在创建一个自定义的视图组件,您必须提供一些关于内容和特征的信息。这个信息可能只要一个按钮的标签,但也可能包括你想添加到事件的额外状态信息。
    提供无障碍服务与一个自定义视图的最低要求是实现dispatchPopulateAccessibilityEvent()。这个方法被系统调用,要求AccessibilityEvent在自定义视图兼容的辅助功能服务在Android 1.6(API级别4)以上。下面的示例代码演示了该方法的基本实现。
@Override
public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
    super.dispatchPopulateAccessibilityEvent(event);
    // Call the super implementation to populate its text to the event, which
    // calls onPopulateAccessibilityEvent() on API Level 14 and up.

    // In case this is running on a API revision earlier that 14, check
    // the text content of the event and add an appropriate text
    // description for this custom view:
    CharSequence text = getText();
    if (!TextUtils.isEmpty(text)) {
        event.getText().add(text);
    }
}
  Android 4.0(API级别14)以上,使用onPopulateAccessibilityEvent()和onInitializeAccessibilityEvent()方法来填充或修改在一个AccessibilityEvent的内容。 使用onPopulateAccessibilityEvent()方法专门为添加或修改事件的文本内容,这些文本内容最终会转换为诸如TalkBack的声音提示。使用onInitializeAccessibilityEvent()方法用于填充事件的其他信息,比如选择视图的状态。
    此外,实现onInitializeAccessibilityNodeInfo()方法来填充 AccessibilityNodeInfo对象,通过该方法可查看的视图层次,在收到该事件时生成一个辅助功能的事件,为了获得一个更详细的上下文信息,并提供适当的反馈给用户。
    下面的代码示例显示了如何通过使用ViewCompat.setAccessibilityDelegate()覆盖这三个方法。 注意,此示例代码要求在你的项目中添加Android Support Library API 4(版本5或更高)。
ViewCompat.setAccessibilityDelegate(new AccessibilityDelegateCompat() {
    @Override
    public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
        super.onPopulateAccessibilityEvent(host, event);
        // We call the super implementation to populate its text for the
        // event. Then we add our text not present in a super class.
        // Very often you only need to add the text for the custom view.
        CharSequence text = getText();
        if (!TextUtils.isEmpty(text)) {
            event.getText().add(text);
        }
    }
    @Override
    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(host, event);
        // We call the super implementation to let super classes
        // set appropriate event properties. Then we add the new property
        // (checked) which is not supported by a super class.
        event.setChecked(isChecked());
    }
    @Override
    public void onInitializeAccessibilityNodeInfo(View host,
            AccessibilityNodeInfoCompat info) {
        super.onInitializeAccessibilityNodeInfo(host, info);
        // We call the super implementation to let super classes set
        // appropriate info properties. Then we add our properties
        // (checkable and checked) which are not supported by a super class.
        info.setCheckable(true);
        info.setChecked(isChecked());
        // Quite often you only need to add the text for the custom view.
        CharSequence text = getText();
        if (!TextUtils.isEmpty(text)) {
            info.setText(text);
        }
    }
}
  在Android 4.0(API级别14)以上的应用程序中,您可以在您的自定义视图类中直接实现这些方法。对于这种方法的另一个示例,请参见 Android Support Library(版本5或更高)示例AccessibilityDelegateSupportActivity在(<sdk>/extras/android/support/v4/samples/Support4Demos/).
    注意:你可能会发现在自定义视图中实现辅助功能之前,Android
4.0描述了使用dispatchPopulateAccessibilityEvent()方法填充AccessibilityEvents。在Android 4.0版本,然而,推荐的方法是使用onPopulateAccessibilityEvent()和onInitializeAccessibilityEvent()方法。

2.3.5 提供一个自定义辅助功能的上下文

    在Android 4.0(API级别14)的框架增强了辅助功能服务检查,包含用户界面组件的视图层次,生成一个可访问性的事件。框架允许辅助功能服务提供一个丰富的,上下文信息来帮助用户。
    有些情况下,辅助功能服务不能从视图层得到足够的信息。比如一个自定义控件,两个或两个以上的点击区域,如日历控件。在这种情况下,服务不能得到足够的信息,因为可点击的部分不包含在视图层上。
    在图中所示的例子中,整个日历被当作一个单独的视图,所以如果你不做其它事,辅助性服务得不到关于视图内容和用户在视图中选择的足够信息。例如,如果用户点击17号那天,辅助性框架只接收描述对整个日历控件的信息。在这种情况下,辅助反馈服务只会发出“Calendar”,好点的情况发出”April Calendar”的反馈,用户想知道哪天被选中。
    为了对辅助性服务提供足够的上下文信息,,框架提供了一种指定一个虚拟视图层次的方法。虚拟视图层次结构为应用程序开发人员提供一个互补的视图层,更接近在屏幕上实际的信息。这种方法允许辅助性服务给用户提供更多有用的上下文信息。
    一个虚拟视图层次可能使用的另一个情况是:一个用户界面包含一组控件(视图),功能相近,在一个控件上的动作影响一个或多个元素的内容,比如一个带单独的向上和向下按钮的数字选择器。在这种情况下,辅助性服务无法得到足够的上下文信息,因为在一个控件上的动作,要改变另一个控件的内容,这些控件的依赖关系在服务中是无法知道的。为了处理这种情况,用一个包含这些相关控件组的视图,并为这个视图提供一个虚拟视图层,这个虚拟的视图层能够提供这些组件的信息和行为。

2.3.6 处理自定义的触摸事件

    自定义视图控件可能需要非标准的触摸事件的行为。例如,一个自定义控件可以使用onTouchEvent(MotionEvent)来侦测ACTION_DOWN 和 ACTION_UP事件,触发特殊的单击事件。为了与辅助性服务保持兼容性,代码处理该自定义点击事件必须做到以下几点:
  •     生成一个接近解释AccessibilityEvent的点击动作。
  •     对不能使用触摸屏的用户提供自定义点击动作的辅助性服务。
    为了用一个有效的方法来处理这些需求,代码应该重写performClick()方法,该方法必须调用超类方法的实现,然后执行任何需要通过点击事件完成的操作。当检测到自定义点击击动作,代码应该调用你的performClick()方法。下面的代码示例演示了这种模式。
class CustomTouchView extends View {

    public CustomTouchView(Context context) {
        super(context);
    }

    boolean mDownTouch = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        // Listening for the down and up touch events
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownTouch = true;
                return true;

            case MotionEvent.ACTION_UP:
                if (mDownTouch) {
                    mDownTouch = false;
                    performClick(); // Call this method to handle the response, and
                                    // thereby enable accessibility services to
                                    // perform this action for a user who cannot
                                    // click the touchscreen.
                    return true;
                }
        }
        return false; // Return false for other touch events
    }

    @Override
    public boolean performClick() {
        // Calls the super implementation, which generates an AccessibilityEvent
        // and calls the onClick() listener on the view, if any
        super.performClick();

        // Handle the action for the custom click here

        return true;
    }
}
   上面所示的模式通过调用performClick()方法确保了自定义点击事件是与辅助性服务兼容的,performClick()方法既生成一个辅助性事件,又提供一个访问辅助性服务的入口,来代表用户执行了自定义的点击事件。
    注意:如果您的自定义视图具有明显的可点击区域,如自定义日历视图,在你自定义视图中,为了兼容辅助性服务,必须通过重写getAccessibilityNodeProvider()实现一个虚拟视图层。

2.4 测试辅助功能

    测试你应用程序的辅助功能是确保良好用户经验的一个重要组成部分。你可以启用声音反馈,在应用中只使用方向控制来测试你应用中最重要的辅助性特征。更多在应用程序中辅助性测试信息,请参阅辅助性测试清单一节。

抱歉!评论已关闭.