21.3.3. 在捕获阶段和冒泡阶段都注册父级侦听器
为了在捕获阶段和冒泡阶段(在目标接收到事件通知的前后)都调用侦听器函数,我们必须2次注册侦听器— 一次把useCapture 设置为true ,一次设置为false. 比如,在我们的TextField 例子中,假设我们希望为theSprite注册TextEvent.TEXT_INPUT事件的textInputListener() 侦听器,并且我们希望textInputListener() 可以在捕获阶段和冒泡阶段都被调用,我们这样:
theSprite.addEventListener(TextEvent.TEXT_INPUT, textInputListener, true)
theSprite.addEventListener(TextEvent.TEXT_INPUT, textInputListener, false)
NOTE
如果一个父级侦听器想要在捕获和冒泡2阶段都被调用,必须要定义2次.
21.3.4. 在目标阶段注册目标侦听器
要在目标阶段注册一个目标侦听器,我们设置addEventListener()'s useCapture 属性为false:
theEventTarget.addEventListener(theEvent, theListener, false)
因此,在我们先前的TextField 一例中,要为theTextField注册TextEvent.TEXT_INPUT 事件注册textInputListener() 侦听器。我们这样:
theTextField.addEventListener(TextEvent.TEXT_INPUT,textInputListener,false)
或者不设置useCapture:
theTextField.addEventListener(TextEvent.TEXT_INPUT,textInputListener)
上面的代码使得textInputListener() 会在AS发出一个以theTextField 为目标的TextEvent.TEXT_INPUT事件的时候调用。它会在舞台实例和theSprite 对象在捕获阶段收到事件通知之后被执行,但是在舞台实例和theSprite 对象接收到冒泡阶段的通知之前。
21.3.5. useCapture 属性的双重用途
在前两章中展示的, addEventListener()'s useCapture 属性在下面2种情况下被设置为false:
• 当注册一个在冒泡阶段被调用的父级侦听器
• 当注册一个在目标阶段被调用的目标侦听器
所以,当一个以useCapture 设置为 false 的情况下注册的侦听器,这个侦听器就会在这个事件被发送时调用,并且下面2条都是正确的:
• 事件的目标是这个被注册了侦听器的对象(这时,这个侦听器是在目标阶段被执行).
• 事件的目标是这个被注册了侦听器的对象的子级对象(这时,这个侦听器是在冒泡阶段被执行,在子级对象接收到这个事件之后)
比如,下面的代码为Stage实例注册了一个clickListener() 侦听器以侦听MouseEvent.CLICK事件
someDisplayObject.stage.addEventListener(MouseEvent.CLICK,clickListener,false);
因为useCapture 是 false, clickListener() 会在下面2中状况下都被调用:
• 当用户点击可显示区域 (这种情况下Flash 运行时发出了目标为舞台实例的事件).
• 当用户点击任何屏幕上的显示对象 (这种情况下Flash 运行时发出了一个目标为被点击对象的事件,显示对象总是Stage 实例的子级).
虽然clickListener() 仅仅注册在了一个对象上(Stage 实例),但是在运行时,不管发出的某事件的目标是该对象还是他的子级对象,clickListener() 总是会执行!因此,在某些情况下,一个侦听器函数必须包含用于忽略它不感兴趣的事件的代码。我们将在"DistinguishingEvents Targeted at an Object from Events Targeted at That Object'sDescendants."中学习这种代码。
21.3.6. 移除事件侦听器
当在处于显示层级的对象上注销一个事件侦听器,我们必须指明一开始这个侦听器是注册为捕获阶段调用的,还是目标或者冒泡阶段。要实现这个,我们使用removeEventListener()'的第三个属性,useCapture, 它和 addEventListener()的 useCapture 属性是镜像关系。如果这个侦听器一开始时注册为捕获阶段被调用的(注册的时候useCapture是true), 那我们在注销的时候就要把removeEventListener() 的useCapture 属性也设置为true. 如果侦听器是被注册为在目标或者冒泡阶段被调用的(注册的时候useCapture为false),我们必须在注销的时候把
removeEventListener()'的 useCapture 也设置为false.
NOTE
当注销侦听器的时候,经常设置removeEventListener()的useCapture属性符合添加侦听器的时候一样。
比如,下面代码我们把addEventListener()'的useCapture设置为true,在捕获阶段为someDisplayObject 注册一个clickListener()
someDisplayObject.addEventListener(MouseEvent.CLICK,clickListener, true);
对应地,当要从someDisplayObject注销掉clickListener() 侦听器,我们必须把removeEventListener()'的useCapture属性也设置为true
someDisplayObject.removeEventListener(MouseEvent.CLICK, clickListener,true);
当要注销一个在捕获和冒泡2个阶段都注册了的侦听器,也同样要调用2次removeEventListener()
注册:
someDisplayObject.stage.addEventListener(MouseEvent.CLICK, clickListener, true);
someDisplayObject.stage.addEventListener(MouseEvent.CLICK,clickListener, false);
注销:
someDisplayObject.stage.removeEventListener(MouseEvent.CLICK,clickListener, true);
someDisplayObject.stage.removeEventListener(MouseEvent.CLICK,clickListener, false);
NOTE
每一个以addEventListener() 形式注册的事件都要被单独对待,需要用属于它自己的removeEventListener() 函数来注销
现在我们熟悉了事件流,让我们来看看一个例子展示了他如何帮助一个现实的应用程序集中化代码
21.4. 使用事件流来集中代码
如果你在等待一个被订满房间的旅馆,让旅馆管理人在有空房的时候通知你比你一个一个房间去问他们什么时候离开要好得多。同样地,当处理一个事件的发送时,把侦听器注册给一个父级对象比注册给它的每个子级好得多。比如,假设我们建造了一个简单的复选框,包含了下面2个类:
• CheckBox, 一个 Sprite 的子类,作为一个包含了整个复选框的容器
• CheckBoxIcon, 一个 Sprite 的子类,代表了复选框的图标
在运行时,每一个CheckBox 实例都创造2个子对象: 一个复选框图标的CheckBoxIcon实例,一个复选框文本标签的TextField 实例 。以备参考,我们叫这个主CheckBox 实例为 container,它的2个子类为icon 和 label.
Figure 21-3 shows our checkbox control.
我们想要我们的复选框容易使用,所以我们设计成,用户点击图标或者文本标签的时候开关ON或者OFF 。因此,在我们的实现中,以icon 和 label 为目标的鼠标点击事件我们都必须探测。要实现这个,我们可以为这几个对象单独注册鼠标点击侦听器。但是,注册2个侦听器的会增加程序调试时间并且重复注册增大了代码出错的可能性。为了避免代码重复,我们可以为container.注册一个侦听器。因为container 是con 和label 可视化父级,不管它们什么时候发出的点击事件都会通知给container'。当 container'的鼠标点击侦听器运行,我们就知道icon或者label发生了鼠标点击事件,作为响应,我们可以开关这个复选框ON或者OFF。
Example 21-2 shows the code for our example checkbox, with event-handling sections in
bold.
Example 21-2. Handling aCheckbox's events hierarchically
现在我们把AS的层级事件发送系统的基础都过了一遍。但是还有更多的话题,让我们继续。