`
mysh
  • 浏览: 28951 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

zk 与 flash 交互 - FlashContainer

阅读更多

前阵子搞 zk 与 flash 交互,搞得头破血流,zk 本身带有 flash 标签,但只能当播放器用,没法进行比较复杂的 flash 控制,如调用 flash 的内部方法。flash 也没法直接通过前端与 zk 组件交互,于是自己搞了一个中间件,用于 zk 与 flash 的交互,使得 zk 可以直接调用 flash 的内部方法,flash 可以直接向 zk 发送数据,同时加入了一点同步控制,因为 flash 虚拟机是异步单线程的,且 zk 的每个 flash 调用都是独立的,在实际执行过程中无法保证调用顺序。

需要 zk 5.0 以上版本,flash 支持 flex sdk3 和 sdk4 编译程序。

 

demo.zul(zk 演示页面) 

<?page title="demoZK" contentType="text/html;charset=UTF-8"?>
<zk>
	<window title="demoZK" border="normal" width="100%" height="100%"
		apply="mysh.Demo">
		<div id="flashDiv" />
	</window>
</zk>

 

Demo.java(zk 端演示组件)

package mysh;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.GenericComposer;
import org.zkoss.zul.Div;
import org.zkoss.zul.Messagebox;

import mysh.FlashContainer;

/**
 * Demo.
 * 
 * @version Revision 1.0.0
 */
public class Demo extends GenericComposer {
    
    /*
     * @see org.zkoss.zk.ui.ext.AfterCompose#afterCompose()
     */
    @Override
    public void doAfterCompose(Component comp) {
        Div flashDiv = (Div) comp.getFellow("flashDiv");
        flashDiv.addEventListener(FlashContainer.ON_CONTAINER_INFO,
            new EventListener() {
                public void onEvent(Event event) throws Exception {
                    Messagebox.show("receive from flash : " + event.getData(),
                        "zk msg", Messagebox.OK, Messagebox.INFORMATION);
                }
            });
        
        FlashContainer flashContainer = FlashContainer.genFlashContainer(
            flashDiv, "demoFlash.swf");
        
        flashContainer.registObserver(flashDiv);
        
        flashContainer.callFlash("func1", "param");
    }
    
}

 

 

demoFlash. mxml(flash 端演示)

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   applicationComplete="init()"
			   xmlns:mx="library://ns.adobe.com/flex/mx"
			   width="594"
			   height="416">

	<fx:Script>
		<![CDATA[
			import mysh.FlashContainer;

			import mx.controls.Alert;

			private var flashContainer:FlashContainer;

			private function init():void
			{
				this.flashContainer=new FlashContainer(this);
				this.flashContainer.registFunc("func1", this.function1);
			}

			private function function1(param:String):void
			{
				Alert.show("msg from zk : " + param);
			}

			protected function send2ZK_clickHandler(event:MouseEvent):void
			{
				this.flashContainer.sendEvent2ZK(this.input.text);
			}
		]]>
	</fx:Script>
	<s:Button id="send2ZK"
			  x="46"
			  y="101"
			  label="发送到 zk"
			  click="send2ZK_clickHandler(event)"/>
	<s:TextInput id="input"
				 x="45"
				 y="56"/>

</s:Application>

 

 

 

flashContainer.as(flash 端组件。D.eval 下载地址

package mysh
{ import flash.external.ExternalInterface; import mx.controls.Alert; import mx.core.Application; import r1.deval.D; /** * flash 容器 * * @version 20110117 */ public class FlashContainer { /** * 容器持有者 */ private var holder:Object; /** * 发送事件调用的JS方法 */ private var eventSendFunc:String; /** * 所有注册方法 */ private var funcs:Object=new Object(); /** * 执行顺序 */ private var order:Number=0; public function FlashContainer(holder:Object) { this.holder=holder; // sdk3 : this.eventSendFunc=Application.application.parameters.eventFunc; // sdk4 : // this.eventSendFunc=FlexGlobals.topLevelApplication.parameters.eventFunc; ExternalInterface.addCallback("jsCall", jsCall); } /** * 外部js调用的方法 */ private function jsCall(jsonStr:String):void { // Alert.show(jsonStr); var jsonObj:Object=D.eval(jsonStr); var funcArray:Array=jsonObj.funcs as Array; var order:Number=jsonObj.order as Number; // 检查执行顺序,若 小于0,则执行;若 大于0,则只有 大于 this.order 才执行 if (order > 0) { if (order > this.order) { this.order=order; } else { return; } } // Alert.show(order+","+this.order); for each (var pack:Object in funcArray) { var func:String=pack.func; var args:Array=pack.args; var treatedArgs:Array=new Array(); for (var i:int=0; i < args.length; i++) { treatedArgs[i]=(args[i] as String).replace(/&quot;/g, "\"").replace(/&apos;/g, "'"); } if (this.funcs[func] == null) { Alert.show("FlashContainer 找不到方法:" + func, "内部错误"); return; } (this.funcs[func] as Function).apply(this.holder, treatedArgs); } } /** * 注册方法名 * @param func 方法名 * @param method 方法实体 */ public function registFunc(func:String, method:Function):void { if (func == null || func == "" || method == null) { throw new Error("方法名或方法不能为空"); } this.funcs[func]=method; } /** * 发送事件到 zk * @param value 值 */ public function sendEvent2ZK(value:String):void { // Alert.show(this.eventSendFunc); ExternalInterface.call(this.eventSendFunc, value); } } }
 

 

 

flashContainer.zul(zk 页面)

<?page title="" contentType="text/html;charset=UTF-8"?>
<zk>
	<window title="" border="normal"
		use="FlashContainer">
	</window>
</zk>
 

 

FlashContainer.java(zk端组件。swfobject.js 下载地址。随着功能的增加,我觉得叫 FlashManager 比较合适)

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Div;
import org.zkoss.zul.Script;
import org.zkoss.zul.Window;

/**
 * flash容器.
 * 非线程安全,需要外部同步.
 * 
 * @version 20110117
 */
public final class FlashContainer extends Window implements AfterCompose {
    
    /**
     * serialVersionUID.
     */
    private static final long   serialVersionUID = 2564493195082621543L;
    
    /**
     * zul 页面地址.
     */
    private static final String ZUL_PATH         = "flashContainer.zul";
    
    /**
     * 方法调用定义.
     * 
     * @version Revision 1.0.0
     */
    public static final class FlashFuncInvoke {
        
        /**
         * 方法名.
         */
        private String   func;
        
        /**
         * 方法参数.
         */
        private String[] args;
        
        /**
         * 构造器.
         * 
         * @param tFunc
         *            方法名
         * @param tArgs
         *            方法参数
         */
        public FlashFuncInvoke(String tFunc, String... tArgs) {
            if (tFunc == null || tFunc.length() == 0 || tArgs == null) {
                throw new RuntimeException("非法参数");
            }
            this.func = tFunc;
            this.args = tArgs;
        }
        
        /**
         * 返回一个“单元素列表”.
         * 
         * @param tFunc
         *            方法名
         * @param tArgs
         *            方法参数
         * @return 单元素列表
         */
        public static List<FlashFuncInvoke> genSingleEleList(String tFunc,
            String... tArgs) {
            List<FlashFuncInvoke> funcList = new ArrayList<FlashContainer.FlashFuncInvoke>(
                1);
            funcList.add(new FlashFuncInvoke(tFunc, tArgs));
            return Collections.unmodifiableList(funcList);
        }
        
    }
    
    /**
     * 用于浏览器端向服务端发送事件.
     */
    private static final String ON_CIENT_INFO     = "onClientInfo";
    
    /**
     * flash 容器事件的名称,用于取得此容器发送的事件(从客户端取得的事件).
     */
    public static final String  ON_CONTAINER_INFO = "onContainerInfo";
    
    /**
     * 执行顺序号限制(2^52-1).
     */
    private static final long   ORDER_LIMIT       = 0xFFFFFFFFFFFFFL;
    
    /**
     * 执行顺序号生成器.
     */
    private final AtomicLong    orderGen          = new AtomicLong(1L);
    
    /**
     * 事件监听组件.
     */
    private Div                 eventListener     = new Div();
    
    /**
     * flash 容器.
     */
    private Div                 flash             = new Div();
    
    /**
     * 浏览器端脚本容器.
     */
    private Script              script            = new Script();
    
    /**
     * 浏览器 js 方法命名空间.
     */
    private String              ns;
    
    /**
     * flash动作观察者.
     */
    private Queue<Component>    observers         = new LinkedList<Component>();
    
    /**
     * 生成 FlashContainer(用于 flash 9 版本).
     * 
     * @param parent
     *            flash容器的父组件
     * @param swfSrc
     *            页面 js 可访问的 swf 路径
     * @return FlashContainer
     */
    public static FlashContainer genFlashContainer(Component parent,
        String swfSrc) {
        Map<String, String> args = new HashMap<String, String>();
        args.put("flashSrc", swfSrc);
        args.put("flashVer", "9");
        return (FlashContainer) Executions.createComponents(
            FlashContainer.ZUL_PATH, parent, args);
    }
    
    /**
     * 生成 FlashContainer(用于 flash 10 版本).
     * 
     * @param parent
     *            flash容器的父组件
     * @param swfSrc
     *            页面 js 可访问的 swf 路径
     * @return FlashContainer
     */
    public static FlashContainer genFlashContainerVer10(Component parent,
        String swfSrc) {
        Map<String, String> args = new HashMap<String, String>();
        args.put("flashSrc", swfSrc);
        args.put("flashVer", "10");
        return (FlashContainer) Executions.createComponents(
            FlashContainer.ZUL_PATH, parent, args);
    }
    
    /**
     * 生成 FlashContainer(用于 flash 10 版本).
     * 
     * @param parent
     *            flash容器的父组件
     * @param swfSrc
     *            页面 js 可访问的 swf 路径
     * @param flashVars
     *            传递给 flash 的参数
     * @return FlashContainer
     */
    public static FlashContainer genFlashContainerVer10(Component parent,
        String swfSrc, Map<String, String> flashVars) {
        Map<String, Object> args = new HashMap<String, Object>();
        args.put("flashSrc", swfSrc);
        args.put("flashVer", "10");
        args.put("flashVars", flashVars);
        return (FlashContainer) Executions.createComponents(
            FlashContainer.ZUL_PATH, parent, args);
    }
    
    /**
     * 构造器.
     */
    public FlashContainer() {
        this.ns = this.getUuid();
        this.appendChild(this.eventListener);
        this.appendChild(this.flash);
        this.appendChild(this.script);
        
        this.eventListener.addEventListener(FlashContainer.ON_CIENT_INFO,
            new EventListener() {
                @Override
                public void onEvent(Event event) throws Exception {
                    // flash 控件不支持,打开下载页面
                    if (event.getData().equals("flashNotSupported")) {
                        alert("flashNotSupported");
                    }
                    
                    for (Component comp : FlashContainer.this.observers) {
                        
                        Event e = new Event(FlashContainer.ON_CONTAINER_INFO,
                            comp, event.getData());
                        Events.sendEvent(e);
                    }
                }
            });
        
        // 发送事件脚本
        String scriptStr = "function " + this.ns
            + "_flashSend(v){var e = new zk.Event();e.$init(zk.Widget.$('"
            + this.eventListener.getUuid() + "'), '"
            + FlashContainer.ON_CIENT_INFO + "', v);zAu.send(e);}";
        
        // 调用 flash 方法
        scriptStr += "function " + this.ns + "_callFlash(jsonStr){"
            + "var flash=null;try{flash = document.getElementById(\""
            + this.flash.getUuid()
            + "\").jsCall(jsonStr);}catch(err){setTimeout(\"" + this.ns
            + "_callFlash('\"+jsonStr+\"')\",150);}}";
        
        this.script.setContent(scriptStr);
        
        // swfobject
        Script swfobject = new Script();
        swfobject.setSrc("/common/js/swfobject.js");
        this.appendChild(swfobject);
        
    }
    
    /*
     * @see org.zkoss.zk.ui.ext.AfterCompose#afterCompose()
     */
    @SuppressWarnings("unchecked")
    @Override
    public void afterCompose() {
        // javaScript 可识别的 swf 地址
        String flashSrc = (String) Executions.getCurrent().getArg()
            .get("flashSrc");
        
        // flash 播放需要的播放器版本号
        String flashVer = (String) Executions.getCurrent().getArg()
            .get("flashVer");
        
        // 传递给 flash 的参数
        Map<String, String> flashVars = (Map<String, String>) Executions
            .getCurrent().getArg().get("flashVars");
        
        String detectFlashVer = "9,0,124";
        String swfobjectFlashVer = "9.0.124";
        if (flashVer != null && flashVer.equals("10")) {
            detectFlashVer = "10,0,0";
            swfobjectFlashVer = "10.0.0";
        }
        
        // 检查 flash 控件版本,若不支持给定的 flash 版本,弹出页面
        Clients.evalJavaScript("if(!DetectFlashVer(" + detectFlashVer + ")){"
            + this.ns + "_flashSend('flashNotSupported');}");
        
        // 初始化flash,传入 js发送事件方法名
        Clients.evalJavaScript("function " + this.ns
            + "_initSWF(){try{swfobject.embedSWF('" + flashSrc + "', '"
            + this.flash.getUuid() + "', '100%', '100%', '" + swfobjectFlashVer
            + "', '', {eventFunc:'" + this.ns + "_flashSend'"
            + this.genFlashVars(flashVars)
            + "}, {wmode:'opaque'}, {});}catch(e){setTimeout('" + this.ns
            + "_initSWF();',100);}}" + this.ns + "_initSWF();");
        
    }
    
    /**
     * 生成 flashvars 参数字串.<br/>
     * 若参数为 null,则生成的参数为 ''<br/>
     * 参数名为空的参数将被忽略;含有引号的参数为非法参数,将抛运行时异常<br/>
     * 格式:,a:'a',b:'b'
     * 
     * @param flashVars
     *            参数 map
     * @return 参数字串
     */
    private String genFlashVars(Map<String, String> flashVars) {
        if (flashVars == null) {
            return "";
        }
        
        StringBuilder vars = new StringBuilder("");
        
        String tempKey, tempValue;
        for (Map.Entry<String, String> entry : flashVars.entrySet()) {
            tempKey = entry.getKey();
            if (tempKey == null || tempKey.length() == 0) {
                continue;
            }
            tempValue = entry.getValue();
            if (tempValue == null || tempValue.contains("\'")
                || tempValue.contains("\"")) {
                throw new RuntimeException("非法参数:" + tempValue);
            }
            
            vars.append(",");
            vars.append(tempKey);
            vars.append(":'");
            vars.append(tempValue);
            vars.append("'");
        }
        return vars.toString();
    }
    
    /**
     * 只执行最新的调用(以调用顺序为准),忽略旧的调用(执行顺序值小于客户端当前执行顺序值的调用). <br/>
     * 此方法用于解决对 flash 端的多个调用不能保证顺序的问题. <br/>
     * 若不希望任何调用被忽略,请用 callFlashFuncList 方法. <br/>
     * 调用flash方法.
     * 
     * @param func
     *            flash 方法名
     * @param args
     *            可变参数(参数中不允许含有 &quot; 或 &apos;,有则抛异常)
     */
    public void callFlashLatest(String func, String... args) {
        this.innerCallFlashFuncList(this.orderGen.incrementAndGet(),
            FlashFuncInvoke.genSingleEleList(func, args));
    }
    
    /**
     * 调用flash方法. 这个方法的多个调用在 flash 客户端不能保证调用顺序. <br/>
     * 要确保调用顺序,尝试 callFlashLatest 或 callFlashFuncList 方法. <br/>
     * 
     * @param func
     *            flash 方法名
     * @param args
     *            可变参数(参数中不允许含有 &quot; 或 &apos;,有则抛异常)
     */
    public void callFlash(String func, String... args) {
        this.innerCallFlashFuncList(-1L,
            FlashFuncInvoke.genSingleEleList(func, args));
    }
    
    /**
     * 只执行最新的调用(以调用顺序为准),忽略旧的调用(执行顺序值小于客户端当前执行顺序值的调用). <br/>
     * 此方法用于解决对 flash 端的多个调用不能保证顺序的问题. <br/>
     * 按顺序执行给定的 flash 方法. <br/>
     * 方法参数中不允许含有 &quot; 或 &apos;,有则抛异常
     * 
     * @param list
     *            方法定义列表
     */
    public void callFlashFuncListLatest(List<FlashFuncInvoke> list) {
        this.innerCallFlashFuncList(this.orderGen.incrementAndGet(), list);
    }
    
    /**
     * 按顺序执行给定的 flash 方法. 这个方法的多个调用在 flash 客户端不能保证调用顺序. <br/>
     * 要确保调用顺序,尝试 callFlashFuncListLatest 方法. <br/>
     * 方法参数中不允许含有 &quot; 或 &apos;,有则抛异常
     * 
     * @param list
     *            方法定义列表
     */
    public void callFlashFuncList(List<FlashFuncInvoke> list) {
        this.innerCallFlashFuncList(-1L, list);
    }
    
    /**
     * 向 flash 客户端发送执行命令(方法调用).
     * 
     * @param order
     *            给定一个执行顺序值(一个从小到大的正值,不能大于 2^52-1=4503599627370495L. <br/>
     *            若为 -1L,则 flash 端执行此调用;若为一个正值,则 flash
     *            端将忽略旧的调用(执行顺序值小于客户端当前执行顺序值的调用). <br/>
     *            给定执行顺序值用于解决这样一个问题:flash 客户端的多个调用的执行不能同步,但给定执行顺序将导致旧的调用被忽略
     * @param list
     *            方法定义列表
     */
    private void innerCallFlashFuncList(long order, List<FlashFuncInvoke> list) {
        if (order < -1L || order > FlashContainer.ORDER_LIMIT) {
            throw new RuntimeException("执行顺序值超出 [-1, 2^52-1]");
        }
        
        if (list == null || list.size() < 1) {
            return;
        }
        
        StringBuilder str = new StringBuilder("var j={order:");
        str.append(order);
        str.append(",funcs:[");
        for (FlashFuncInvoke funcInvoke : list) {
            str.append(this.genJsonString(funcInvoke.func, funcInvoke.args));
            str.append(",");
        }
        if (str.charAt(str.length() - 1) == ',') {
            str.deleteCharAt(str.length() - 1);
        }
        str.append("]}");
        
        Clients.evalJavaScript(this.ns + "_callFlash(\'" + str.toString()
            + "\')");
    }
    
    /**
     * 根据给定的方法名、方法参数生成 json 对象字符串.
     * 
     * @param func
     *            方法名
     * @param args
     *            方法参数(参数中不允许含有 &quot; 或 &apos;,有则抛异常)
     * @return json 对象字符串
     */
    private String genJsonString(String func, String[] args) {
        String argsStr = "";
        if (args != null && args.length != 0) {
            for (String arg : args) {
                if (arg == null) {
                    throw new RuntimeException("不允许 null 值作为参数");
                }
                if (arg.contains("&quot;") || arg.contains("&apos;")) {
                    throw new RuntimeException("参数中不允许含有 &quot; 或 &apos;");
                }
                argsStr += "\""
                    + arg.replace("\"", "&quot;").replace("'", "&apos;")
                    + "\",";
            }
            argsStr = argsStr.substring(0, argsStr.length() - 1);
        }
        return "{func:\"" + func + "\", args:[" + argsStr + "]}";
    }
    
    /**
     * 注册一个 flash 事件观察者.
     * 该观察者需要注册 ON_CONTAINER_INFO 的事件监听
     * 
     * @param observer
     *            flash 动作观察者
     */
    public void registObserver(Component observer) {
        this.observers.add(observer);
    }
    
}
 

 

0
0
分享到:
评论
2 楼 maofan3000 2013-08-12  
不知道swfobject需要下载什么版本?
1 楼 maofan3000 2013-08-12  
我拿你的例子,怎么也不出效果,您能把你的文件打包下载吗?

相关推荐

Global site tag (gtag.js) - Google Analytics