FlexLite框架解读(五):SystemManager

Posted on August 23, 2013

这段时间一直有童鞋询问框架初始化的问题。其实一句话就能说清楚:实例化一个SystemManager,然后把所有UI都放在它里面。SystemManager负责框架的初始化,它继承自Group,除了继承来的标准容器功能,它还增加了三个特殊职责:1.弹出框等层级管理功能;2.鼠标事件过滤;3.自动跟随舞台改变大小。下面分别描述:

(1)通常层级管理都是采用多个子容器的方式来实现。如下图,SystemManager是根容器,然后分别添加四个Group作为子容器。根据不同的用途向各个子容器添加对象,即可实现分层管理。

SystemManager

但是这么做有个问题,SystemManager仍然可以继续添加子项。而且后添加的子项,一定会在所有的层级之上!这样就留出了破坏框架内部层级管理的漏洞。因此我们需要一个更好的方案,从代码上杜绝这种情况,而不是靠人为约定(因为人为约定通常都不靠谱)。在SystemManager里采用的是虚拟的子容器SystemContainer作为层级管理。SystemContainer本身并不是容器,它只是记录了SystemManager的两个层级索引值。有任何添加或移除子项的行为发生时,这两个索引值都会动态更新。当往SystemContainer里添加子项时,它会把子项添加到SystemManager中,并确保子项添加到记录的那两个索引值之间的层级。

简单说就是我们在SystemManager的子项列表里插上了两个分层的标签,每次通过SystemContainer添加的子项,都插入到这两个标签之间。SystemManager里共插入了四个标签,从上自下每两个标签确定一个SystemContainer,分别是cursorContainer鼠标样式层,toolTipContainer工具提示层,popUpContainer弹出框层,最下面的标签到索引0的位置,就是SystemManager.addElement()等自身方法操作的区间。你可以通过SystemManager.popUpContainer.addElement()添加一个子项试试,然后用SystemManager.addElementAt()添加一个新的子项,无论索引指定为多少,新的子项都在之前通过popUpContainer添加的层级之下。对外使用上跟使用了多个子容器的方式没有区别。区别是你通过SystemManager的任何方法添加子项都不可能会盖住弹出框或工具提示了。

这里需要说明下,通常情况下,你只需操作SystemManager自身的方法来管理子项,而不应该关心或调用popUpContainer等容器,因为他们都有专门的管理器负责添加子项。比如PopUpManager和ToolTipManager,正确的方法是调用对应的管理器来操作要添加到这些层级的子项。至于项目里自定义的特殊层级。你仍然可以使用增加子容器到SystemManager里的方法来扩展。没必要去构造虚拟层级管理,因为SystemManager这么做主要是防止你添加的子项盖住了系统管理器的弹出层。

(2)鼠标事件过滤这点是为了弥补FP原生事件的缺陷,FP抛出的所有的鼠标事件对象默认都是不可取消的,也就是在构造函数里传入了cancelable=false。而在框架内有很多组件是需要调用event.preventDefault()来实现阻止某个操作发生的功能。所以在根容器的鼠标事件捕获阶段加了层过滤,把相关的鼠标事件对象转换为可以取消的再重新抛出。

(3)自动跟随舞台改变大小这个非常好理解。FP原生只不带自动布局功能的。所以这里让桥接用的SystemManager跟随舞台大小而改变,从而通知相关子项层层重新布局。你只需把SystemManager实例化好添加到显示列表。它就会自动监听舞台事件,然后始终保持自己的尺寸跟舞台完全一致。此时SystemManager的x,y,width,height设置都是无效的。当然,也有一些特殊的情况下,你不想跟随舞台。比如要做的游戏是固定场景尺寸的。这时你可以将SystemManager.autoResize设置为false。即可关闭这个功能。甚至,你可以自己实现一个自定义的SystemManager作为根容器(框架内几乎所有的管理器都是可以自定义的),只需实现ISystemManager接口即可。具体实现参考SystemManager的代码。

最后有必要说说,如何让SystemManager和游戏场景结合的问题。记住一点,FlexLite是UI框架,只负责UI部分,不要把它用到游戏场景里。你的游戏场景应该仍然使用传统的显示列表来开发。SystemManager本身是标准的Sprite,所以它可以被任意addChild()到舞台上。但是在SystemManager内部,只允许使用addElement()系列方法来添加子项(在《FlexLite框架解读(三):自定义皮肤》中,我们解释了为什么必须这么做的原因,不明白的可以回头看看)。这里我们就要用到UIComponent来隔离,它是唯一既可以addChild()传统显示对象,又可以被addElement进SystemManager里的类。

所以主要有两种方式。1.直接把SystemManager作为文档类,然后实例化一个UIComponent添加到SystemManager里,再把游戏场景addChild进UIComponent。2.不改变原有的文档类结构。实例化SystemManager然后把它addChild到游戏场景的上面盖住场景即可。UI都在SystemManager里开发。游戏仍然在传统显示列表里开发。

这篇文章不长,因为SystemManager本身也没有很多内容。鉴于问的人比较多,所以集中写一下。希望对大家有所帮助。: )