EXML是一种严格遵循XML语法的标记语言,通常用于描述静态UI界面。这节内容将详细介绍EXML的语法规则。 ## 根节点 首先分析一个最简单EXML文件内容: ~~~ typescript ~~~ 它跟XML一样,是由标签组成的。每个标签都有个命名空间前缀,例如``中的`e`,它的对应命名空间声明也在根节点上:`xmlns:e="http://ns.egret.com/eui"`。以e这个命名空间开头的节点,表示在EUI这个UI库中的组件。而``中的`Group`就是对应代码中`eui.Group`这个类。 这个例子中只有一个根节点,根节点上的`class`属性表示它在运行时解析后要注册为的全局类名。以上的EXML文件,在运行时解析后完全等价于如下代码: ~~~ typescript module app { export class MyGroup extends eui.Group { public constructor(){ super(); } } } ~~~ 从这个例子可以看出EXML文件与代码的对应关系。EXML解析后会变成一个自定义类,继承的父类就是EXML的根节点,模块名和类名定义在跟节点上的`class`属性内。 `注意:一定要加class这个属性,否则编译会报错` ## 添加子项 上面的例子只有一个根节点,将它扩展,添加一个Image子项: ~~~ typescript ~~~ 以上内容等价于如下代码: ~~~ typescript module app { export class MyGroup extends eui.Group { public constructor(){ super(); var image = new eui.Image(); this.addChild(image); } } } ~~~ ## 设置属性 刚刚的例子只添加了一个空图片,什么都显示不出来,接下来给它设置一些属性: ~~~ typescript ~~~ 上述代码直接给Image节点加了source属性,它解析后相当于如下代码: ~~~ typescript var image = eui.Image(); image.source = "image/button_up.png"; image.x = 10; this.addChild(image); ~~~ 声明属性时不用考虑值的类型,都写在双引号内即可。解析器运行时会去读取节点上这个属性的类型,并正确格式化为对应的结果。例如source的结果是赋值一个字符串,而x的结果是赋值一个数字10,不会带双引号。 以上是最常见的属性写法,通常只能描述简单数据类型的赋值,如果是复杂数据类型,比如要对属性赋值为另一个节点时,可以采用另一种写法: ~~~ typescript ~~~ ``是属性节点,表示父级节点Image的scale9Grid属性,这个属性要接受一个Rectangle对象的实例。以上内容等价为: ~~~ typescript var image = eui.Image(); image.source = "image/button_up.png"; image.x = 10; var rect = new Rectangle(); rect.x = 10; rect.y = 10; rect.width = 45; rect.height = 35; image.scale9Grid = rect; this.addChild(image); ~~~ ## ID属性 我们可以在节点上声明一个id属性,注意这个id属性与HTML中的id并不是一回事,它的结果相当于给解析后的类声明了一个公开变量。例如: ~~~ typescript ~~~ 等价于: ~~~ typescript module app { export class MyGroup extends eui.Group { public iconDisplay:eui.Image; public constructor(){ super(); var image = new eui.Image(); this.addChild(image); this.iconDisplay = image; } } } ~~~ ## 属性语法糖 前面的例子描述了如何给Image设置scale9Grid(九宫格)属性,使用起来有些麻烦。因此准备了一系列的语法糖可以简化属性的声明,九宫格的属性还可以用如下方式声明: ~~~ typescript ~~~ 对于特别常用的属性,解析器会内置一些语法糖,使编写更加简洁。这里介绍另一个语法糖,width或height属性可以直接写百分比: ~~~ typescript ~~~ 解析器在处理这两个属性时,若赋值的是具体数字,按默认规则走,生成对width/height的赋值代码,若赋值的是百分比字符串,那么就会生成percentWidth等属性的赋值代码: ~~~ typescript var image = eui.Image(); image.source = "image/button_up.png"; image.percentWidth = 100; image.percentHeight = 100; this.addChild(image); ~~~ 所以代码中,实际上应该使用percentWidth等属性去设置百分比,width属性在代码中只接受具体的数字。 目前只有以上这两种属性语法糖需要注意,不排除以后把使用频率高的属性也设计为语法糖。 ## 节点默认属性 上文介绍了复杂属性节点的声明方式,要先显式声明一个属性名称的节点,内部再跟上要赋值的节点。这里还有一个类似语法糖的写法,eui库内的组件,通常都会有一个默认属性,如果子节点是赋值给父节点的默认属性,那么可以省略属性名节点。如下例: ~~~ typescript ~~~ 这个例子中,将Group实例赋值给了一个滚动容器Scroller的viewport属性。由于viewport是Scroller的默认属性,因此我们可以直接省略``节点,改成如下写法: ~~~ typescript ~~~ 除了支持省略属性名节点外,若默认属性的类型是一个数组,还可以省略Array节点。其实添加子项也只是省略默认属性的一种特例,因为容器的默认属性是`elementsContent`,类型正是数组。最开始添加子项的那个例子完整写法如下: ~~~ typescript ~~~ 当然,直接用最简洁的省略默认属性写法即可。