感謝迅雷公司程序員: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資源介紹文檔