当前位置: BOLT界面引擎 > 知识库文章 > HelloBolt系列教程 > HelloBolt4:响应事件

HelloBolt4:响应事件

作者:Tsukasa

    在界面中加入交互,响应各种事件,在脚本代码中更新界面效果.

     当鼠标悬浮到正中的Hello,Bolt!文本时,字体变为带下划线的粗体,鼠标变为手型,移出时变回初始的字体和鼠标图案;界面中多了一个关机图标,点击时弹出对话框;点击关闭按钮后,程序退出。
    
        在HelloBolt3中,我们已经通过在布局xml中向UIObjectTree Template上加入各种UIObject的方式建立了一个虽小但五脏俱全的界面。但是仅仅通过布局xml定义的界面,是静态的,不可交互的。要让界面可以动态的交互,还需要加入对各种输入事件的响应代码,在事件响应代码中执行交互逻辑。
        在界面中响应各种事件 ,在Bolt中是通过在适当的UIObject对象上加入以lua定义的事件响应函数实现的。
  Bolt中的事件,分为输入事件和非输入事件。用Bolt搭建的界面被激活后,当用户操作界面时,操作系统会向容器窗口发送各种输入消息,比如鼠标移动,点击,键盘输入等等,Bolt会依这些输入消息按照一定的规则触发特定UIObject对象上的特定事件,决定输入消息触发哪些UIObject对象上的事件的规则,称为消息路由。比如鼠标消息的消息路由很直观的就是鼠标在界面上的哪些UIObject对象范围内,就触发其中指定了响应鼠标事件的最顶层(zorder最大)的UIObject对象上的鼠标事件。比如当鼠标在正中的Hello,Bolt!文本上移动时,如果在对应的TextObject对象上定义了OnMouseMove事件的响应函数,该对象就会接收到OnMouseMove事件,该函数会被调用。
非输入事件是Bolt中自定义的,并非输入消息触发的事件。比如当某个UIObject对象被加入到对象树上之时,会触发该对象的OnInitControl事件(这是一个常用的初始化事件)。
       在布局xml中,通过在<obj>节点中加入<eventlist>节点,指定对象对特定事件的响应函数。
HelloBolt4 XAR包MainWnd.xml中的UIObjectTree Template定义中,<obj id="msg" class="TextObject">节点下加入了如下xml节点:
<eventlist>
              <event name="OnMouseMove" file="MainWnd.xml.lua" func="MSG_OnMouseMove" />
              <event name="OnMouseLeave" file="MainWnd.xml.lua" func="MSG_OnMouseLeave" />
         </eventlist>
<eventlist>节点下有若干<event>子节点,每个<event>节点指定对象对该对象类型支持的一个特定事件的响应函数(每种对象支持的事件参照Bolt元对象介绍),其中name属性指定要响应的事件名,可以是Bolt中定义的各种输入事件和非输入事件, file属性指定定义事件响应函数的文件(路径相对于当前的布局xml), func指定在文件中的事件响应函数的函数名。上面的xml片段就指定 id为msg的对象接收OnMouseMove事件,当事件触发时,调用MainWnd.xml.lua中的定义的MSG_OnMouseMove函数;OnMouseLeave事件亦然。当用户操作鼠标在Hello,Bolt!的文本范围内移动时,MSG_OnMouseMove函数会被调用,同样鼠标离开文本范围时,MSG_OnMouseLeave函数会被调用。
    转入MainWnd.xml.lua中OnMouseMove响应函数的定义如下:

function MSG_OnMouseMove(self)

         self:SetTextFontResID ("msg.font.bold")
         self:SetCursorID ("IDC_HAND")
end
UIObject的事件响应函数第一个实参一定是指向该对象的一个引用, 得益于lua的不定参函数声明的特性,第一个参数之后的参数依事件的不同而不同。 OnMouseMove事件的响应函数还可以接收第二和第三个参数, 分别代表鼠标点击时在对象本身坐标系中点击点的横纵坐标值,因为不需要,在上面的函数定义中直接忽略。动态的改变界面的效果,就是用lua语言调用Bolt提供的各种接口的过程。如这里,self是展示正中间文本的TextObject对象,在其上调用TextObject的SetTextFontResID接口,将字体更改为资源中定义的另一个不同字体,那么其显示出来的文本字体就会发生相应的变化,与在布局xml中指定该对象的<font>属性是等效的,之后调用SetCursorID接口改变鼠标图标。同样在在鼠标移出文本范围时, MouseLeave事件的响应函数MSG_OnMouseLeave的定义中,同样是改变字体和鼠标图标。

        这样,当鼠标悬浮到文本之上时,字体变为带下划线的粗体,鼠标变为手型,移出时字体变为初始字体,鼠标变回箭头。
    
要达到点击下方的关闭按钮退出程序的效果,需要在代表按钮的对象上加入OnLButton事件响应,在MainWnd.xml中的如下xml节点:
<event name="OnLButtonDown" file="MainWnd.xml.lua" func="close_btn_OnLButtonDown" />
注意这里设置<obj id="close.btn" class="LayoutObject">代表的逻辑对象响应鼠标事件,而不是其下的按钮背景接收,是因为在逻辑上我们视这些对象复合成为一个逻辑上的按钮对象,点击按钮关闭程序,而不是点击按钮背景关闭程序,因为按钮上除了背景之外还有其他元素,背景在逻辑上不能代表整个按钮,所以我们加入LayoutObject这样的逻辑对象隔离逻辑层次在定义事件响应时也是非常有必要的。
        转到MainWnd.xml.lua, close_btn_OnLButtonDown函数定义中,简单的调用lua内建的os包中的exit方法,简单粗暴的退出进程,这里仅作演示,真正的应用开发中不推荐这么做,因为这样跳过了进程入口函数的正常退出流程,无法执行各种资源释放流程,比如我们在主程序中加入的Bolt反初始化流程。
    
当UIObject对象被加入到对象树上时,会触发该对象的OnInitControl事件。为对象树的根对象加入非输入事件OnInitControl事件响应,如MainWnd.xml中如下xml节点所示:
         <event name="OnInitControl"/>
这是event定义中的又一种形式,如果函数定义的脚本文件为同名布局文件加上.lua后缀(如MainWnd.xml和MainWnd.xml.lua)同时事件响应函数与事件名相同,可以同时省略file以及func属性;否则必须同时显式指明。这样做有时可以减少代码,但是会影响Bolt的加载效率,一般不推荐使用。这里要指定OnInitControl事件的响应函数为MainWnd.xml.lua中定义的OnInitControl函数,可以省略。

     转到MainWnd.xml.lua中OnInitControl函数的定义如下:

local owner = self:GetOwner()

              local objFactory = XLGetObject("Xunlei.UIEngine.ObjectFactory")
              local newIcon = objFactory:CreateUIObject("icon2","ImageObject")
              local xarManager = XLGetObject("Xunlei.UIEngine.XARManager")
              newIcon:SetResProvider(xarManager)
              newIcon:SetObjPos(45,165,45+70,165+70)
              newIcon:SetResID("app.icon2")
              local function onClickIcon()
                   XLMessageBox("Don't touch me!")
              end
              newIcon:AttachListener("OnLButtonDown",true,onClickIcon)
              self:AddChild(newIcon)
这段代码通过编程方式动态在对象树上加入一个UIObject对象(对象的动态创建),虽然这种方式相较于在布局xml中定义对象来说非常枯燥,但有些时候还是有动态创建UIObject对象的需求的,遵循完备性原则,Bolt依然提供了这样的能力。以上代码中,self参数指向触发事件的对象,这里就是UIObjectTree上的显示底图的根对象, "Xunlei.UIEngine.ObjectFactory"标识的全局对象即是Bolt中用于创建UIObject对象的工厂对象,调用其上的CreateUIObject接口创建id为icon2 , class 为ImageObject的对象。Xunlei.UIEngine.XARManager标识的全局对象是Bolt的XAR包管理器,因为ImageObject需要使用XAR资源包中的位图资源,要通过调用SetResProvider接口将XAR包管理器设置到对象上,对象才可以通过资源id获取到正确的位图资源。之后设置位置和位图资源,并通过调用AttachListener接口加入事件响应函数,最后调用根对象上的AddChild接口,将这个新创建出的对象作为根对象的子对象加入到对象树之上。响应函数经常定义在闭包中,关于lua的闭包请参考lua要快点学
        这样,我们的界面上就多出了一个没有在布局xml中定义的对象,它在界面中显示了一个关机图标,点击它之后会弹出对话框。
       从上面动态创建对象的代码中,我们可以看出,动态创建对象跟在布局xml中静态定义对象的能力和流程是相同的,都是确定对象的层次,要加入到哪个对象之下;确定对象的各种属性,如位置,资源等;确定对象的事件响应。其实本质上,在布局xml中静态定义的对象,实际上是在UIObjectTree Template模板被实例化时,Bolt的加载器解析布局中的<obj>节点定义,按照上述流程迭代的将布局xml中定义的对象加入到对象树上,并设置好对象在布局xml中定义的属性和事件响应,两种方式是完全等效的。

 

 

迅雷公司 版权所有 Copyright 2003-2010 Thunder Inc.All Rights Reserved. 意见反馈:xl7doc@xunlei.com