我在GitHub上新开了一个项目:https://github.com/hammerc/hammerc-study-Stage3D
山寨的Starling版本我取名叫做Scorpio2D,以后的笔记中只会贴出核心的代码,完整的代码和示例大家可以去GitHub中查看。
同时为了避免Starling中和原生API重名的问题,所有可能导致重名的类名我都会在后面加上2D,比如Starling中的EventDispatcher被我山寨过来后就重命名为EventDispatcher2D。
另外由于高版本的Starling中采用了很多的优化技术来提高运行效率,但这些技术会使阅读难度增大,所以我参照的是比较老的版本。
=============================================================
那么从哪里开始看呢,既然显示列表的基类是EventDispatcher,那就从事件开始吧!
“Starling为啥要自己重新写一个EventDispatcher,这不是脑残么?难道他认为自己写的会比原生的效率更高?”
额,其实不是这样,Staling是在模仿AS3原生显示列表,那么很重要的一环就是要实现事件的冒泡,原生的事件冒泡是在原生显示列表中实现的,所以想要在Starling的显示列表中也实现事件的冒泡功能就需要自己另做一套事件体系了。
事件其实就是实现观察者模式+责任链模式,而作为冒泡功能的实现,是非常值得一看源码的,主要的逻辑是在发送事件时,设置target和currentTarget的值,另一个是实现终止冒泡的逻辑,以及向父层调用dispatchEvnet方法实现冒泡。
下面列出核心代码以供参考:
1 public function dispatchEvent(event:Event2D):void 2 { 3 var listeners:Vector.<Function> = _eventListeners ? _eventListeners[event.type] : null; 4 //如果当前没有任何侦听同时事件不需要冒泡则可以停止执行 5 if(listeners == null && !event.bubbles) 6 { 7 return; 8 } 9 //记录下当前的事件目标对象 10 var previousTarget:EventDispatcher2D = event.target; 11 //如果没有事件目标对象或当前目标对象存在则设置当前对象为事件目标对象, 注意最后会对目标对象进行还原 12 if(previousTarget == null || event.currentTarget != null) 13 { 14 event.setTarget(this); 15 } 16 //抛出侦听的事件 17 var stopImmediatePropagation:Boolean = false; 18 if(listeners != null && listeners.length != 0) 19 { 20 //设置当前目标对象 21 event.setCurrentTarget(this); 22 //抛出事件 23 for each(var listener:Function in listeners) 24 { 25 listener(event); 26 //如果事件被立即终止则跳出循环 27 if(event.stopsImmediatePropagation) 28 { 29 stopImmediatePropagation = true; 30 break; 31 } 32 } 33 } 34 //判断事件是否可向上进行冒泡 35 if(!stopImmediatePropagation && event.bubbles && !event.stopsPropagation && this is DisplayObject2D) 36 { 37 var targetDisplayObject:DisplayObject2D = this as DisplayObject2D; 38 if(targetDisplayObject.parent != null) 39 { 40 //设置当前目标对象为空 41 event.setCurrentTarget(null); 42 //事件冒泡 43 targetDisplayObject.parent.dispatchEvent(event); 44 } 45 } 46 //还原为第一个抛出的事件目标对象 47 if(previousTarget != null) 48 { 49 event.setTarget(previousTarget); 50 } 51 }
完整代码: