Methods,Events,Handlers ,and Attributes
出自OpenFace
第二十一章 方法,事件,句柄,属性
第四部分 进级概念
目录 |
概述
LZX中,方法、事件、句柄,属性的概念和彼此联系的。本章将介绍它们之间的复杂关系。
属性,事件,方法,句柄都是定义类的实例怎样运转的性质。属性表现实例的状态。事件功能是在实例改变时和应用的其它部分通讯。方法通过名字被触发后被执行的Javascript块。每个句柄都是绑定给事件的一种特殊方法。
事件发生时,句柄同时被调用。事件可能有几个句柄,句柄可以用于联系一个实例的方法和另一个实例的方法。
由于LZX可以给实例增加属性,你可以给任何实例增加属性,事件,方法,句柄。(根据其它的如Java等面向对象语言,LZX允许定义存在新属性的实例的类)
LZX中使用属性,事件,句柄描述应用程序如何响应用户输入和其它的改变的。一般的,属性值改变时会发生事件。可以使用<event>标签定义事件。由句柄决定应用对事件的响应。
方法,事件和属性是很多应用都需要的概念。有几种事件驱动情形需要第四个概念--代表.
下面我们假定理解<attribute>元素的意思。本文结尾更详细的阐述了属性。
方法
在Javascript中方法是一种可以在实例中执行的行为,可以在脚本中调用它。
方法可以在<node>或<view>元素中定义。定义方法时,必须指明名字属性的值。在脚本中要使用名字调用它。调用方法时,使用name()方法的参数要放在()中。
在数据设置时不能使用<method>元素。那样会被当作数据,而不再是要执行的操作。
下例中,方法myMethod()在唯一的一个节点中定义。它由脚本myNode.myMethod()调用(节点使用id属性时,可以在程序的任何地方调用)
Example 21.1. Defining a method
<canvas>
<node id="myNode">
<method name="myMethod">
Debug.write('Nice day if it do not rain');
</method>
</node>
<script>
myNode.myMethod()
</script>
</canvas>
方法的命名
方法的命名需谨慎。应该具有描述性,可以帮助你和其它读者理解它的含义。也要记住在继承类时,新类会继承超类的方法。若定义了重名方法,原方法会被重写。为了避免'命名冲突',可以参阅LZX Reference文档中相关内容。
重写方法
当创建继承其它类的类时,新类继承其超类的所有方法。你可以重新定义继承的所有方法,这就是重写。
简单的参数
Example 21.2. Defining a method with arguments
<canvas>
<node id="myNode">
<method name="add" args="a,b">
var sum = a + b;
Debug.write('a + b = ' + sum);
</method>
</node>
<script>
myNode.add(4,9)
</script>
</canvas>
返回值
有时方法需要把方法的结果返回给调用方法的脚本。下例中方法returnsum() 使用return关键字。在脚本块中,这个返回值被用于计算其它结果。
Example 21.3. Returning data from a method
<canvas>
<node id="myNode">
<method name="returnsum" args="a,b">
var sum = 3 + 4;
Debug.write('a + b = ' + sum);
return sum;
</method>
</node>
<script>
var x = 5;
var y = myNode.returnsum();
var z = x + y;
Debug.write('z equals ' + z)
</script>
</canvas>
事件句柄
事件句柄是收到事件时要执行的代码。一个事件可以有0或多个句柄。
有两种方法可以用于定义句柄:
用"onevent"在节点中创建标签
使用 <handler> 标签
为了方便,对某些事件可以使用onevent语法,如下例所示的oninit为了易读性< handler>语法更适用。
标签中的onevent语法
对于这样的事件,最简单的声明事件句柄方法是包含属性的节点的定义标签中包含它。 这种语法对必须使用<handler>通过类属性产生的事件是不起作用的。
下例中事件句柄处理通过按键将视图的颜色由红改变成蓝而产生的事件。
Example 21.4. Event handler defined in opening tag
<canvas height="50">
<view height="30" width="30" bgcolor="red" oninit="LzFocus.setFocus(this)">
<handler name="onkeydown" reference="LzKeys">
this.setAttribute('bgcolor', blue);
</handler>
</view>
</canvas>
<handler>标签
<handler>标签定义了实例如何响应事件。句柄可以用代码处理事件或者使用方法来处理。一个事件可以有无限个句柄。
Handler 标签的"name"属性
<handler>标签定义当句柄触发的事件时执行的代码。句柄的名字与它关联的事件的名字是一样的。通过这种方式定义的定义的事件句柄在和它们关联的事件发生时执行。
Example 21.5. A simple handler
<canvas height="40">
<text text="not press keyboard" oninit="LzFocus.setFocus(this)">
<handler name="onkeydown" reference="LzKeys">
this.setAttribute('text', 'key handler');
</handler>
</text>
</canvas>
一个event多个handlers
一个事件可以关联多个句柄,事件发送时,所有相关的句柄都会执行。
Example 21.6. Multiple handlers for one event
<canvas>
<text oninit="LzFocus.setFocus(this)"> Howdy!
<handler name="onkeydown" reference="LzKeys" args="k">
var key=k.intValue();
if(key==13) Debug.write("handler one")
if(key==32) Debug.write("handler two")
if(key==35) Debug.write("handler three")
</handler>
</text>
</canvas>
句柄调用方法
句柄可能包含关联事件发生是要执行的javascript代码,或者它可能涉及一个包含要执行的代码的方法。引入另一个方法使用<handler>标签的method属性。
Example 21.7. Calling a method from a handler
<canvas>
<simplelayout/>
<view id="myview" height="30" width="30" bgcolor="blue"/>
<text text="Make red the blue box!" oninit="LzFocus.setFocus(this)">
<handler name="onkeydown" reference="LzKeys" method="redify"/>
<method name="redify">
myview.setAttribute("bgcolor", red)
</method>
</text>
</canvas>
类定义中的句柄
句柄在子类中是不可重写的,因此可以根据需要为事件增加多个会被触发的句柄。
若想要子类能够重写超类方法的句柄,超类需要通过事件关联一个方法:
<handler name="eventName" method="methodName" />
超类会重写方法名.
方法是实例中可以执行的一种行为 ,可以在任何脚本中调用它。也可以用句柄调用。例如,上例中的用法:
<handler name="eventName"> this.methodName(); </handler>
属性
属性定义类的实例的特性。以<view>元素为例。它具有40多个属性,例如x,y,背景色等。这些属性有些是元素自己定义的,有些是继承node类的。这行属性在LZX标签中定义.每个属性都有类型,如数字型,布尔型,串型。
定义了属性,就可以在类的标签中使用。
<view name="charlie"/>
但 framitz不是<view>元素定义的属性,所以会产生错误:
<view framitz="whatnot"/>.
可以使用<attribute>标签定义新的属性。
<view name="bob"> <attribute name="framitz"/> </view>
属性可以是标签的元素或JavaSctipt类的性质。属性通常在标签中声明和设置,也可以在脚本中设置和读取它们的值。不是所有的属性都可以在脚本中设置,类似的也不是所有的都可以在标签中使用。属性按性质分为以下五种类型:
属性可以在顶级标签(canvas)中声明:
Example 21.8. Setting an attribute value in the tag header
<canvas height="20"> <view width="20" height="20" bgcolor="red"/> </canvas>
可选的设置属性的方式是将<attribute>标签作为要设置的属性的标签的子节点。
Example 21.9. Using the attribute element to set an attribute value
<canvas height="20"> <view> <attribute name="width" type="number" value="20"/> <attribute name="height" type="number" value="20"/> </view> </canvas>
<attribute>标签对写类和执行存在属性的复杂约束是很又用的.
在脚本中多数属性的值是可以通过.语法来获取的.
Example 21.10. Using dot syntax to retrieve an attribute value
<canvas height="20"> <view name="myView" width="20" height="20" bgcolor="red"/> <script> var myAttributeValue = myView.x; // myAttributeValue now has the value 20 </script> </canvas>
属性值也可以使用getAttribute() 方法读取.有时是不必要的,但对于获得名字由脚本描述的任意属性的值是很有用的,
Example 21.11. Using getAttribute to retrieve an attribute value
<canvas height="20"> <view name="myView" width="20" height="20" bgcolor="red"/> <script> var myAttributeName = "x"; var myAttributeValue = myView.getAttribute(myAttributeName); // myAttributeValue now has the value 20 </script> </canvas>
所有可以在脚本中设置的属性都可以通过使用setAttribute() 方法赋值:
Example 21.12. Using setAttribute to set an attribute value
<canvas height="20">
<view width="20" height="20" bgcolor="red" oninit="this.setAttribute('width', 50);"/>
</canvas>
属性的种类
根据设置,读取,修改值 所有属性可以属于五类之一:
Attributes (with setter)需要设置的
Attributes (without setter)无需设置的
Event Handler (script may be defined in XML tag) 事件句柄(XML标签定义的脚本)
Final Attributes (defined only in XML tag)终极属性(XML标签中定义的)
Read-only Attributes (JavaScript fields)只读属性(JavaScript 域)
下面有简洁的描述
需要设置的属性
这是内置的需要设置并且在运行时修改 和在约束中使用的属性。调用setAttribute()方法时设置被自动调用。可以通过脚本使用.语法得到属性的值。
For example:
Example 21.13. Using setAttribute to update a constraint
<canvas height="20">
<view id="myView" bgcolor="red" oninit="LzFocus.setFocus(this)">
<handler name="onkeydown" reference="LzKeys">
myView.setOpacity(1.5-this.opacity);
</handler>
<text text="${'My opacity is ' + myView.getOpacity() + ' Press any key to change it.'}"/>
</view>
</canvas>
无需设置的属性
有些属性在标签中是可用的,但没有预定义的设置方法。它们使用默认的设置方法在运行时设置属性值。典型的例子是在组件中使用(attribute)已定义的常用属性。它们也可以在标签中声明。
事件句柄属性
事件句柄属性是当特殊的事件发生时要执行的内容说明. 它们总是包含脚本,在运行时不能改变.它们的值不能(也不必要)通过脚本获得.
<canvas height="400">
<view width="50" height="50" bgcolor="red" />
</canvas>
终极属性
终极属性在标签中声明和赋值,但不能在脚本中改变name 和id 就是很好的例子。
只读属性
只读属性,有时成为'域',是元素的API变量。由于它们是只读的,所以不能在标签中设置,它们的值可以通过.语法获得。
<attribute>标签
在类的定义中,<attribute>标签定义可以在类的实例中设置的属性。
Example 21.14. Defining an attribute in a class
<canvas height="36">
<class name="diamond" >
<attribute name="size" type="number"/>
</class>
<diamond size="25" x="36" bgcolor="red" width="${this.size}" height="${this.size}" rotation="45"/>
</canvas>
该例中<attribute>元素设置它关联的对象的属性。这种用法当中<attribute>元素等价于在标签中使用属性。下例使用标签元素设置view的宽,<attribute>标签设置view的高。
Example 21.15. 在视图中定义属性
<canvas height="25"> <view width="25" bgcolor="red"> <attribute name="height" value="25"/> </view> </canvas>
这种语法功能上等价于LZX中定义的属性。<attribute> 的使用可以增加可读性。
可以自定义新属性.这是必须使用<attribute> 标签.
属性和约束
属性可以被其它属性的值约束.
属性类型
默认情况下属性被定义为表达式类型.很多情况下这不是我们想要的结果。
Example 21.16. Incorrect: attribute has no effect
<canvas height="300">
<simplelayout spacing="5"/>
<class name="box" >
<attribute name="label" value="Label"/>
</class>
<box label="Box1" height="100" width="100" bgcolor="red"/>
<box height="100" width="100" bgcolor="red"/>
</canvas>
确保让属性做它应该做的事,声明属性时定义其类型.
Example 21.17. Declaring attribute type
<canvas height="300">
<simplelayout spacing="5"/>
<class name="box" >
<attribute name="label" value="Label" type="string"/>
</class>
<box label="Box1" height="100" width="100" bgcolor="red"/>
<box height="100" width="100" bgcolor="red"/>
</canvas>
使用<type>选项
类型选项指定属性值的类型和解析值表达式的效果.
串:
给产生JavaScript串的表达式赋值 使用 值="${表达式}"
颜色:
给JavaScript表达式赋值,使用 值="${表达式}".。表达式必须是数字颜色值
布尔:
真或假
数字:
数字或数字表达式
大小:
非负数字表达式
文本:
所有其它类型作为指定类型的表达式解析
颜色概要
颜色可以有一下的值:
black 000000 green 008000 silver C0C0C0 lime 00FF00 gray 808080 olive 808000 white FFFFFF yellow FFFF00 maroon 800000 navy 000080 red FF0000 blue 0000FF purple 800080 teal 008080 fuchsia FF00FF aqua 00FFFF
属性赋值时间
不管在属性元素还是在标签中设置的属性值,都要根据选项评估。
immediately 立刻的
定义元素时初始化表达式的属性值.该表达式必须是常量而且不能依赖其他对象。
只一次
初始化元素时初始化表达式的属性值.表达式不能依赖任何其他元素的性质,也不能依赖于被评估的任何其它属性的特别顺序.如果需要可以使用init方法。
总是
表达式值改变的任何时候更新属性值:属性受以下表达式的值约束。
已声明的评价时间可以通过使用值="$when{表达式}"赋值来重写。
通过JavaScript访问属性值
属性通常可以在类方法和表达式中通过名字来使用,(除了在类初始化时,要在元素中创建属性必须使用this。语法不被支持)。
For example:
Example 21.18. attribute for internal flags
<class name="myclass">
<attribute name="foo" value="1"/>
<attribute name="thing" value="foo + 4" when="always"/>
<attribute name="bar"/>
<method name="dothis">
if (bar) {
this.setAttribute("foo", 6);
}
</method>
</class>
定义<attribute>
很多时候设置属性需要执行绝对代码操作。例如,设置视图的宽,视图的clip属性是false时,视图需要更新父视图的宽。但执行onwidth事件处理要执行的代码就很方便。最好使用setAttribute() 方法设置view的宽属性。
使用setAttribute()设置属性时,setAttribute()处理给属性赋值的任务,并发送事件。可以通过在标签中设置属性。之后定义一个设置的方法。
Example 21.19. Defining an explicit setter method
<canvas height="40">
<text text="click me" oninit="LzFocus.setFocus(this)">
<attribute name="time" setter="setTime(time)"/>
<attribute name="ontime" value="null"/>
<handler name="onkeydown" reference="LzKeys" args="k">
var key=k.intValue();
Debug.write('inited:' + this.isinited);
var now = (new Date()).getTime();
this.setAttribute("time", now);
</handler>
<method name="setTime" args="t">
if (!this.isinited) { //catch the case where this.time is being
this.time = t; //initialized during object instantiation
return;
}
if (typeof this.time=="undefined"||this.time==null) {//handle first set of time
this.setAttribute("text", "first click registered");
}
else {
var diff = t - this.time; //handle any additional setting of time
this.setAttribute("text", diff + " milliseconds between clicks");
}
this.time = t; //as this is the declared setter for
//this.time, we have to set it
</method>
</text>
</canvas>
获取和设置属性的不同方法
LZX中有有四种方式获取和设置属性,每种都有一定的优缺点:
使用方法setAttribute() and getAttribute()
uch as onx() and ony()对特定属性使用预定义的"setters"
使用上述常用的获取和设置方法
直接读取属性,不用取,置方法
setAttribute() and getAttribute()
最可信的获取和设置属性的方法是在包含属性的视图,类,节点中使用方法getAttribute() or setAttribute()。触发setAttribute()会自动的触发属性关联的‘on’事件。触发事件需要绑定属性的约束正确的工作。
是访问属性最慢的方式。以下两点可以说明:
普遍认为调用方法要比不用方法慢很多。
setAttribute()触发属性的关联事件,并更新目标属性的约束。
外部属性设置方法
有些OpenLaszlo 运行包含的类属性定义了外部的获取和设置方法。类LzView有setX(), setWidth()等方法。实际上这些方法的使用本质上和使用setAttribute() or getAttribute(),效果是一样的。
Example 21.20. 使用属性
<node> <attribute name="foo"/> <method name="doSomething"> this.setWidth(10); </method> </node>
如果自定义了获取和设置方法,它就不会自动的通过setAttribute() or getAttribute()触发。最好的设置属性的方法是处理属性关联的事件。
直接获取和设置属性
可以不用触发任何方法而直接获取和设置属性,这样就难免会出现不可预知的行为。由于不调用方法,关联的属性事件据不会发生。 绑定改属性的约束也不会更新。当使用LFC对象设置属性时,不调用包含重要处理信息的方法而直接设置属性值无疑是个馊主意。
Example 21.21. 直接得到属性值
<node>
<attribute name="foo"/>
<method name="doSomething">
this.width = 10;
Debug.write('this.width='+this.width)
</method>
</node>
鉴于以上原因,明智的直接设置属性--setX是修改属性值的最快的方法,虽然在有界集内是不安全的。直接获取属性是安全的。直接获取属性和使用setAttribute()设置属性是理想的选择。应该认真考虑以上设置属性的方法。

