要看懂/userguide/index.zul是如何实现导航的,这里有一些知识必须能够首先要理解和掌握才行。

一、ZK指令

XML处理指令描述了如何处理ZUML页面。包括page、init、component和taglib等等,这里我们了解最常见的几种处理指令就可以了。

page指令描述了页面的属性,可以将page指令放置在XML文档的任何地方,但是language属性只有当指令位于最高层次时才是有意义的,也就是说,处于根组件的层次。

component指令为某一页面定义新的组件。使用此指令定义的组件,仅对于使用该指令的页面是可见的。为了定义在所有组件中可以使用的组件,可是使用附加语言插件(language addon),即一个XML文件(/WEB-INF/zk.xml),用来定义在Web应用程序中所有页面都可使用的组件。

component指令有两种方式:通过宏和通过类(by-macro and by-class)。其中通过宏来定义组件又分为两种,内联宏和常规宏,它们的区别在于:内联宏的行为就像内联扩展(inline-expansion),若遇到了内联宏,则zk不会创建宏组件,相反,它会内联扩展定义在URI内的组件,换言之就像直接把内联组件的内容插入到目标页面。而对常规宏,zk将会创建一个真实的组件(称为宏组件)来显示常规宏中定义的组件,即宏组件将会作为定义在常规宏内组件的父组件被创建。常规宏允许开发人员提供额外的API并且对组件的用户隐藏实现细节,每个常规宏组件均是一个ID空间所有者,所以不必担心常规宏组件会和其它的组件名字冲突。下面是内联宏的一个例子:

username.zul

<!--MacroDef  username.zul-->
<row>Username<textbox id="${arg.id}" value="${arg.name}"/>
</row>

demo.zul

<?component name="username" inline="true" macro-uri="username.zul"?>
<grid><rows><username id="ua" name="John"/></rows>
</grid>

demo.zul的等价页面

<!--等价页面-->
<grid><rows><row>UserName:<textbox id="ua" value="Jhon"/></row></rows>
</grid>

init指令有两种格式。第一种格式是指定一个类(要实现org.zkoss.zk.ui.util.Initator接口)用于处理具体应用(application-specific)的初始化,一旦指定,在页面被赋值(evaluated)前,此类的实例会被创建,并且其doInit方法会被调用,另外,页面被赋值(evaluated)完毕后,doFinally方法会被调用。当例外发生时,doCatch方法会被调用。因此,init指令并不限于初始化,你可以将其用于清理及错误处理。第二种格式是指定一个zscript文件用于处理具体应用(application-specific)的初始化。

variable-resolver指令,为zscript解释器指定一个变量分解器(variable resolver )用以分解未知变量。被指定的类必须实现 org.zkoss.xel.VariableResolver接口。你可以使用多个variable-resolver指令以指定多个变量分解器。声明靠后的分解器有更高的优先级。下面是一个ZK结合Spring框架的例子,它分解了在Spring框架中声明的Java Beans,这样我们可以直接访问到它们。

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>

别的指令在这里不再赘述。

二、事件监听器(EventListener)

org.zkoss.ui.event.EventListener描述了一个事件监听器,如果一个事件监听器被注册到组件中(Component.addEventListener(String evname,EventListener listener),那么当在这个组件上有一个事件发生时,此监听器将被通知到。如下是此接口的定义,它是相当简单的:

public interface EventListener {public void onEvent(Event event) throws Exception;
}

如果一个事件监听器同时实现了Deferrable接口且isDeferrable()返回true,那么此监听器被认为是一个延时事件监听器,如果一个事件监听器实现了Express接口,那么此监听器在所有其它的监听器被通知之前被通知,甚至在zuml页面中用onXxx属性声明的监听器也要在它的后面被通知到,也就是说此监听器的优先级被提高了。

GenericEventListener是EventListener的默认实现,它的子类可以更加直观地用onXxx的形式表示事件处理方法,而GenericEventListener中onEvent方法的实现会自动地把事件导航到相应的方法(主要是依据名称),另外GenericEventListener还提供了一个非常便利的方法,bindComponent(Component c)以便于把一个目标事件组件绑定到事件监听器上,这样在这个组件上发生的事件就会被事件监听器监听到。

三、Composer(设计者)与ComposerExt

Composer用于初始化组件,而ComposerExt在Composer的基础上增加了更多的控制,需要指出的是实现了ComposerExt接口的组件必须也同时实现Composer,反之不成立。与Composer接口相关的ZK属性是apply。如下是Composer的定义:

public interface Composer {public void doAfterCompose(Component comp) throws Exception;
}

为了说明Composer的用途必须首先了解一下ZK Loader加载组件的顺序:

1、如果Composer也同时实现了ComposerExt,调用ComposerExt的doBeforeCompose(Page p,Component c,ComponentInfo ci)方法。

2、创建组件(调用UiFactory.newComponent(Page p,Component c,ComponentInfo ci))

3、如果Composer也同时实现了ComposerExt,调用ComposerExt的doBeforeComposeChildren(Component comp)。

4、创建组件的所有孩子。

5、在组件的所有孩子(即子组件)被创建完成后,调用doAfterCompose(Component c)

6、如果需要的话发送onCreate事件。

AfterComposer与Composer的区别:前者只能被组件本身实现,而Composer是用于初始化组件(可以实现AfterComposer也可以不实现)的控制器。

GenericComposer是Composer的默认实现,它继承自GenericEventListener,可以看出它同时又是一个事件监听器,comp上产生的事件它都会监听到。

abstract public class GenericComposer extends GenericEventListener implements Composer{public void doAfterCompose(Component comp) throws Exception {//bind this GenericEventListener to the supervised componentbindComponent(comp);}
}

GenericAutowireComposer扩展了GenericComposer,它实际上是定义了若干隐含对象,例如protected Desktop desktop,即组件对应的桌面。这些隐含对象对于组件来说是肯定可以访问的,比如前面定义的桌面对应着Component.getDesktop(),问题是现在需要把这些隐含对象对应到GenericAutowireComposer,使这些隐含对象对于它以及它的子类均是可见的,其实这在GenericAutowireComposer中只用一行代码就做到了:

public void doAfterCompose(Component comp) throws Exception {super.doAfterCompose(comp);//wire variables to reference fields (include implicit objects)Components.wireVariables(comp, this);}

注意:上面代码的注释非常精炼,说明了Components.wireVariables(comp, this)的目的,我个人的理解是(不一定十分准确):只要某一变量对于comp是可见的,并且在GenericAutowireComposer或其子类中有相应名称的字段(或称域、成员变量),那么上述方法调用就会把comp中可见的变量的值赋给GenericAutowireComposer或它的子类的同名字段,当然毫无疑问这肯定是通过反射机制来实现的。

啰里啰唆说了这么多,其实GenericAutowireComposer的职责就是使得对组件可见的隐含对象以及变量能够被GenericAutowireComposer及其子类的同名属性引用到。

注意:1、由于GenericAutowireComposer持有组件的引用,所以单例的Composer不能被多个组件共享。2、应该把GenericAutowireComposer的子类定义在单独的Java文件中,而不是定义在zuml页面的zscript元素中,看来BSHInterceptor的工作原理和Java编译器与虚拟机还是有差别的。

GenericForwardComposer是GenericAutowireComposer的子类,它主要完成一些导航的功能,通过继承此类,我们就可以书写类似onXxx$compId的事件处理方法以监听标识符(id)为compId的组件产生的onXxx事件,也就是说,继承自此类的监听器不仅可以监听它所在的组件上产生的事件,还可以监听它所在的组件的子组件的事件,当然方法的命名需要遵循特定的规则。下面是一个例子:

package cn.asix.zkdemo;import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Label;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Window;
public class MyForwardComposer extends GenericForwardComposer {private Textbox mytextbox;private Window self; //embeded object, the supervised window "mywin"private Page page; //the ZK zuml pageprivate Label mylabel;public void onChange$mytextbox(Event event) {mylabel.setValue("You just entered: "+ mytextbox.getValue());}
}

ZUML页面:

<?page title="GenericForwardComposerDemo" contentType="text/html;charset=UTF-8"?>
<zk><window title="GenericForwardComposerDemo" border="normal" apply="cn.asix.zkdemo.MyForwardComposer"><textbox id="mytextbox"/><label id="mylabel"/></window>
</zk>

onChange$mytextbox可以监听id标识为mytextbox的textbox上产生的onChange事件。

四、关于变量与表达式

就像在JSP中一样,可以在zuml页面的任何部分使用EL表达式,但除了属性的名字、元素以及处理指令。语法:${exp}。

EL表达式中的变量对于当前的组件必须是可见的,即Component.containsVariable(String name,boolean local)返回true。例如:

package cn.asix.zkdemo;import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.util.ComposerExt;
public class SimpleComposer implements Composer,ComposerExt {public void doAfterCompose(Component comp) throws Exception {}public ComponentInfo doBeforeCompose(Page arg0, Component arg1,ComponentInfo arg2) {return arg2;}public void doBeforeComposeChildren(Component comp) throws Exception {comp.setVariable("person", new Person(), false);}public boolean doCatch(Throwable arg0) throws Exception {arg0.printStackTrace();return false;}public void doFinally() throws Exception {}public class Person{private String name;private int age;public static final int DEFAULT_AGE=20;public Person(){this.name="defaultName";this.age=DEFAULT_AGE;}public String getName() {return name;}public int getAge() {return age;}}
}

ZUML代码:

<?page title="GenericForwardComposerDemo" contentType="text/html;charset=UTF-8"?>
<zk><window title="SimpleComposerDemo" border="normal" apply="cn.asix.zkdemo.SimpleComposer"><button label="${person.name}" onClick="alert(person.age);"/></window>
</zk>

在上面的例子中,由于window是一个ID空间,而SimpleComposer在doBeforeComposeChildren方法中将变量person添加到了window所对应的命名空间中:comp.setVariable("person",new Person(),false),因此这个变量对于在window所在的ID空间中的子组件button也是可见的,所以label="${person.name}"等价于label="defaultName",而onClick="alert(person.name)"等价于onClick="alert(20)",这里有一点不一样,label中的内容属于EL表达式,而onClick中的内容属于脚本代码,不过定义在命名空间中的变量对于属于同一个命名空间的脚本代码及EL表达式都是可见的。关于组件的命名空间有这样的解释:1、每一个ID空间都有一个确切的命名空间与之对应;2、定义在命名空间中的变量对于属于同一个命名空间的脚本代码及EL表达式都是可见的。

The End.

ZK UserGuide(一)理解/userguide/index.zul导航原理相关推荐

  1. 彻底理解HashMap的元素插入原理

    转载自   彻底理解HashMap的元素插入原理 HashMap,是Java语言中比较基础也比较重要的一种数据结构,由于其用途广泛,所以,Java的工程师在设计HashMap的时候考虑了很多因素. 通 ...

  2. c语言转fpga原理,要想玩转FPGA,就必须理解FPGA内部的工作原理-可编程逻辑-与非网...

    FPGA(Field-Program mable Gate Array),即现场可编程门阵列,它是在 PAL.GAL.CPLD 等可编程器件的基础上进一步发展的产物.它是作为专用集成电路(ASIC)领 ...

  3. 彻底理解Vue数据响应式原理

    彻底理解Vue数据响应式原理 当创建了一个实例后,通过将数据传入实例的data属性中,会将数据进行劫持,实现响应式,也就是说当这些数据发生变化时,对应的视图也会发生改变. const data = { ...

  4. 北风设计模式课程---深入理解[代理模式]原理与技术

    北风设计模式课程---深入理解[代理模式]原理与技术 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 设计模式都是对生活的抽象,比如用户获得装备,我可以先装 ...

  5. agv系统介绍_重载AGV小车主要结构及导航原理是什么?

    相信对AGV有过了解的朋友都知道,当我们在进行工业生产过程时,重载AGV小车可以帮我们实现无人驾驶搬运的一个工作,可以保证AGV在运行时不用通过人工干预的情况下来完成现场的搬运工作,通过无人驾驶技术进 ...

  6. syslog 向内存中缓存_动画:深入浅出从根上理解 HTTP 缓存机制及原理!

    HTTP 缓存,对于前端的性能优化方面来讲,是非常关键的,从缓存中读取数据和直接向服务器请求数据,完全就是一个在天上,一个在地下. 我们最熟悉的是 HTTP 服务器响应返回状态码 304,304 代表 ...

  7. 0基础怎样理解深度学习的工作原理?做个票价预测工具就懂了

    原作:Radu Raice 安妮 编译自 Medium 量子位 出品 | 公众号 QbitAI 这篇文章颇!具!人!气! 软件工程专业的学生Radu Raice近日发表了文章<Want to k ...

  8. 利用Python理解TTF矢量字体显示原理

    本文从微软雅黑字体MSYH.TTF中抽取出2次B样条曲线和直线的控制节点坐标数据,利用Python将汉字轮廓绘制出来. MSYH字体轮廓是由2次B样条曲线和直线构成的,下图(fontforge软件获取 ...

  9. 深入理解图卷积神经网络(GCN)原理

    深入理解图卷积神经网络(GCN)原理 文章目录 深入理解图卷积神经网络(GCN)原理 前言 一.为什么需要GCN 二.GCN的原理 1.图的定义 2.GCN来了 2.1 矩阵计算公式 2.2 以小规模 ...

  10. 什么是MOS管驱动电路,如何理解MOS管驱动电路原理?

    今天泰德兰电子 小编和大家分享主题:什么是MOS管驱动电路,如何理解MOS管驱动电路原理? 作为电子工程师,我们都知道在使用MOS管设计开关电源或者马达驱动电路的时候,大部分人都会考虑MOS的导通电阻 ...

最新文章

  1. mysql left join的深入探讨
  2. mysql select 区分大小写,MySql查询不区分大小写解决方案(两种)
  3. linux无法运行病毒,{转}为什么linux系统不容易中病毒?
  4. 《大话数据结构》第3章 线性表 3.8.2 单链表的删除
  5. 信息安全系统设计基础第三周学习总结—20135227黄晓妍
  6. C#实现文件与二进制互转并存入数据库
  7. c语言更改编译时字体,c习题编译时出现空的字符常量,怎么修改?
  8. Java RandomAccessFile writeShort()方法与示例
  9. cookie 保存导航菜单的展开状态
  10. [ZJOI2013]防守战线
  11. mysql sql中的一些问题,Null与空字符
  12. 戴尔软件部门第一弹 收购备份公司AppAssure
  13. Mysql时间函数及格式处理
  14. 十进制数转换BCD码
  15. 鲁百年创新设计思维学习总结
  16. 20190131-JS - Promise使用详解--摘抄笔记
  17. NBA球员出手位置分布图
  18. PDF文件如何添加页面或插入其他PDF页面
  19. 阿里云服务器绑定域名、esc绑定域名、域名备案
  20. 校园招聘--奇虎360笔试

热门文章

  1. python中的unicode码是什么_Python中Unicode字符串
  2. JAVA JSP图书管理图书系统 servlet图书管理系统实现简单的图书管理系统源码
  3. Python连接Access数据库详细步骤
  4. 计算机opnet仿真实验心得,SIMULINK仿真实验心得体会
  5. rost反剽窃检测系统_个人如何进行毕业论文查重检测?
  6. Axure元件库,Axure元件库下载,Axure Design
  7. 2017年大数据行业盘点:方案落地转向了价值创造
  8. visio2013安装包及破解工具KMS
  9. Notebook左侧开启导航
  10. 国军标-Gjb软件设计说明书模板