迅雷7界面引擎XLUI_Bolt_SDK的调用例子

感谢迅雷公司程序员:hansom  刘智聪 luren lyx waterflier  编写
本文出处  vab5211314(DonelRi)'s Blog      >>  cnjyl.6k0.org

=======================================界面引擎 XML文件格式以及XAR包介绍========================================
术语:
XLUE:Xunlei UIEngine,迅雷UI引擎,代号BOLT
XAR: Xunlei Archive,一种迅雷自定义文件格式。
标准对象: XLUE引擎内部实现的基础对象类,以及从它派生的各类对象类。
控件: XLUE引擎支持的一种由用户在外部经过XML和lua来配置的复合对象,一个控件
能够由多个标准对象或者控件组成。
对象树:XLUE引擎内置的一种数据结构,用于管理标准对象以及控件

XLUE XAR 包文件结构介绍
XAR 包的设计目的主要是用来实现模块化,方便代码重用,通常使用XLUE引擎的应用程序,一个XAR包就够用了。可是对于复杂的应用程序,好比支持插件扩展的 应用程序,一个XAR就不够用了,须要多个XAR一块儿协同工做。XAR提供了一种引用包含的机制,一个XAR包能够包含另一个XAR包,可是不能互相包 含。即A包中包含了B包,那么B包中就不能再包含A包。所以这时咱们能够将主程序里面用到的XAR包做为主包,插件里面用到的XAR包做为附属XAR包, 而后插件包包含主XAR包,使用其中提供的类或者注册的全局函数,以及资源文件。

XAR包文件结构以下:
1. package.cfg
本 文件主要是用来描述XAR包的一些信息,包括包名,做者,版权全部者等,另外还指明nametable.cfg文件的路径,主资源包名,以及启动脚本路 径,除此以外还能够指明该XAR包包含了哪一个XAR包。若是指定了包含的XAR,那么加载该XAR包的时候也会去加载包含的XAR,除非包含的XAR已经 被加载过。所以,该XAR包可使用包含的XAR包中的控件,资源等信息。

2. nametable.cfg(可选,用户能够自定义名字)
本文件主要用来描述layout包里面定义的控件和模板的位置,便于查找。该文件由XLUE编译工具生成。

3. layout(布局XML及LUA脚本文件目录)(可选)
本目录主要由xml文件和lua脚本构成,也多是打包过的.xar格式的文件,其中包括对象树模板,对象模板,宿主窗口模板,动画模板的定义(这里要特 别注意,定义的是模板,并不是模板的实例),并描述他们的属性,方法以及事件响应。而且其中包含全部用户自定义的控件,全部的用户自定义控件由一个xml文 件以及一个lua文件构成,xml文件中定义控件的属性,方法,以及事件对应的响应函数,lua脚本中是这些方法和响应函数的实现。XML文件中定义的模 板和控件都是静态的配置信息,只有在经过lua脚本建立控件或者建立模板实例的时候才会去获取并解析其中的XML节点配置信息,解析的过程当中会建立出来相 应类型的模板或者控件实例

4. res(资源包集合目录) (可选)
   本目录主要由资源包构成,里面多是一个个的zip格式的资源包,也可能只是文件夹格式的资源包。资源包的构成以下:
1) resource.cfg
本文件描述资源包的信息,包括包名,做者,版权全部者这些信息,还可能会指定父资源包。当一个资源的ID在本包里找不到对应的资源时,会尝试到父包中查找。
2) bitmap(图片等资源文件的存放目录,用户能够自定义名字)
    本文件夹存放图片文件,只能是PNG格式的图片。
3) bitmap.xml(可选,用户能够自定义名字)
4) color.xml(可选,用户能够自定义名字)
5) font.xml(可选,用户能够自定义名字)
上面这三个XML不是必须有的,只是为了分类存放方便,用户也能够把全部的资源定义放在一个本身命名的xml中。
6) onload.lua(可选,用户能够自定义名字)
   本文件在资源包被加载或者资源包切换的时候被加载,于加载资源以前执行,若是须要进行一些额外的处理能够加上须要的lua代码
7) onunload.lua(可选,用户能够自定义名字)
                  本文件在资源包被加载或者资源包切换的时候被加载,于释放资源以前执行,
若是须要进行一些额外的处理能够加上须要的lua代码
5. onload.lua(可选,用户能够自定义名字)
本文件是XAR中的驱动,XAR被加载时发现其中有这个文件,就会去加载并运行它,经过它来将layout目录里面定义的控件和res目录里面定义的资源展现到程序界面中。这个文件标明为可选,由于没有这个文件程序不会报错,但对于功能完整的XAR包这个是必须的

能够正常工做,功能完整的XAR包,有如下几种配置方式:
1.最简单XAR包
1) package.cfg
2) onload.lua

这种方式配置的XAR包,自己不带有资源或者布局,依赖其余包里面提供的
资源,经常使用于逻辑较为简单,功能单一的插件

2. 较复杂的XAR包
1) package.cfg
2) res
3) onload.lua
这种方式配置的XAR包,和上面那种相比,多了资源包目录, 本身有资源
包,通常用于自带皮肤,而后使用主程序中默认控件布局的插件。


3. 最复杂的XAR包
1)   package.cfg
2)   nametable.cfg
3)   layout
4)   res
5)   onload.lua
这种方式配置的XAR包,带有资源和布局文件目录,并且还有nametable.cfg
配置文件,这个文件主要用于查找模板或者控件。能够是一个独立的包,不依赖其余包,也但是依赖其余包。程序中的主XAR包通常都是这种结构。

XLUE 布局 XML 文件结构
上 面主要介绍了XAR包的构成,以及包内各文件的用途,其中讲到layout包中主要是一些模板的定义,还有用户自定义的控件。那么这些XML的格式又是怎 样的呢? 下面我将详细介绍下layout包里的XML文件的格式,至于每一个XML节点对应的属性,以及属性值描述能够参考下附录。
首先,一个应用程序应该有一个主窗口,所以咱们须要定义一个主窗口的模板,注意是模板并在其中定义一下这个窗口模板的一些属性。
主窗口的XML结构以下:
<xlue>
    <hostwndtemplate>   <!—宿主窗口模板-->
       …
    </hostwndtemplate>
    <objtreetemplate>  <!—对象树模板-->
       …
</objtreetemplate>
    <animationtemplate> <!—动画模板,可选项-->
       …
    </animationtemplate>
    <objtemplate>         <!—对象模板,可选项-->
       …
     </objtemplate>
</xlue>

示例以下:
<!—filename: Mainwnd.xml-->
<xlue>
<hostwndtemplate id=”MainWnd” class=”FrameHostWnd”>
<attr>
       <left>0</left>
       <top>0</top>
       <width>1280</width>
       <height>800</height>
        …
</attr>
<eventlist>
     <event name=”OnCreate” file=”MainWnd.xml.lua”
func=” OnCreate” />
       …
</eventlist>
</hostwndtemplate>

</xlue>

上 面定义了一个主窗口模板,id为”MainWnd”,类型为”FrameHostWnd”,表示这个窗口是一个经常使用的主窗口类型,能够带有标题栏,系统菜 单等属性。属性列表里面指明了窗口原始的位置,左上顶点坐标(0,0),宽度高度分别为1280,800等其余信息。 如今主窗口定义好了,咱们须要把控件绘制到主窗口上,而咱们的控件都是挂载在对象树上的,所以,咱们得先定义一个对象树模板,示例以下:
<!—filename: Mainwnd.xml-->
<xlue>
   …
<objtreetemplate id="MainWndTree" class="ObjectTreeTemplate">
       <attr>
           <left>-200</left>
           <top>-200</top>
           <width>2000</width>
           <height>2000</height>
       </attr>
       <obj id="MainWnd.RootLayout" class="LayoutObject">
           <attr>
              <left>0</left>
              <top>0</top>
              <width>1280</width>
              <height>800</height>
           </attr>
           <children>
              <obj id=”MyEditControl” class=”MyEdit”>
                  <attr>
                     <Width>100</Width>
<Text>编辑框默认显示文字</Text>

                  </attr>
              </obj>
           </children>
           <eventlist>
              <event name=”OnMouseMove” file=”MainWnd.xml.lua”
func=”OnMouseMove”/>

           </eventlist>
       </obj>
</objtreetemplate>
</xlue>

上 面定义了一个对象树模板,id为”MainWndTree”,类型为”ObjectTreeTemplate”,里面包含一个根对 象”MainWnd.RootLayout”,全部的子标准对象或者子控件均可以挂在根对象 的<children></children>中。标准对象的XML结构比较简单,上面的例子就能够看出来,这里不作详细介绍, 另外标准对象模板的结构与标准对象一致。上面的示例中,根对象下挂了一个名为MyEditControl的控件对象,下面以该控件为例介绍下控件的XML 结构。
控件的XML结构以下:
   <!—filename: MyEdit.xml -->
<xlue>
<control class=" MyEdit ">
        <attr_def>   <!— 自定义属性列表,可选项-->
           …
        </attr_def>
       <method_def>  <!— 自定义方法列表,可选项-->
           …
        </method_def>
           …
        <event_def>  <!— 自定义事件列表,可选项-->
           …
       </event_def>
        <objtemplate> <!—控件根对象模板定义-->
           …
       </objtemplate>
    </control>
    
    <hostwndtemplate>   <!—宿主窗口模板,可选项-->
       …
    </hostwndtemplate>
    <objtreetemplate>   <!—对象树模板,可选项-->
       …
</objtreetemplate>
    <animationtemplate> <!—动画模板,可选项-->
       …
    </animationtemplate>
    <objtemplate>         <!—对象模板,可选项-->
       …
     </objtemplate>
     …
</xlue>

上面的结构图中能够看出控件XML的大体结构了。下面给一个较完整的控件的XML示例

<!-- filename: MyEdit.xml>
<xlue>
<control class="MyEdit">
        <attr_def>
            <attr name="NormalBkgID" type="string">
                <default>texture.edit.bkg.normal</default>
            </attr>
          
           <attr name="Text" type="string" />
            <attr name="Width" type="int" >
                <default>50</default>
            </attr>
            <attr name="ReadOnly" type="bool">
                <default>false</default>
            </attr>
           ...
        </attr_def>
        <method_def>
            <SetReadOnly file="MyEdit.xml.lua" func="SetReadOnly" />
         <GetReadOnly file="MyEdit.xml.lua" func="GetReadOnly" />

         <SetText file="MyEdit.xml.lua" func="SetText" />
         <GetText file="MyEdit.xml.lua" func="GetText" />
           ...
        </method_def>
        <event_def>
            <OnEditChange>
                <param>
                    <string/>
                </param>
            </OnEditChange>
      
           <OnEditKeyDown>
              <param>
                  <int/>
                  <int/>
                  <int/>
              </param>
           </OnEditKeyDown>
           ...
        </event_def>
        <objtemplate>
            <children>
                <obj id="newedit.bkg" class="TextureObject">
                    <attr>
                        <left>0</left>
                        <top>0</top>
                        <width>father.width</width>
                        <height>father.height</height>
                   </attr>
                    <children>
                     <obj id="edit.limit" class="LayoutObject">
                         <attr>
                            <left>0</left>
                            <top>0</top>
                            <width>father.width</width>
                            <height>father.height</height>
                            <limitchild>1</limitchild>
                         </attr>
                         <children>
                               <obj id="newedit.edit"
class="EditObject">
                                <attr>
                                   <left>0</left>
                                   <top>0</top>
                                   <width>father.width</width>
                                           <height>father.height
</height>
                                   <zorder>1</zorder>
                                   <transparent>1</transparent>
                                </attr>
                                <children>
                                </children>
                                <eventlist>
                                        <event name="OnChange"
file="MyEdit.xml.lua" func="Edit__OnChange"/>
                                   <event name="OnKeyDown"
file="MyEdit.xml.lua" func="Edit_OnKeyDown"/>
                                   ...
                                </eventlist>
                            </obj>
                         </children>
                     </obj>
                    </children>
                  <eventlist>
                   <event name="OnMouseMove"
file="MyEdit.xml.lua" func="OnMouseMove"/>
                   <event name="OnMouseLeave"
file="MyEdit.xml.lua" func="OnMouseLeave"/>
                   <event name="OnFocusChange"
file="MyEdit.xml.lua" func="OnFocusChange"/>
                     ...
                  </eventlist>
                </obj>
            </children>
            <eventlist>
                <event name="OnBind" file="MyEdit.xml.lua"
func="OnBind" />
                <event name="OnInitControl" file="MyEdit.xml.lua"
func="OnInitControl"/>
              <event name="OnFocusChange" file="MyEdit.xml.lua"
func="Control_OnFocusChange"/>
              ...
            </eventlist>
        </objtemplate>
    </control>
<xlue>


上 面这个例子中就是一个完整控件的实现,因为篇幅限制,我省略了许多内容,可是从上面的描述中,你能够大体知道一个控件的XML大体是什么结构了。使用 Lua脚本就能够很简单的使用主窗口模板建立出主窗口实例,而后,使用对象树模板建立对象树实例,而后将对象树实例绑定到主窗口实例上,而后使用主窗口实 例建立一个窗口,并把它显示出来。这样,咱们就建立并显示了一个带有简单编辑框的主窗口。lua脚本示例以下:
local templateMananger =
XLGetObject("Xunlei.UIEngine.TemplateManager")
local frameHostWndTemplate =
templateMananger:GetTemplate("MainWnd","HostWndTemplate")
if frameHostWndTemplate then
    local frameHostWnd =
frameHostWndTemplate:CreateInstance("MainFrame")
    if frameHostWnd then
       local objectTreeTemplate =
templateMananger:GetTemplate("MainWndTree",
"ObjectTreeTemplate")
       if objectTreeTemplate then
           local uiObjectTree =
objectTreeTemplate:CreateInstance("MainObjectTree")
           if uiObjectTree then
              frameHostWnd:BindUIObjectTree(uiObjectTree)
              frameHostWnd:Create()
           end
       end
    end
end

虽 然作控件提及来简单,作起来就没有那么简单了,须要了解许多XLUE引擎内置对象的类型,属性,事件等。附录中的文件中对这些有很详细的介绍。常用引 擎,就会熟能生巧,实现本身想要的控件就没那么难了。等到你能够熟练使用引擎开发控件,今后你就能够告别 MFC,WTL等大块头了。



XLUE XAR 相关介绍 1. XML与XML以及XML与lua之间的联系 1) XML与XML之间的联系 一 个XML里面定义的控件,能够在另一个XML中使用,甚至一个XAR包中定义的控件,能够在另一个XAR包中使用,这是怎么作到的呢?答案是经过查找 nametable.cfg文件。当咱们在XML中使用了一个在其余的XAR中定义的控件时,要保证这个XAR以前已经被加载过,引擎的XML解析模块在 解析该控件时,会经过查找模块在当前全部已加载的XAR包中查找这个控件的定义,首先查找XAR包中的nametable.cfg文件,找到对应的控件所 在的XML位置,若是找到则加载这个XML,若是找不到则继续下一个,直到查找完全部已加载的XAR若是找到对应的控件,并建立该控件的一个实例。可是如 果包含该控件的XAR没有被加载起来,也会出现找不到的时候,而使用XAR包含是解决这个问题的一个比较好的方法,在当前XAR中包含含有待使用控件的 XAR包。若是碰到有多个地方定义了同一个控件的状况,能够经过加XAR包名字空间来解决冲突的问题,假设当前在AddinXAR中声明一个在 MainXAR中定义的控件,那么能够按照<obj id=”edit1” class= ”MainXAR::MyEdit”/>这样的方式来作,其中MainXAR是包名,MyEdit是其中定义的控件名。一样,正常使用这个控件,需 要MainXAR包以前被加载起来,若是没有,你也能够在lua脚本加载插件XAR以前,手动加载MainXAR。XAR重复加载不会致使问题,内部会进 行判断,所以没必要担忧重复加载的问题。 2) XML与lua之间的联系 XML里面定义的控件属性在lua脚本中怎么获取到的 呢,事件,方法等又是怎么响应的呢?下面我来给你们讲解下,引擎内部在实现自定义控件的时候,在内部注册了控件的GetAttribute以及 SetAttribute方法,这两个方法均可以在lua脚本中直接使用。其中GetAttribute方法用于获取控件XML中定义 的<attr_def></attr_def>属性列表,SetAttribute方法用于设置新的属性列表,这个方法通常不多 会用到。XML中定义的<method_def></method_def>中的方法,会在使用的时候来查找方法名节点的属性 值,lua文件名以及lua函数名来肯定lua函数的位置,当在一个lua文件中调用另外的lua文件中定义的函数时,这个功能将起到做用,由于lua并 不能跨文件使用变量或者函数,除非在全局表中注册这些变量或者函数。假设咱们须要在MainWnd.xml对应的lua文 件:MainWnd.xml.lua中动态的对MyEdit控件的属性进行修改,能够这样作,    <!—filename:MainWnd.xml.lua--> function OnCreate(self)    local objTree = self:GetBindUIObjectTree()     获取宿主窗口上绑定的对象树      local edit = objTree:GetUIObject(”MainWnd.RootLayout:MyEditControl”) 获取对象树上MainWnd.RootLayout节点下的MyEditControl控件实例 edit:SetText(”编辑框实例”) 设置Text属性值 edit:SetReadOnly(true) 设置是否只读 … end         其中SetText,SetReadOnly是method_def中定义的子节点名,而不是子节点func属性中的名 字。<event_def></event_def>中定义的事件也是相同的道理,只是响应事件的时候,会以event节点的 name属性值做为查找的key,找到后,定位到其中的file,func属性描述的lua函数。 2.主窗口加载流程以及相关事件说明 前面讲了不少关于XAR的知识,我想你必定有兴趣知道XLUE引擎是怎么拉起一个窗口并显示内容的下面我来为你们讲解一下XAR的工做流程。咱们从上面的lua示例代码开始讲起, local templateMananger = XLGetObject("Xunlei.UIEngine.TemplateManager") 这句代码的做用是获取XLUE引擎内部的模板管理器对象,模板的建立,获取都须要经过这个对象 local frameHostWndTemplate = templateMananger:GetTemplate("MainWnd","HostWndTemplate") 这 句代码的做用是从模板管理器中查找ID为MainWnd,类型为HostWndTemplate的模板,若是模板管理器中不存在该模板,那么将会触发一个 XML中的查找操做,首先在nametable.cfg中查找,看有没有对应ID和类型的记录存在,若是存在,那么将取出记录里面的path字 段”layout\MainWnd.xml”, 并解析这个XML文件,解析XML文件的过程当中,会查找对应ID和类型的节点,找到则记录下该节点对象,并建立一个类型为 HostWndTemplate,ID为MainWnd的模板对象。 if frameHostWndTemplate then 获取模板对象可能会失败,因此这里作个判断     local frameHostWnd = frameHostWndTemplate:CreateInstance("MainFrame") 使用模板对象建立一个ID为MainFrame的实例,这个时候会根据以前记录下的节点对象,解析MainWnd节点的属性,并将属性值绑定到新建立出来的frameHostWnd对象中。 if frameHostWnd then 建立宿主窗口模板实例也可能会失败,因此这里加个判断        local objectTreeTemplate = templateMananger:GetTemplate("MainWndTree", "ObjectTreeTemplate") 与 上面搜索MainWnd节点模板相似,过程基本同样,不一样的是扫描对象树模板的时候发现有obj节点,会解析obj节点及其子节点,并把它加入到对象树 中,这个操做会依次触发obj的OnPosChange, OnBind,以及OnInitControl事件,这里有一个问题是当一个obj收到OnBind事件时,该obj可能尚未被加入对象树中,这个时候 是不能经过对象树管理器取到这个obj或者她的子obj的,当收到OnInitControl事件时,控件以及它的子对象或控件都已经加到对象树中,这时 再来操做就正常了,因此建议把控件初始化以及获取控件子对象位置信息等相关的操做放到OnInitContrl事件的响应函数中        if objectTreeTemplate then        获取模板对象可能会失败,因此这里作个判断            local uiObjectTree = objectTreeTemplate:CreateInstance("MainObjectTree") 建立一个ID为MainObjectTree的对象树实例,并将解析出来的属性值绑定到其中            if uiObjectTree then               建立对象树模板实例也可能会失败,因此这里加个判断               frameHostWnd:BindUIObjectTree(uiObjectTree)                         将对象树实例绑定到宿主窗口实例上,对象树实例和宿主窗口实例的 关系是一一对应的,这个时候会触发宿主窗口模版XML节点里面定 义的OnBind事件               frameHostWnd:Create()               真正的建立窗口函数,会触发宿主窗口模版XML节点里面定义的 OnCreate事件,今后进入操做系统的消息循环,用户在窗口上的操做会触发系统事件,能够在lua脚本中响应。            end        end     end 3. 建立控件的流程 前 面讲到XML文件中定义的模板和控件都是静态的配置信息,只有在经过lua脚本建立控件或者建立模板实例的时候才会去获取并解析其中的XML节点配置信 息,解析的过程当中会建立出来相应类型的对象。下面讲解下详细的建立控件流程,XLUE引擎提供的建立控件最方便的办法是使用对象工厂类。对象工厂类是引擎 内置的类,进行过lua封装。下面,咱们以MyEdit.xml中定义的控件为例,介绍下如何动态建立一个控件实例,以及建立的流程。方法以下: local objFactory = XLGetObject(”Xunlei.UIEngine.ObjectFactory”) 获取XLUE引擎内置的对象工厂类 local edit= objFactory:CreateUIObject(”MyEdit.Instance”,”MyEdit”) 建立id为MyEdit.Instance,类型为MyEdit的控件对象实例,建立出来的实例拥有MyEdit控件中定义的全部属性,方法以及事件响应。 那 么CreateUIObject是怎么作到建立一个控件的呢,MyEdit控件的定义可能在任何一个XML文件中,引擎么找到它的呢?答案一样是根据 nametable.cfg文件。建立对象时会在全部已载的XAR中的nametable.cfg文件中查找MyEdit控件的定义,若是没找到名字为 MyEdit的控件,则返回nil,若是找到,则加载对应的XML进行解析,建立一个控件实例,并找到XML中控件的objtemplate 子节点,而后解析objtemplate下的children子节点以及eventlist子节点,并将其中的内容挂靠到控件实例上,这样,一个控件实例 就被建立出来了,建立出来的控件实例是不会被自动添加到对象树上的,所以若想让控件显示出来,须要把他添加到对象树中,完整的建立控件代码以下:      <!—filename:MainWnd.xml.lua--> function OnCreate(self)    local objTree = self:GetBindUIObjectTree()      获取宿主窗口上绑定的对象树 local objFactory = XLGetObject(”Xunlei.UIEngine.ObjectFactory”) 获取XLUE引擎内置的对象工厂类 local edit= objFactory:CreateUIObject(”MyEdit.Instance”,”MyEdit”) edit:SetText(”编辑框实例”) 设置Text属性值 edit:SetReadOnly(true) 设置是否只读 local root = objTree:GetUIObject(”MainWnd.RootLayout”) 获取对象树上MainWnd.RootLayout节点 root:AddChild(edit) 将新建立的edit控件实例添加到对象树中,做为root对象的子节点对象 edit:SetObjPos(100,100,200,20) 设置edit控件实例的位置,左上顶点坐标(100,100)为相对于父节点对象root 的坐标,宽度为200,高度为20 end       经过上面的代码,咱们已经成功地建立了一个控件,而且将他展现在主窗口上。 4. XAR的编译以及nametable.cfg文件的说明 按 照前面所说,nametable.cfg文件在不少时候都会被用到,那么它是怎么来的呢,答案是XAR编译生成,XAR编译的目的是为了检查XML和 lua文件里面的语法错误,以及XML脚本中不符合模板或者控件编写规范的问题。编译XAR的时候,咱们会检查全部的XML以及lua文件,若是发现其中 存在错误,那么会给出提示,若是没有错误,则会生成nametable.cfg文件,这个过程当中并不会对XML以及lua脚本执行编译操做。 nametable.cfg文件记录了当前XAR中全部的二级模板,控件节点,即<xlue>节点下定义的模板,控件节点的 id,type,class以及所在路径path信息。type表示节点对应的XLUE引擎中的类型,id和type一块儿能够用来定位模板或者控 件,path信息是用来指示对应的XML文件路径的,class字段记录模板或者控件class信息,暂时没有用到 5. XAR中的资源获取 XAR 包中res目录下的资源包下定义的资源XML,能够经过XAR提供的接口在lua脚本中得到,获取资源时会先从本XAR中查找,若是查找不到,则会尝试从 包含的XAR包对应的资源包中查找一级一级往上查找,找完全部的包含XAR包,直到找到对应的资源。若是最后仍是找不到,会返回失败。关于资源的介绍,详 见XLUE XAR资源介绍文档
相关文章
相关标签/搜索