Flash V2组件基础件开发:ActionRegistry消息分派器。

类别:编程语言 点击:0 评论:0 推荐:

需求


  在一个小型客户端项目中,为了美化界面,除了网络连接、数据逻辑处理部分用VC实现以外,显示部分都使用Flash,速度要求不高,用Flash的好处是可以做出一些效果,而且美工可以只负责美工,程序员编写一些简单的Flash Action配合一下即可。

  这是一个简单的Flash应用,半天时间就已经完成了。进入测试,通过所有要求。

  随之而来的是版本升级问题,最初的简单版本经过3次升级,加上美工参与代码维护以后,基本上原来的代码已经散乱不堪,BUG开始涌现,维护问题突显。

  原本打算抽调一名程序员协助美工完成脚本代码维护,不过最终没安排过来。美工自己显然已经不知道自己把代码改成什么样了,也找不出BUG出在什么地方。

  程序工作的流程很简,从网络接收的数据经过简单分析处理,最终被分解为简单的“action”(实际上我们把它称为消息),传递给Flash,由它解释成显示数据。初期因为每个action只驱动一个显示动作,所以工作得很好,代码也很整齐。后来升级以后,美工介入编码,由于没有系统结构意识,在“一个Action驱动多个显示动作”时采用了愚蠢的“补贴”方式,致使每个处理action的函数都臃肿不堪,最终导致了蜘蛛网似的代码结构,全局变量(Flash中的绝对路径)到处使用,一个小的改动都可能使显示全部错误。

  我开始思考解决办法。

  美工的编码方式很简单但也不能说错误,他们有一套自己的编码方式,在短时间内强行去改变他们是没有意义的,让程序员参与编码不但浪费人力,反而让他们双方都觉得进度缓慢,唯一可行的方式是顺着美工的方向走。大致看了一下他们的代码,从网上抄过来的特效代码比较多,只是结构稍差,但并非不可行。

  我首先要求美工:每一个小的显示部件单独开发,在一个独立的文件中做好、测试好,并且要保证在各个路径层次上执行正常(减少他们使用全局路径,最终确定了只使用一个全局路径以方便维护)。他们做这些没有任何问题,完成速度也很快。

  然后我把这些小部分要用到的action分析了一下,基本上还是我们原来那些action,现在的要求是把这些action送达这些小部件中以进行处理。

  问题明确了:每个action由数个不同的小部件侦听,只要写好这个类,并由这些部件注册侦听器即可。

功能描述

  因为是在VC中发送这些action,为了在Flash脚本中处理简单一些,我们在VC中驱动Flash显示的代码统一形式为:

  m_wndFlash.SetVariable (“_root.action_processor.action“, “action_type:action_body“);

  第一个参数是action处理器组件的一个setter函数,后面的字符串就是传递给它处理的。因为在VC中无法直接调用组件的函数,只能用这种方式间接调用。根据这个约定,我在原来的基础上编写了ActionRegistry类,为了美工调用方便,做成了一个可视组件,实际上并不负责显示,只是方便他把组件拖到场景中并取个名字。

实现

  实现过程并没有太复杂,所以我直接把代码发上来了。

class biz.blueskytech.ActionRegistry
{
    private var _action_listenerlist_map : Object;

    function ActionRegistry ()
    {
        _action_listenerlist_map = {};
    }

    function registryListener (type:String, obj:Object) : Void
    {
        if (_action_listenerlist_map[type] == undefined)
            _action_listenerlist_map[type] = new Array ();

        _action_listenerlist_map[type].push (obj);
    }

    function removeListener (type:String, obj:Object) : Void
    {
        var arr = _action_listenerlist_map[type];
        if (arr != undefined)
        {
            for (var i=0; i<arr.length; i++)
                if (arr[i] == obj)
                    arr.splice (i, 1);
        }
    }

    function dispatchAction (type:String, args:Array) : Void
    {
        var arr = _action_listenerlist_map[type];
        if (arr != undefined)
        {
            for (var i=0; i<arr.length; i++)
            {
                var obj = arr[i];
                var funcName = "on" + type.charAt(0).toUpperCase () + type.substr (1);
                if (typeof (obj[funcName]) == "function")
                    obj[funcName].apply (obj, args);
                else
                    trace ("the listener not implement "+type+" handle");
            }
        }
        else
        {
            trace ("action "+type+" no listeners");
        }
    }

    function set action (str:String) : Void
    {
        if (str != undefined)
        {
            var delimiter_pos = str.indexOf (":");
            if (delimiter_pos > 0)
            {
                var action_type = str.substr (0, delimiter_pos);
                var action_body = str.substr (delimiter_pos+1);
                if (action_type.length > 0)
                {
                    //trace ("call listeners handler of action {"+action_type+"}");
                    var args = parse_body (action_type, action_body);
                    dispatchAction (action_type, args);
                }
                else
                {
                    trace ("invalid action type or args");
                }
            }
            else
            {
                trace ("invalid action format");
            }
        }
        else
        {
            trace ("invalid action call");
        }
    }

    function parse_body (type:String, body:String) : Array
    {
        var funcName = type+"Handler";
        if (typeof (this[funcName]) == "function")
            return this[funcName].apply (this, [body]);
        else
        {
            trace ("action handler not found");
            return [];
        }
    }
}


至于怎么生成组件,不是本文的重点,只要用一个空的MovieClip和它链接,并定义组件即可,是否导出编译组件并不重要。

调用实例

  新建一个Flash文件,在里面加入一个ActionRegistry组件实例,命名为action_registry。

  在第一桢加入下面的代码:

action_registry.testHandler = function (t:String):Array
{
 return [5,4];
}

  以上代码是为action_registry加入test消息的解析函数,用它来把消息解析成参数,供侦听回调函数使用。下面代码注册了2个侦听器,它们有不同的处理函数:

var obj = new Object ();
obj.onTest = function (t1:Number, t2:Number)
{
 trace ("T1: "+t1);
 trace ("T2: "+t2);
}
action_registry.registryListener ("test", obj);

var obj1 = new Object ();
obj.onTest = function (t1:Number, t2:Number)
{
 trace (“Result: “+(t1+t2));
}
action_registry.registryListener ("test", obj1);

  初始化就完成了。我们现在加入一个按钮,给它的click(或是release)事件编写代码:

on (click)
{
 _root.action_registry.action = "test:0";
}

  因为test消息的参数我们并没有用到,实际上从引号后面就可以空差。现在测试影片吧,你应该可以看到2个消息处理函数都执行了,这也达到了本文的要求。

  如果你知道怎么在VC中给影片中的变量赋值,你自然会明白这个组件给你带来了什么——一个多路消息分派器。

  如果你只是在Flash中使用此分派器,则无需先将消息格式化为字符串,可以直接调用:

_root.action_registry.dispatchAction (”test”, [5,4]);

  这样做也提高了效率。

测试总结

  现在我们只要在要侦听这个消息的地方去注册侦听器即可,对代码层次、调用顺序没有任何影响,很好地达到了要求。

  本文也使用了一些Flash V2组件脚本的新语法。

本文地址:http://com.8s8s.com/it/it27030.htm