Extending Classes

出自OpenFace

跳转到: 导航, 搜索

Prev                                                                           Next

                           第二十四章  继承类

                           第四部分  进阶概念


  这一章中假设你非常熟悉一些LZX中的基本概念,如视图,函数和属性。了解面向对象编程思想也是非常有用的,但不是必须的。在20章 类,介绍了类的初级概念。


目录

概况

  继承机制允许你从其它类中创建一个自定义类。优点是在你每一次创建一个相似类的时候,大部分逻辑并不需要重写。

  一个类继承于另外一个类被成为子类,而被继承的类则是该类的超类。子类继承了父类的所有函数和属性。如<view>一个视图元素,被每个子类所继承。在LZX中,在<class>标签中通过extends属性来创建一个子类。

<class name="myclass" extends="mysuperclass">
  ...
</class>


  创建一个没有extends属性的类都被默认为<view>的子类,<view>中所有函数和特性都被子类所继承。下面声明的两个类是一样的。

<class name="myclass"/>
<class name="myclass" extends="view"/>


  每个类只能继承自一个类,但可以进行多层继承。多重继承是不被支持的,也就是说,创建一个新类时不能继承多余一个已存在的类。在类里面函数,属性,操作和视图都会被依次继承下来。实现还存在问题)。

Example  24.1. 继承链

<canvas height="125">
 <class name="top">
   <attribute name="myfoo" value="bar" type="string"/>
 </class>
 <class name="middle" extends="top">
   <method name="doit">
     Debug.write("myfoo is " + this.myfoo);
   </method>
 </class> 
 <class name="bottom" extends="middle">
   <text text="press any key">
    <handler name="onkeydown" reference="LzKeys">
         this.parent.doit();
    </handler>
   </text>
 </class> 
 <bottom/>
</canvas>

覆写函数,时间和属性

  子类可以覆写父类中的函数来提供不同的实现。覆写父类中的函数时,参数可以不同。例如,如果父类的函数有两个参数如函数(args1,arg2),在子类中你在可以选择三个函数来作为函数的参数,如method(arg1,arg2,arg3)。

  LZX里不支持函数重载。意思在在一个类中不能有同样名子的不同函数.因为在运行时,所关心的只是调用的函数名.如果你在一个类中定义两个或更多具有同名的方法时,在运行时,会出现编译警告。

  不过,你可以有一个或多个handler事件处理器,在21章 函数,事件,操作和属性中有描述。

您可以使用<attribute>标签修改继承来的属性或者在<class>定义一个自己的属性。实现还存在问题

Example 24.2. Inheriting Properties(继承属性)

<canvas width="100" height="100">

<class name="bt" bgcolor="red" width="100">
 <attribute name="text" type="string"/>
   <text  name="ct" x="5" width="100"  fontsize="18"/>
     <method name="setText" args="t">  
        this.ct.setText(t);
     </method>
</class>
<bt text="hello" bgcolor="red">
   <handler name="oninit">
        this.ct.setText(this.text);
   </handler>
</bt>  
</canvas>


  你可以使用超级关键字来调用父类中的方法。在某些情况下,你覆写了父类中函数时,超级关键字是非常有用的。一种方法是只能通过super来调用被覆写的函数。换句话说,在子类中的myfun()函数中只能通过super.myfunc()来调用父类中的myfunc()函数。

Example 24.3. super 关键字

<canvas height="140">
 <class name="foo">
   <method name="talk">
     Debug.write("hello");
   </method>
 </class>
 <class name="bar" extends="foo">
   <method name="talk">
     super.talk();
     Debug.write("goodbye");
   </method>
 </class>
 <bar oninit="this.talk()"/>
</canvas>

text声明

  text>和<inputtext>标签都是唯一的内部类,因为它们能容纳文字内容:

<canvas height="50" >
<simplelayout axis="y"/>
 <inputtext>plain text</inputtext>
 <text>styled text</text>
</canvas>


通过脚本实例化类

  一般使用标签实例对象。例如,假定声明了<class name="myclass">,那么就可以使用<myclass/>实例化该类。尽管如此,有时候你会需要使用脚本实例化一个对象。脚本实例化的语法如下:

var myobject = new myclass(parent, attributes, children, instcall)


 where:

  父节点是你的对象要放置的节点层次。是空值也无妨。如果创建view的子类且没有父节点,那么canvas就成为该对象的父节点。

  属性值是传递给对象的信息。例如,你要使用不同的背景色,宽,高实例化新的view,可以传值{背景:红,宽:50,高:50}。

  children 是对象封装的子节点数组。OpenLaszlo运行时间短是由于LZX的层次传给对象的子节点造成的。通常是空值。

  决定对象实例化何时发生的属性instcall是布尔型的。若为‘false’,对象的实例化立即发生,否则,实例会于其它的view同步。

  这些参数都是可选的。无需设置它们。

  下例示出了怎样实例一个新对象并通过脚本将它加到其它的view中。

 Example 24.4. 脚本实例化

<canvas height="120">
   <class name="mybox">
      <view bgcolor="${parent.bgcolor}" width="50" height="50"/>
   </class>
   <view name="redbox" bgcolor="red" width="100" height="100"/>
   <text x="10" y="110" fontsize="16" text="press left key add cyan"/>
   <text x="10" y="130" fontsize="16" text="press right key remove cyan"/>
   <handler name="onkeydown" reference="LzKeys" args="key">
      var keycode = key.intValue();
      if(keycode==37){
          new mybox(canvas.redbox,{name:'cyan',bgcolor:0x00ffff });
      }else if(keycode==39){
          if (canvas.redbox['cyan'] != null) canvas.redbox.cyan.destroy();
      }
   </handler>
</canvas>


  类的定义都是全局的。若动态的实例化一个类,可以使用new global[classString](...)。 参照前例,mybox可以使用new global['mybox'](canvas.redbox, { bgcolor: 0x00ffff })来实例化。

  要知道标签类的名字不总和JavaScript中是一样的。例如:<view> 在脚本中实际是LzView, <node> 应该是LzNode。这样通过脚本实例view类时,应该用new LzView(...) ,而不是not new view(...))。 参阅LZX Reference文档,看看LZX标签是如何映射到JavaScript的。

使用LZX类

使用类的基本方法

  可以使用声明类标签并在canvas下实例类的方法写类。这样就可以看到你构造的类:

<canvas>
 <class name="myclass">
   ...
 </class> 
 <myclass/>
</canvas>

  也可以先使用view标签草拟类,之后再把它转换成类。这种方法的缺点是不能声明属性。

继承view类

  顶层view继承自放在顶层子类的超类。由超类继承的视图会首先按次序放置。这可以通过子视图数组验证。

Example 24.5. 继承view类

<canvas>
 <class name="one">
   <view name="r" bgcolor="red" width="200" height="200"/>
 </class> 
 <class name="two" extends="one">
   <view name="g" bgcolor="green" width="100" height="100"/>
 </class>  
 <class name="three" extends="two">
   <view name="t" bgcolor="teal" width="50" height="50"/>
   <view name="y" bgcolor="yellow" width="25" height="25"/>
 </class> 
 <three id="mysubclass" oninit="Debug.write('subviews: ' + this.subviews)"/>
</canvas>


  类的实例中声明的视图会被放在类的顶层,除非使用属性defaultplacement的声明。那些视图稍后会被依次放在子视图数组中。属性defaultplacement可以告诉类在哪里声明视图会被放在类的哪个层级并且后面部分会做出更详细的解释。

 Example 24.6. 继承view类的次序

<canvas height="250">
 <class name="foo">
   <view bgcolor="red" width="100" height="100"/>
 </class> 
 <foo name="myfoo" oninit="Debug.write('subviews: ' + this.subviews)">
   <view bgcolor="yellow" width="50" height="50"/>
 </foo>
</canvas>



  那么,它们就是按先r,后y的次序显示。(不会叠加)

Example 24.7. 使用simplelayout继承view 的次序

<canvas height="250">
 <class name="foo">
   <view bgcolor="red" width="100" height="100"/>
 </class>
 <foo name="myfoo" oninit="Debug.write('@@@subviews: ' + this.subviews)">
   <simplelayout/>
   <view  bgcolor="yellow" width="50" height="50"/>
 </foo>
</canvas>

布置

  类的内部结构对其各级子节点通常是不可见的。默认情况下,出现在类内部的实例使子节点成为类的顶层实例。这对容器类通常是不合理的。

Example 24.8. Undesired placement

<canvas height="50">
 <class name="myframe" extends="view">
   <attribute name="bgcolor" value="red"/>
   <view x="5" y="5" width="${parent.width-10}" height="${parent.height-10}" bgcolor="#FFFFCC"/>
 </class> 
 <myframe width="220" height="20">
   <text>This is some text</text>
 </myframe>
</canvas>


  这种行为可以通过使用defaultplacement属性或determinePlacement() 方法来改变。属性很容易用--它是通过命名要附加子节点的子视图来鉴别的类属性。子节点会用匹配的名字附加给第一个子视图。若没有,子节点就作为类的顶层节点,这中情况可能发生在属性defaultplacement没有指定的情况。

  需要知道属性defaultplacement必须声明为‘string’类型。

Example 24.9. Placing a child in desired subview

<canvas height="50">
 <class name="myframe" extends="view">
   <attribute name="bgcolor" value="red"/>
   
   <attribute name="defaultplacement" value="insideview" type="string"/>
   
   <view x="5" y="5" width="${parent.width-10}" name="insideview" height="${parent.height-10}" 
               bgcolor="#FFFFCC"/>
 </class>
 <myframe width="220" height="50">
   <text>This is some text</text>
 </myframe>
</canvas>


  类中声明的元素不需考虑布置,但超类或类的实例中的子节点需要。

Example 24.10. Defaultplacement

<canvas height="150">
 <class name="myframe" extends="view">
     <attribute name="bgcolor" value="red"/>
     <attribute name="defaultplacement" value="'insideview'"/>
     <view x="5" y="5" width="${parent.width-10}" name="insideview" height="${parent.height-10}" 
             bgcolor="#FFFFCC"/>
     <view x="5" y="${parent.height}" name="anotherview" 
           width="${parent.width-10}" height="10" bgcolor="blue"/>
 </class>
 <class name="subframe" extends="myframe">
   <simplelayout axis="y"/>
   <text bgcolor="teal">subframe text</text>
 </class>
 <myframe width="220" height="50">
   <text>This is some text</text>
 </myframe>
 <subframe width="220" height="50" y="70">
   <text bgcolor="green">More subframe text</text>
 </subframe>
</canvas>



排版

  布局需要作为一个必要属性来考虑。这通常是需要的行为,因为它使超类和实例类修改默认放置的视图的布局是很简单的。布局属性可以通过设置不存在的属性placement来重写该行为。属性placement示出元素的容器应该放在容器的内部。容器内有defaultplacement属性,它的值优先级较高。你可以确信布局没有在类中通过把属性defaultplacement作为标签元素声明而被处理。

Example 24.11. 布局设置

<canvas>
<class name="myplacement" defaultplacement="'red'" >
  <simplelayout spacing="10"/>
  <view name="red" bgcolor="red" width="60" height="60"/>
  <view name="yellow" bgcolor="yellow" width="60" height="60"/>
</class>
<myplacement>
  <view name="blue" bgcolor="blue" width="50" height="50" placement="yellow"/>
  <view name="green" width="50" height="50" bgcolor="green"/>
  <view name="teal" width="50" height="50" bgcolor="teal"/>
  <simplelayout spacing="10"/>
</myplacement>
</canvas>

直接父节点

  直接父节点涉及到子节点的位置。若没有placement赋值,那么子节点的值和父节点是相同的。

Example 24.12. Parent vs. immediateparent

<canvas height="200">    
 <class name="container" defaultplacement="'red'">
   <view name="red" bgcolor="red" width="150" height="150"/>
 </class>
 <container name="top">
   <view name="yellow" bgcolor="yellow" width="50" height="50">
     <handler name="oninit">
       Debug.write('parent: ', this.parent);
       Debug.write('immediateparent: ', this.immediateparent);
     </handler>
   </view>
 </container>
</canvas>

获得 defaultPlacement节点的访问

  可能实例类需要涉及默认放置节点。查找类的子节点是个很好的方法。

 Example 24.13. 获得 defaultPlacement节点的访问

<canvas height="200">
 <class name="container" defaultplacement="'red'">
   <attribute name="contentview" value="null" type="expression"/>
     <view name="green" bgcolor="green" width="100" height="100">
     <view name="yellow" bgcolor="yellow" width="50" height="50">
       <view name="red" bgcolor="red" width="50" height="50"/>
     </view>
   </view>
 </class>
 <container name="top"/>
</canvas>

术语表

  继承:

    类定义中用于创建子类的关键字。

  继承:

    类从超类中自动包含已定义的变量和方法的概念。

  实例化:

    创建类或对象的实例的行为。

  重载:

    使用一个标识符得到不同功能的函数。

  重写:

    在子类中实现超类中提供的不同的方法。

  子类:

    有其它类衍生得到的类。

  超级的:

    允许子类触发它的超类中方法的关键字操作符。

  超类:

    子类获取属性和方法的类。

                                     Home

Prev                                                                          Next