了解 XML 使用 (布局控制)
通過上一篇的學習我們可以制作一個簡單的布局了,但是沒有控件的視窗做再好的布局有什麼用呀。趕緊找些素材,我們來做一個标準的 Windows 視窗。并通過這個視窗我們來了解一下布局中一些細微細節的控制。
一個正常的 Windows 視窗應該有一個像樣的标題欄,标題欄左側包含視窗的 Logo 和視窗的名稱,右側有最小化、最大化和關閉按鈕,還有一些視窗可能會有設定啊等等類似的按鈕。我們到網絡上找一些素材,親自動手制作一個視窗。素材我們可以到 Iconfont-阿裡巴巴矢量圖示庫 去自己下載下傳。通過搜尋功能搜尋一個你喜歡的圖檔,比如我搜尋一個 Logo,會出來很多可用的圖形,随便點選一個進去後,可以設定圖形的顔色:
我下載下傳了一個 PNG 小圖檔,大小是 18x18 像素,藍色。将圖檔儲存到項目的 theme 檔案夾下。
然後我們編輯 main_wnd_frame.xml,删除掉原來的 TileLayout 中所有内容,重新做一個布局,如下所示:
通過分析我們可以判斷,最外部應該使用一個垂直布局
VerticalLayout
,因為标題欄和下面視窗内容區域是一個從上到下的垂直布局。而标題欄裡面是從左到右的一個水準布局
VerticalLayout
。至于下方内容區域,我們暫時先不用關心,先把這個标題欄做好,是以我們還是使用一個水準布局。那麼根據思路你可以先不看下面代碼,自己試着寫一個視窗的布局,算是對自己的一個鍛煉。記得标題欄是有固定高度的,可以通過
height
屬性指定
HorizontalLayout
的高度。
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
<VerticalLayout>
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9">
<Control bkimage="Logo.png" height="18" width="18"/>
<Label text="duilib tutorial" />
</HorizontalLayout>
<!-- 視窗内容區域 -->
<HorizontalLayout bkcolor="#FF4D6082">
</HorizontalLayout>
</VerticalLayout>
</Window>
以上代碼運作後,效果如下:
可以看到,我們給視窗分割了兩個區域,一個标題欄,一個視窗内容區域,内容區域現在還沒有任何内容。但是标題欄已經增加了一個 Logo。其中标題欄我們用水準布局
HorizontalLayout
來實作,用
height="35"
指定了标題欄的高度,用
bkcolor
屬性指定了标題欄的背景色。接着标題欄内部我們使用
Control
控件包裝的 Logo 圖像,使用
bkimage="Logo.png"
指定了圖檔檔案的位置,并同樣使用
height
、
width
設定了寬度和高度。這些屬性在 DuiLib 屬性清單.xml 中都是可以找到的。大家可以翻一番。
但是大家可能發現了,Logo 的位置不是很好,他僅僅的挨在視窗的左上角,我們應該調整一下它的位置,讓它居中顯示。不過很不幸 DuiLib 好像并沒有居中功能,想實作居中功能要通過布局來功能來實作,代碼遠遠比指定一個屬性要多的多。我們不通過布局方法實作難道就沒有方法了嗎?
内外邊距
标題欄高度是 35,而 Logo 的高度是 18,我們讓 Logo 圖像的上方和左側分别向下和向右移動 7~9 像素,這樣 Logo 就可以居中顯示了。通過 padding 屬性可以指定容器的外邊距,這裡要注意,不像 Web 前端一樣使用的是
margin
,剛切換過來的朋友可能容易出錯,是以要尤其注意。我們通過指定 padding=“8,8,0,0” 的方式,指定了 Logo 檔案左邊和上邊分别有 4 個像素的邊距,代碼如下:
.....
<Control bkimage="Logo.png" height="18" width="18" padding="8,8,0,0"/>
.....
此時再看一下效果基本上是上下居中顯示了,而且左側也與邊緣有了寫距離,看上去好看一些了。要注意,
padding
的 4 個參數順序分别是 左、上、右、下。與 Web 前端 CSS 還不一樣,是以大家千萬不要弄混了。
再注意 Logo 和标題文字是不是挨的有點近?那麼我們再給 Logo 指定一個右側的邊距,讓标題“離她遠點”。
.....
<Control bkimage="Logo.png" height="18" width="18" padding="8,8,8,0"/>
.....
可以看到通過 padding 的設定我們可以實作讓容器具有外邊距效果,同樣有外邊距也會有内邊距,我們設定标題欄容器的内邊距同樣可以實作讓内容居中的方法。内容如下:
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9" inset="8,8,8,0">
<Control bkimage="Logo.png" height="18" width="18" />
<Label text="duilib tutorial" height="18" padding="8"/>
</HorizontalLayout>
我們使用
inset
屬性給外部的水準布局容器設定了左側、上方、右側分别 8 個像素的内邊距,由于 Label 預設是填滿整個容器的,是以我們要給他設定一個高度,否則會被擠下來,而且我們删除掉了 Logo 的
padding
屬性,Logo 與标題之間的間距也不在了。是以這次換個方式,我們給标題文字指定了一個
padding="8"
的屬性,你會發現,如果我們不需要指定上、右、下的其他幾個屬性,我們僅指定填寫第一個左側的資料就可以了。這樣可以讓代碼比較簡練。效果如下:
浮動
除了上面的内外邊距可以實作這個效果以外,我們還可以通過讓控件
浮動起來
的方式,通過指定一個 pos 屬性讓它想在哪裡就在哪裡。但前提是你要通過 float 屬性指定控件是一個浮動的控件。代碼如下:
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9">
<Control bkimage="logo.png" height="18" width="18" float="true" pos="8,8,26,26" />
<Label text="duilib tutorial" height="18" float="true" pos="35,8,185,26"/>
</HorizontalLayout>
去掉了外部水準布局容器的内邊距後,我們讓
Control
控件和
Label
控件都成為浮動控件,并指定他們的具體坐标。注意 pos 的第三個和第四個參數,他們決定了控件的右上角位置和右下角位置。要算上控件本身的大小,比如例子中的 Label 控件第三個參數 185,是算上了控件左側的 35 + 控件自身寬度的總和,如下圖所示:
這種方式雖然能實作功能,但是代碼寫起來繁瑣,不容易維護,要調整一個位置要修改好多地方,非常容易出錯。是以除非必要,還是建議大家少用這個浮動的功能。
接下來我們來把右側的幾個功能按鈕也做一下,我們還是去找一下素材。在 Iconfont-阿裡巴巴矢量圖示庫 搜尋 min、max、close 等關鍵字就可以搜尋到相關素材。在找素材的時候大家請注意,由于我們按鈕需要三種狀态的圖檔,一個是普通樣式的,一個是滑鼠懸浮狀态的圖檔,一個是滑鼠按下時的效果,三種效果我們都要找,為了差別不同效果,我使用了不同的顔色。你們也可以根據自己的需要設定不同的顔色,我分别給普通、懸浮狀态和按下狀态的圖檔命名為
btn_*_normal.png
、
btn_*_hovered.png
、
btn_*_pushed.png
,如下圖:
由于實在沒有找到還原視窗的合适圖檔,是以臨時找個了帶了兩個箭頭的圖檔頂替了(論身邊有個視覺是多麼美妙的事情),4 種按鈕預設隻顯示最小化、最大化和關閉,還原按鈕隻有在最大化的情況下才顯示,是以預設我們讓他不顯示,最終代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
<VerticalLayout>
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9" inset="8,8,8,0">
<Control bkimage="logo.png" height="18" width="18" />
<Label text="duilib tutorial" height="18" padding="8"/>
<HorizontalLayout childpadding="3" width="60">
<Button height="18" width="18" normalimage="btn_min_normal.png" hotimage="btn_min_hovered.png" pushedimage="btn_min_pushed.png" />
<Button height="18" width="18" normalimage="btn_max_normal.png" hotimage="btn_max_hovered.png" pushedimage="btn_max_pushed.png" />
<Button height="18" width="18" normalimage="btn_restore_normal.png" hotimage="btn_restore_hovered.png" pushedimage="btn_restore_pushed.png" visible="false"/>
<Button height="18" width="18" normalimage="btn_close_normal.png" hotimage="btn_close_hovered.png" pushedimage="btn_close_pushed.png" />
</HorizontalLayout>
</HorizontalLayout>
<!-- 視窗内容區域 -->
<HorizontalLayout bkcolor="#FF4D6082">
</HorizontalLayout>
</VerticalLayout>
</Window>
- 使用
屬性指定了右側三個按鈕水準布局的子控件左右間距(如果是垂直布局那麼就是上下間距)childpadding
- 使用
屬性設定控件的預設圖檔樣式normalimage
- 使用
屬性設定控件的滑鼠懸浮狀态圖檔hotimage
- 使用
屬性設定滑鼠按下狀态圖檔pushedimage
- 使用
屬性設定還原控件的預設顯示狀态(見還原按鈕的最後一個屬性)visible
重新運作一下程式,我們可以看到三個按鈕已經在右上角了。
當我們滑鼠按下關閉按鈕時,顔色會變為紅色
占位符
因為我們給右側三個按鈕的水準布局容器設定了固定寬度,并且他們左邊的 Label 控件是預設拉伸占滿整個容器的狀态,是以三個按鈕的容器預設就被 “擠” 到最右側去了。如果左邊我們也設定了一個預設寬度的容器,右側關閉按鈕也被設定為固定寬度。那麼布局就不是這樣了,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
<VerticalLayout>
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9" inset="8,8,8,0">
<HorizontalLayout width="185">
<Control bkimage="logo.png" height="18" width="18" />
<Label text="duilib tutorial" height="18" padding="8"/>
</HorizontalLayout>
<HorizontalLayout childpadding="3" width="60">
<Button height="18" width="18" normalimage="btn_min_normal.png" hotimage="btn_min_hovered.png" pushedimage="btn_min_pushed.png" />
<Button height="18" width="18" normalimage="btn_max_normal.png" hotimage="btn_max_hovered.png" pushedimage="btn_max_pushed.png" />
<Button height="18" width="18" normalimage="btn_restore_normal.png" hotimage="btn_restore_hovered.png" pushedimage="btn_restore_pushed.png" visible="false"/>
<Button height="18" width="18" normalimage="btn_close_normal.png" hotimage="btn_close_hovered.png" pushedimage="btn_close_pushed.png" />
</HorizontalLayout>
</HorizontalLayout>
<!-- 視窗内容區域 -->
<HorizontalLayout bkcolor="#FF4D6082">
</HorizontalLayout>
</VerticalLayout>
</Window>
此時如果想讓三個按鈕靠右顯示,我們可以使用 占位符
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
<VerticalLayout>
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9" inset="8,8,8,0">
<HorizontalLayout width="185">
<Control bkimage="logo.png" height="18" width="18" />
<Label text="duilib tutorial" height="18" padding="8"/>
</HorizontalLayout>
<Control />
<HorizontalLayout childpadding="3" width="60">
<Button height="18" width="18" normalimage="btn_min_normal.png" hotimage="btn_min_hovered.png" pushedimage="btn_min_pushed.png" />
<Button height="18" width="18" normalimage="btn_max_normal.png" hotimage="btn_max_hovered.png" pushedimage="btn_max_pushed.png" />
<Button height="18" width="18" normalimage="btn_restore_normal.png" hotimage="btn_restore_hovered.png" pushedimage="btn_restore_pushed.png" visible="false"/>
<Button height="18" width="18" normalimage="btn_close_normal.png" hotimage="btn_close_hovered.png" pushedimage="btn_close_pushed.png" />
</HorizontalLayout>
</HorizontalLayout>
<!-- 視窗内容區域 -->
<HorizontalLayout bkcolor="#FF4D6082">
</HorizontalLayout>
</VerticalLayout>
</Window>
同樣的代碼我們隻是在兩個水準布局中間增加了一個空的 Control 控件,它會根據父容器的寬度無限拉伸,由于兩側的兩個水準布局容器已經設定了固定寬度,那麼它會 “野蠻” 的把剩下所有控件都占掉。這就是所謂的 占位符。如果你需要實作一個緊貼頂部和緊貼底部的容器,那麼你可以使用垂直布局方式,中間同樣增加一個占位符來實作需求。
到這裡我們已經基本上把所有布局相關的内容都說的差不多了,如果還有更重要的内容我會一點點在補充。大家發現,除了之前我們建構視窗時寫了一部分代碼,剩下的内容都是我們通過 XML 來實作的,并沒有敲一行多餘的代碼。這就是 DuiLib,界面和業務有很大的分離性。接下來我們該看看怎麼響應按鈕的點選事件了。
預設樣式
DuiLib 在 XML 文法中提供了一些預設樣式功能,我們可以給指定控件預設一些預設的樣式,當建立這種控件的時候,預設樣式就會在其上面展現。比如我們希望所有按鈕都有一個邊框。那麼可以像下面這樣來編寫 XML
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
<Default name="Button" value="bordersize="1" bordercolor="#FF222222"" />
<VerticalLayout>
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9" inset="8,8,8,0">
<HorizontalLayout width="185">
<Control bkimage="logo.png" height="18" width="18" />
<Label text="duilib tutorial" height="18" padding="8"/>
</HorizontalLayout>
<Control />
<HorizontalLayout childpadding="3" width="60">
<Button name="minbtn" height="18" width="18" normalimage="btn_min_normal.png" hotimage="btn_min_hovered.png" pushedimage="btn_min_pushed.png" />
<Button name="maxbtn" height="18" width="18" normalimage="btn_max_normal.png" hotimage="btn_max_hovered.png" pushedimage="btn_max_pushed.png" />
<Button name="restorebtn" height="18" width="18" normalimage="btn_restore_normal.png" hotimage="btn_restore_hovered.png" pushedimage="btn_restore_pushed.png" visible="false"/>
<Button name="closebtn" height="18" width="18" normalimage="btn_close_normal.png" hotimage="btn_close_hovered.png" pushedimage="btn_close_pushed.png" />
</HorizontalLayout>
</HorizontalLayout>
<!-- 視窗内容區域 -->
<HorizontalLayout bkcolor="#FF4D6082">
</HorizontalLayout>
</VerticalLayout>
</Window>
我們增加了一行
<Default name="Button" value="bordersize="1" bordercolor="#FF222222"" />
設定其 name 屬性為 “Button”,value 屬性為樣式描述,因為是作為 XML 的值來使用,是以雙引号等要做一下轉義。這也操作後,所有按鈕都具備了
"bordersize="1" bordercolor="#FF222222"
兩個屬性。如下所示:
全局字型
像預設屬性一樣,全局字型也是可以讓多個控件使用的一個屬性,不過控件可以決定是否使用這個屬性,而上面介紹的預設屬性是強制的。使用方法如下:
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
<Font shared="true" id="1" name="微軟雅黑" size="18" />
<VerticalLayout>
<!-- 标題欄 -->
<HorizontalLayout height="35" bkcolor="#FFD6DBE9" inset="8,8,8,0">
<HorizontalLayout width="185">
<Control bkimage="logo.png" height="18" width="18" />
<Label text="duilib tutorial" height="18" padding="8" font="1"/>
</HorizontalLayout>
<Control />
<HorizontalLayout childpadding="3" width="60">
<Button name="minbtn" height="18" width="18" normalimage="btn_min_normal.png" hotimage="btn_min_hovered.png" pushedimage="btn_min_pushed.png" />
<Button name="maxbtn" height="18" width="18" normalimage="btn_max_normal.png" hotimage="btn_max_hovered.png" pushedimage="btn_max_pushed.png" />
<Button name="restorebtn" height="18" width="18" normalimage="btn_restore_normal.png" hotimage="btn_restore_hovered.png" pushedimage="btn_restore_pushed.png" visible="false"/>
<Button name="closebtn" height="18" width="18" normalimage="btn_close_normal.png" hotimage="btn_close_hovered.png" pushedimage="btn_close_pushed.png" />
</HorizontalLayout>
</HorizontalLayout>
<!-- 視窗内容區域 -->
<HorizontalLayout bkcolor="#FF4D6082">
</HorizontalLayout>
</VerticalLayout>
</Window>
我們添加了一行
<Font shared="true" id="1" name="微軟雅黑" size="18" />
并在标題的 Label 中增加了一個
Font="1"
的屬性,用意就是讓這個 Label 使用 Font 編号為 1 的字型。而 Font 通過 id 屬性指定了編号。這樣再運作程式後,窗體标題就變成了微軟雅黑 18 大小的字型。
Font 有如下屬性可以使用
- name:字型名稱
- size:字型大小
- bold:粗體
- italic:斜體
- underline:下劃線
- id:字型的編号
- shared:是否共享