Flex Accessibility 全键盘支持和扩展

来源:互联网 发布:excel数据生成图表 编辑:程序博客网 时间:2024/05/20 09:45

Flex 和 Accessibility 简介

Flex 是一个用来创建丰富信息的互联网应用程序的服务器组件。Flex 内置的界面可以由 Macromedia Flash 播放器显示在客户端系统上,Flex 的本质上是一种描述应用程序界面的 XML 语言 (MXML) 以及一个 ECMA 规范的脚本语言(ActionScript),它能处理用户和系统的事件,并构建复杂的数据模型。Flex 作为实现 RIA 应用的一种重要语言,以其丰富的用户界面体验,简单的开发模式得到了广泛的应用。

Accessibility 简称 A11Y,指的是软件产品的可访问性,易用性。Accessibility 的目的是使产品不仅能供普通的用户使用,同时也能满足一些特殊客户群体如视力、听力、肌肉运动(无法使用鼠标)有障碍的用户的使用需求。这就要求软件产品必须尽量满足以下几个方面:

  1. 以特殊的样式如白背景黑字或黑背景白字显示用户界面以满足视力有缺陷的用户
  2. 所有用户界面的文字、输入框等都能通过某种工具(Screen Reader)以语音的形式展示以方便视力有缺陷的用户
  3. 用户界面的每个控件如图片,文字,按钮等,都能通过某种方式用键盘操作(全键盘支持),如使用 Tab 键可以在各个控件之间切换。

很多用户界面开发语言都提供了对 Accessibility 的支持,Flex 也提供了对 Accessibility 的支持,通过使用不同的样样式和主题,Flex 应用能够简单的实现上述第一个要求,通过设置每个控件的 Label 或 tooltip 也能简单的满足第二个要求,Flex 对全键盘的支持要复杂一些。本文主要介绍 Flex 的全键盘支持以及如何利用已有的支持进行扩展以实现产品级的更好的全键盘支持。

Flex Accessibility 全键盘支持

Adobe 提供的官方 Flex 开发文档对于 Accessibility 部分的介绍比较简单,本小节将重点介绍 Flex 中与 Accessibility 全键盘相关的接口,类,方法,变量以及通过实例说明如何使用它们。

配置 Flex 应用的编译属性以支持 Accessibility

Flex 提供了“accessible”参数以配置应用是否支持 Accessibility。在 Flex Builder 中,用户可以通过 ProjectPropertiesFlex CompilerGenerate accessible SWF file 配置,如图 1 所示:


图 1.Flex Builder 配置
 

如果使用 mxmlc 命令行,可以加“-accessible”选项,如果用户是通过 Ant 脚本调用 mxmlc 任务编译应用程序(如下代码所示):


清单 1. Ant mxmlc 任务
  <mxmlc file="Test.mxml" output="Test.swf"  locale="en_US">  <load-config filename="flex_config.xml"/>    <source-path path-element="src"/>  </mxmlc> 

用户可以在 load-config 的配置文件 flex-config.xml 文件中添加以下配置选项:


清单 2. accessible 配置
  <compiler>  ...  <accessible>true</accessible>  ...  </compiler> 

与 Accessibility 全键盘支持相关的重要的接口

IFocusManager 接口

IFocusManager 定义了一类接口,组件必须实现此类接口才能管理组件中的焦点以响应鼠标活动或键盘活动(Tab 键),才能支持默认按钮。FocusManager 类实现了该接口,关于该接口定义的方法在 FocusManager 类一节有详细描述。

IFocusManagerComponent 接口

IFocusManagerComponent 定义了一类接口,组件必须实现此类接口才能从 FocusManager 获得焦点。大部分的 Flex 组件如 Button, TextInput, CheckBox 等已经实现了该接口,因此它们可以获得焦点,也有一些组件如 Text,Label,Image 等并没有实现该接口,所以这些组件无法获得焦点,我们可以通过覆写该组件并实现 IFocusManagerComponent 接口让这些组件也可以接受焦点。我们以 Text 和 Image 为例实现可以获得焦点的 AccText,AccImage 组件。


清单 3.AccText 类
  // 继承 IFocusManagerComponent 接口并设置 tabEnabled 属性为 true 使 AccText 类可以接受焦点 public classAccText extendsText  implementsIFocusManagerComponent  {  public functionAccText()  {  Super();  this.tabEnabled = true;  }  } 


图 2.普通 Text 和 AccText 示例
图 2:普通 Text 和 AccText 示例 

清单 4.AccImage 类
  // 继承 IFocusManagerComponent 接口并设置 tabEnabled 属性为 true 使 AccImage 类可以接受焦 public classAccImage extendsmx.controls.Image    implementsIFocusManagerComponent  {  public functionAccImage()  {  Super();  this.tabEnabled = true;  }  } 


图 3.普通 Image 和 AccImage 示例
图 3:普通 Image 和 AccImage 示例 

所有直接继承 UIComponent 的组件都可以通过实现 IFocusManagerComponent 接口的方式来使组件支持 Accessibility 键盘支持,一些容器组件如 HBox 默认在获得焦点后会马上把焦点传递给它的第一个孩子组件,自己无法获得焦点,也无法通过调用 focusManager.setFocus(box) 把焦点赋给 HBox,因为 HBox 本身没有实现 IFocusManagerComponent 接口,我们可以通过覆写 HBox 组件让 HBox 本身也可以获得焦点并显示出来。以下代码实现一个可以获得焦点的 AccHBox.


清单 5. AccHBox 类
  /**  * 继承 IFocusManagerComponent 接口并设置 tabEnabled,focusEnabled 和 tabChildren 属性为 true 使 * AccHBox 类可以接受焦点,并且它的孩子也都可以接受焦点 */  public  classAccHBox extendsHBox implementsIFocusManagerComponent  {  public functionAccHBox()  {  Super();  this.focusEnabled = true;  this.tabEnabled = true;  this.tabChildren = true;  }  } 


图 4.AccHBox 示例
图 4:AccHBox 示例 

IFocusManagerContainer 接口

IFocusManagerContainer 定义了一类接口,容器组件必须实现此类接口才能承载 FocusManager。大部分继承 Container 的的 Flex 容器组件都实现了该接口,List 和 ListBase 没有实现该接口,因此 List 的内容是无法获得焦点的,如果用户自定义的组件继承 List 或者 ListBase,必须实现该接口才能支持 Accessibility 全键盘,mx.controls.Menu 继承 List 并实现了 IFocusManagerContainer 接口,Menu 的源代码可以做为用户扩展 List 的参考。

IFocusManagerComplexComponent 和 IFocusManagerGroup

IFocusManagerComplexComponent 定义了一类接口,可以拥有多个内部焦点目标的组件可以实现实现此类接口以获得来自 FocusManager 的焦点,该接口开发人员很少需要用到,FlexHTMLLoader 组件实现了此接口,它允许同时存在 Flex 的焦点和 Html 的焦点。

IFocusManagerGroup 定义了一类接口,分组后归到具有以下特征的集中的组件都必须实现此类接口:在任何给定的时刻,都只能选择集中的一个成员。例如,RadioButton 实现 IFocusManagerGroup,因为在任一时刻,同一组中的一系列 RadioButton 只可以有一个 RadioButton 被选中,并且 FocusManager 需要确保不为那些未被选中去响应使用 Tab 键移动焦点的 RadioButton 提供焦点。

这两个接口的使用范围比较小,在实际的 Flex 应用开发中使用的频率也很低,这里不做过多的描述。

与 Accessibility 全键盘支持相关的重要的类

FocusManager 类

FocusManager 类管理组件上的焦点,以响应鼠标活动或键盘活动(Tab 键),FocusManager 管理“组件级别”的焦点。可以由 FocusManager 管理的所有组件都必须实现 mx.managers.IFocusManagerComponent 接口 .

FocusManager 有几个非常重要也是开发人员经常会使用的方法:

setFocus(o:IFocusManagerComponent) 和 getFocus()

setFocus 方法将焦点设置到某个特定的实现了 IFocusManagerComponent 接口的组件。

setFocus 方法可以实现程序初始化完成后将焦点设定到某个特定组件,如一般的登录页面都会将默认的焦点设置在第一个输入框。我们可以监听 Application 的 createComplete 事件,然后调用 focusManager.setFocus(this.username) 把焦点设置在用户名的输入框上,如图 5 所示 :


清单 6. Login.mxml
  <?xml version="1.0" encoding="utf-8"?>  <mx:Application  xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:widget="*" layout="vertical" creationComplete="initApp()">  <mx:Script>  <![CDATA[  importmx.styles.StyleManager;  importmx.managers.FocusManager;  importmx.managers.PopUpManager;  importmx.controls.Menu;  // 程序初始化的时候将焦点设置到用户名的输入框 private functioninitApp():void {  this.focusManager.setFocus(this.username);  }  ]]>  </mx:Script>  <mx:Panel title="登录" >  <mx:HBox width="100%" id="box"/>  <mx:Form width="100%" height="100%">  <mx:FormItem label="用户名" width="45%">  <mx:TextInput id="username" />  </mx:FormItem>  <mx:FormItem label="密码" width="45%">  <mx:TextInput id="pwd"  displayAsPassword="true"/>  </mx:FormItem>  </mx:Form>  <mx:ControlBar width="100%">  <widget:AccHBox>  <widget:AccImage id="img" source="assets/icons/arrowRight_b.png"/>  <widget:AccText text="Register"/>  </widget:AccHBox>  <mx:Spacer width="80%">  </mx:Spacer>  <mx:Button label="登录" id="okBtn"/>  <mx:Button label="取消"/>  </mx:ControlBar>  </mx:Panel>  </mx:Application> 


图 5.Login 页面
图 5:Login 页面 

任何时候我们都可以调用该方法把焦点设置到某个特定的组件上。

getFocus() 方法获取当前具有焦点的 IFocusManagerComponent 组件。

showFocus() 和 hideFocus()

showFocus 方法在 showFocusIndicator 为 true 的时候在具有焦点的对象(如果有)上绘制可视的焦点指示符。有时候用户调用了 setFocus() 方法后虽然焦点已经设置成功,但是界面上没有任何焦点提示,这可能是因为 showFocusIndicatror 设置为 false,调用 showFocus() 方法会自动把 showFocusIndicator 设置为 true,并调用 UIComponent 的 drawFocus() 方法绘制可视的焦点指示符。相应地,如果用户希望焦点设置在某个组件上,但是不期望组件显示可视的焦点提示符,调用 hideFocus() 会自动把 showFocusIndicator 设置为 false,并从具有焦点的对象(如果有)上删除可视的焦点指示符。

UIComponet 类

UIComponent 类是所有可视组件(交互式和非交互式)的基类。交互式组件可以参与 Tab 切换和其它几种键盘焦点处理,接受低级事件(如键盘和鼠标输入),还可以被禁用,以便该组件不能收到键盘和鼠标输入。这与非交互式组件(如 Label 和 Text)相反,非交互式组件只显示内容且不能由用户操作。

UIComponet 类中有几个非常重要的方法与 Accessibility 有关,掌握这几个方法对开发人员开发自定义组件并支持 Accessibility 有更重要的作用。

getFocus(), setFocus() 和 drawFocus(isFocused:Boolean)

UIComponent 的 getFocus 方法和 FocusManager 的 getFocus 方法实现的功能相同,都是返回当前具有焦点的对象。而 setFocus 方法则有细微的差异,UIComponent 的 setFocus 方法不支持任何参数,但是它们实现的基本功能也是类似的,只是在程序中的调用方式不同。如上面的登录页面例子我们可以用 focusManager.setFocus(this.username); 和 this.usernamesetFocus(); 两个不同的语句实现相同的功能,相对而言,第一个语句更能体现出全局的焦点设置给了某个组件。

drawFocus 方法根据传入的参数在获得焦点的组件周围绘制(参数为 true)或删除(参数为 false)可视的焦点指示符,该方法被 FocusManager 的 showFocus 和 hideFocus 方法调用,用户可以通过 username.drawFocus(true) 和 focusManager.showFocus() 实现同样的功能。有些组件如 TextInput 在调用 setFocus 方法后就可以直接显示可视的焦点指示符,但是某些组件如 Button 在调用 setFocus 方法后不能显示可视的焦点指示符,对于这样的组件我们可以同时调用 setFocus 和 drawFocus 或 showFocus 以确保组件能正确显示或隐藏焦点指示符。

我们同样以登录页面为例:当用户第二次访问登录页面,cookie 里保存了上次登录的用户名和密码,页面默认的焦点应该设置在“登录”按钮上,调用 focusManager.setFocus(this.okBtn) 或者 this.okBtn.setFocus() 可以把焦点设置在登录按钮上,但是用户看到的却是如图 6 所示的界面,界面上没有任何焦点提示符可以让用户知道默认的焦点设置在登录按钮上。


图 6.无焦点提示符的登录按钮
图 6:无焦点提示符的登录按钮 

我们可以做如下的改进:在 focusManager.setFocus(this.okBtn) 方法之后我们调用 focusManager.showFocus() 或者 this.okBtn.drawFocus(true) 可以解决这个问题,如图 7 所示:


图 7.显示焦点指示符的登录按钮
图 7:显示焦点指示符的登录按钮 

focusInHandler(event:FocusEvent) 和 focusOutHandler(event:FocusEvent)

focusInHandler 方法和 focusOutHandler 方法是当 UIComponent 对象获得或者失去焦点时被调用的时间处理方法。可以通过覆写这两个方法改变组件的默认动作。如 HBox 默认在获得焦点后会马上把焦点传递给它的第一个孩子组件,我们可以覆写 focusInHandler 方法让 HBox 默认每次都把焦点传给最后一个孩子。


清单 7. AccHBox 类
publicclassAccHBox extendsHBox implementsIFocusManagerComponent  {  /**  * 继承 IFocusManagerComponent 接口并设置 tabEnabled,focusEnabled 和 tabChildren 属性为 true 使 * AccHBox 类可以接受焦点,并且它的孩子也都可以接受焦点 */  public functionAccHBox()  {  Super();  this.focusEnabled = true;  this.tabEnabled = true;  this.tabChildren = true;  }  /**  * 覆写 focusInHandler 函数改变 HBox 的默认行为,默认焦点在最后一个孩子 */  override protected functionfocusInHandler(event:FocusEvent):void {  super.focusInHandler(event);  if(this.numChildren <= 0) return;  varlastChild:DisplayObject = this.getChildAt(this.numChildren-1);  if(lastChild isIFocusManagerComponent)  {  this.focusManager.setFocus(IFocusManagerComponent(lastChild));  }  } 

}

keyDownHandler(event:KeyboardEvent) 和 keyUpHandler(event:KeyboardEvent)

之前介绍的所有的类和方法都与焦点有关,因为全键盘支持的关键就在焦点,组件只有在获得焦点后才能接受键盘的操作。对键盘操作的处理可以在 keyUpHandler 和 keyDownHandler 方法中实现。keyDownHandler 是 key Down 事件调用的事件处理函数,keyUpHandler 是 Key Up 事件调用的事件处理函数。基本上所有实现了 IFocusManagerComponent 接口的组件都覆写了这两个方法以实现键盘操作的支持。Flex 的全键盘支持有一套自己的规则,如一般的 Web 应用都会使用 Enter 键作为用户选择执行操作的键,Flex 则使用 Space 键(某些组件同时支持 Enter 和 Space),在开发 Flex 应用的时候要充分考虑到整个应用全键盘支持的一致性,所以建议在用户自定义的组件中同时支持 Space 键和 Enter 键或者只支持 Space 键,如果用户想只支持 Enter 键,那么需要覆写 Flex 所有不支持 Enter 键的组件以实现应用级的一致性,这显示是不可取的。以之前的 AccImage 为例,我们实现可以接受键盘操作的 AccImage.


清单 8. 扩展的 AccImage 类
   public  classAccImage extendsmx.controls.Image    implementsIFocusManagerComponent  {  public functionAccImage()  {  super();  this.tabEnabled = true;  }  /**  * 覆写 keyDownHandler 函数 , 改变 Image 的默认行为,当用户按下 space 和 enter 键的时候弹出对话框 */  protected override functionkeyDownHandler(event:KeyboardEvent):void {  if(event.keyCode == Keyboard.SPACE )  {  mx.controls.Alert.show("Space 键触发","提示",1,this);  }  else if(event.keyCode == Keyboard.ENTER)  {  mx.controls.Alert.show("Enter 键触发","提示",1,this);  }  }  } 


图 8.扩展的 AccImage 示例
图 8: 扩展的 AccImage 示例 

与 Accessibility 全键盘支持相关的的变量

1 . flash.display.InteractiveObject.tabEnabled:指定此对象是否遵循 Tab 键顺序,大部分实现了 IFocusManagerComponent 的组件的默认值都是 true.

2 . flash.display.InteractiveObject.tabIndex: 指定对象按 Tab 键顺序排列,Flex 会默认的为每个显示在界面的组件分配一个顺序值,一般按照从上倒下,从左到右的顺序分配,如果用户

3 . flash.display.DisplayObjectContainer.tabChildren:确定对象的子项是否支持 Tab 键,容器组件需要设置该变量使其孩子能够接受 Tab 键。。

结束语

本文对 Flex 的 Accessibility 全键盘支持做了一个简要的介绍 , 并结合例子对如何扩展 Flex 的全键盘支持做了总结,全键盘支持作为 Web 应用的一个重要特征,值得研究的地方还很多,而且随着 Flex 的不断改进,对全键盘的支持也会越来越全面。希望通过本文的示例项目,可以帮助您开发更好的支持全键盘操作的 Flex 应用。

0 0
原创粉丝点击