Duilib學習筆記《02》— 界面布局



1. 界面描述XML文件

Duilib主要是通過XML來進行界面的布局配置,程序通過讀取並解析XML文件來創建對應的窗體。DuiLib的頁面布局分為三類:窗體(Window)、容器(Contain)和控件(Control)。顧名思義窗體就是要創建的窗口,容器則相當於是窗體內的一個子窗體,可以在容器內添加容器或者控件,當然定義的位置也都是相對與容器內的左上頂點;控件就是一些常用的Button、Edit、Label等窗體上的基本元素。

容器經常使用的有VerticalLayout(垂直布局容器)、HorizontalLayout(水平布局容器)、TabLayout(頁標簽布局容器)、RichEdit(富文本框)、Combo(下拉文本框)、List(列表)

控件經常使用的有Label(標簽)、Button(按鈕)、Option(選擇框)、Edit(文本框)、ScrollBar(滾動條)等等。

首先根節點必須是Window,這個表示窗體,然后在跟節點內可以添加內容。各節點可以添加屬性,屬性包含 名字、位置、大小、背景色、前景色、背景圖片、顯示文本、鼠標懸浮提示等等。(注:在duilib中有一份”屬性列表.xml”的文件,詳細羅列了每個空間對應的屬性,方便使用時查閱)

 
 

2. 簡單空白窗體界面

此處以創建一個簡單的空白的灰色背景窗體為例。對應的XML布局文件對應的也就很簡單。如下:

<?xml version="1.0" encoding="UTF-8"?>
<Window size="800,600" roundcorner="4,4"> 
    <VerticalLayout bkcolor="#AAA0AAA0"> 
    </VerticalLayout>
</Window>

根據字面意思可以很容易看出XML文件所表示的窗體屬性,窗體大小(size)為800X600,窗口圓角大小(roundcorner)為(3,3)等等。

接下來,創建DuilibDemo程序來讀取解析該XML文件創建對應的窗體(注:對應的具體實現代碼暫不作具體解釋,在筆記最后會給出配對的代碼方便下載查閱。本節主要是針對XML窗體布局部分,具體代碼如何顯示后續會具體單獨詳解),效果如下:

  Duilib Empty Window
 

3. 標題欄創建

通過第二步中創建的簡單空白窗體,可能發現最終窗體效果和MFC方式創建的並沒什么太大區別。因為上述簡單窗體的創建只是讀取解析XML然后創建對應的窗體,具體的相關消息流程都暫未做處理。所以,接下來,我們通過做一個標題欄的創建來演示說明。
3.1 屏蔽系統標題欄 

在此之間,我們得屏蔽掉系統標題欄。在消息處理函數中,我們通過在消息處理函數HandleMessage中對消息WM_NCACTIVATE、WM_NCCALCSIZE、WM_NCPAINT處理來屏蔽系統標題欄,具體屏蔽消息處理代碼如下(可在配對的代碼中查看):

LRESULT CMainWndDlg::OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if( ::IsIconic(*this) ) bHandled = FALSE;
    return (wParam == 0) ? TRUE : FALSE;
}
LRESULT CMainWndDlg::OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    return 0;
}
LRESULT CMainWndDlg::OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    return 0;
}

這樣之后運行就會得到一個不帶系統標題欄的灰色空白窗體。

 
3.2 創建自繪標題欄

屏蔽系統標題欄之后,接下來就可以創建自繪標題欄了。其實創建自繪標題欄不需要額外修改程序代碼部分,只需要在XML中添加標題欄Caption部分的布局即可。對於標題欄,我們所熟知的主要是分為兩部分:左上角的title和右上角的系統按鈕。再加上標題欄本身占有一部分區域,而且在該區域可以支持鼠標拖動窗體的,所有在原有的xml文件基礎上對應的我們需要添加修改的地方有三處:

3.2.1)區域大小聲明。在創建窗體的時候根據需要提前指定窗體可拖動標題欄大小邊距。

<Window size="800,600" caption="0,0,0,64" roundcorner="4,4">

3.2.2)Title區域

<HorizontalLayout name="captionTitle" childpadding="6"> 
    <Control width="10" />    <!-- 占空位,占據左邊10個單位大小空位 -->
    <VerticalLayout>
        <Control height="20" />
        <Label text="Demo演示窗體" textcolor="#FF447AA1" width="200"  />
    </VerticalLayout>
</HorizontalLayout>

3.2.3)系統按鈕區域

<HorizontalLayout name="captionSysBtn" width="126" height="24" inset="0,1,0,0">
    <Button name="menuBtn" maxwidth="26" maxheight="17" normalimage="file='sys_dlg_menu.png' source='52,0,78,17'" hotimage="file='sys_dlg_menu.png' source='26,0,52,17'" pushedimage="file='sys_dlg_menu.png' source='0,0,26,17'"/>
    <Button name="minBtn" maxwidth="26" maxheight="17" normalimage="file='sys_dlg_min.png' source='52,0,78,17'" hotimage="file='sys_dlg_min.png' source='26,0,52,17'" pushedimage="file='sys_dlg_min.png' source='0,0,26,17'"/>
    <Button name="maxBtn" maxwidth="26" maxheight="17" normalimage="file='sys_dlg_max.png' source='52,0,78,17'" hotimage="file='sys_dlg_max.png' source='26,0,52,18'" pushedimage="file='sys_dlg_max.png' source='0,0,26,17'"/>
    <Button name="restoreBtn" visible="false" maxwidth="26" maxheight="17" normalimage="file='sys_dlg_restore.png' source='52,0,78,17'" hotimage="file='sys_dlg_restore.png' source='26,0,52,17'" pushedimage="file='sys_dlg_restore.png' source='0,0,26,17'" />
    <Button name="closeBtn" maxwidth="45" maxheight="17" normalimage="file='sys_dlg_close.png' source='90,0,135,17'" hotimage="file='sys_dlg_close.png' source='45,0,90,17'" pushedimage="file='sys_dlg_close.png' source='0,0,45,17'"/>
</HorizontalLayout>

注意:為了使界面更加美觀,引入了一些圖片資源。比如窗體背景、按鈕圖片等等。具體使用方法很簡單,參考代碼使用即可。雖然界面效果達到了,但細心的人可能會發現,鼠標點擊標題欄區域時還是會彈出系統自帶的菜單等。這是因為我們目前只是在界面上達到了屏蔽了系統自帶標題欄,並自繪標題欄的效果,但消息的處理還沒改變。所以此處還需要添加對點擊等操作的消息處理,即在HandleMessage中添加對消息WM_NCHITTEST的處理。對應OnNcHitTest分支下的處理函數如下:

LRESULT CMainWndDlg::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam);
    ::ScreenToClient(*this, &pt);
    RECT rcClient;
    ::GetClientRect(*this, &rcClient);
    RECT rcCaption = m_PaintManager.GetCaptionRect();
    if( pt.x >= rcClient.left + rcCaption.left && pt.x < rcClient.right - rcCaption.right \
        && pt.y >= rcCaption.top && pt.y < rcCaption.bottom ) {
            CControlUI* pControl = static_cast<CControlUI*>(m_PaintManager.FindControl(pt));
            if( pControl && _tcscmp(pControl->GetClass(), _T("ButtonUI")) != 0 && 
                _tcscmp(pControl->GetClass(), _T("OptionUI")) != 0 &&
                _tcscmp(pControl->GetClass(), _T("TextUI")) != 0 )
                return HTCAPTION;
    }
    return HTCLIENT;
}

這樣一來也就達到了預期的效果。當然,這只是最簡單的界面效果,想要得到復雜的界面效果,首先還需要根據實際需要在界面添加相關控件繪制等等。具體的布局可以直接在上述XML文件中繼續添加完善;其次,還需要對界面一些控件的消息響應的處理,具體消息效應會在后續章節提到。上述布局完成后對應的效果如下:

Duilib Demo Window

4. UIDesigner

Duilib中實際上提供了所見即所得的窗體設計器UIDesigner。如下圖所示:

Duilib UIDesigner

對於習慣了MFC對話框中直接拖控件來布局的人來說或許很喜歡這個設計器。這個設計器同樣也是可以直接拖放相關控件來完成布局,最終保存會自動生成對應的XMl文件。如果熟悉了XML布局后,實際上手寫起來或許會更方便,而且對於一些復雜的界面布局來說,手動寫XML文件應該比用該設計器要方便的多。


5. 補充說明

1)上述的布局只是簡單的布局,在布局中很多控件的屬性可以參考下載的duilib中的“屬性文件.xml”中羅列的信息。

2)全局屬性。在上述最終的Demo圖片中可以發現字體和默認的有些不一樣,實際上是進行了相關設置。對於字體、Default之類的的屬性設置具體參考例子代碼:

<Font name="宋體" size="13" bold="true" />
<Font name="宋體" size="12" bold="true" underline="true"/>
<Font name="宋體" size="12" />
<Font name="宋體" size="22" bold="true"/>

這里我們定義了四種字體樣式,序號默認從0開始依次遞增。而要具體使用時,如Demo中標題欄的字體設置:

<Label text="Demo演示窗體" textcolor="#FF447AA1" width="200" font="3" />

這里font=”3″就表示Label中的文字使用序號3對應的<Font name=”宋體” size=”22″ bold=”true”/>這種樣式。

3)布局這塊,上述只是簡單的一個布局,引導大家熟悉。對於如何更好的學會布局,一方面可以隨着后續深入學習,進一步熟悉相關控件及屬性后,要能靈活運用大到實際例子中;另一方面,一個很好的方法就是查看一些例子,通過例子來學習。對於設計好的布局,可以直接通過UIDesigner來打開XML文件可以很方便的即時查看界面樣例。

最后附上本節對應的代碼(說明,后續章節都是基於此代碼逐步完善)。代碼下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM