天天看點

duilib開發基礎:建立自定義控件的過程

       用Duilib開發界面時,很多情況下庫自帶的控件不滿足需求,就需要基于Duilib建立自定義控件(自繪新的控件,或者用來封裝win32的子窗體,來顯示視訊、網頁等)。

       在群裡經常會有剛接觸Duilib的朋友問題怎麼建立自己的自定義控件,或者建立的控件無法正常建立出來。我簡單寫一篇部落格,把建立自定義控件的完整過程,和一些注意事項說明一下。另外說一下如果把win32的子窗體封裝為控件,希望能有幫助。

       建立自定義控件包含兩個過程:

       1、繼承現有的控件類建立新的控件類

       2、讓程式識别新的控件并可以在xml中使用

建立新的控件類:

       首先從的現有的Duilib控件中選擇一個最合适的控件類,繼承他然後重寫幾個接口。

duilib開發基礎:建立自定義控件的過程
duilib開發基礎:建立自定義控件的過程

      為了做出換膚控件,首先選擇CButtonUI為父類,因為CButtonUI控件本身就已經包含了normal、hot、pushed等狀态,同時包含單擊事件。

       新的控件名為CSkinPickerPictureItemUI。一般來說,建立新控件後,最先應該重寫的兩個函數是GetClass和GetInterface。他們後用來區分控件的類型的虛函數,用于動态識别控件類型和做控件的類型轉換。

       從Duilib的自帶控件上可以看出,比如目前的自定義控件類名為CSkinPickerPictureItemUI,那麼GetClass函數傳回的字元串SkinPickerPictureItemUI。而GetInterface函數是根據傳入的參數,是否與自身的字元串比對,來決定能否把自己轉換為需要的控件類型。GetInterface中用來比對的字元串,應該與xml中的對應的控件的标簽名稱一直,這裡應該是SkinPickerPictureItem。

      比如CButtonUI類,GetClass對應ButtonUI,GetInterface對應Button。這不是強制的,但是保持這個風格很重要!

      理論上,完成這兩個接口就建立好最基本的自定義控件了。但是為了讓自定義控件的行為和外觀更豐富,就需要重寫更多的函數了,我這裡把經常會重寫的函數說明一下!

       以上列出的函數,是最常被重寫的。

       DoEvent函數:控件的核心函數,他是消息處理函數,用來處理Duilib封裝過的各個消息,比如滑鼠的移入移出、出現的懸停、單擊輕按兩下、右擊、滾輪滑動、擷取焦點、設定光标等等。是以如果你的控件需要修改這些行為,必須重寫這個函數,具體的處理方法可以參考Duilib現有的控件或者仿酷狗程式。

       DoPaint函數:控件的核心函數,他是控件的繪制處理函數,當Duilib底層要重新繪制這個控件,或者控件自己調用Invalidata函數強制自己重新整理時,這個函數就會被觸發,在這個函數裡完成了各種狀态下的背景前景繪制,背景色繪制,文本繪制,邊框繪制。而這個函數會調用PaintBkColor、PaintBkImage、PaintStatusImage、PaintText、PaintBorder等函數來完成各個繪制步驟。是以你可以根據需求來決定重寫DoPaint或者隻重寫某幾個PaintXXX函數。DoPaint函數經常和DoEvent函數結合使用,DoEvent得到了某個消息後,改變控件的狀态,然後調用Invalidate函數讓控件重繪。

      SetAttribute函數:用于擴充自定義控件的屬性,Duilib的控件本身已經包含name、text、bkimage等屬性,如果要增加新屬性,就需要重寫此函數來擴充屬性,上面的CSkinPickerPictureItemUI例子中已經有用法了。

      DoInit函數:當控件被添加到容器後,由容器調用的函數。在這裡,整個Duilib程式架構已經完成,當需要做一些界面的初始操作時可以重寫此函數,常見的用法就是在此建立Win32子窗體并且封裝它,相關内容我在後面再說。

     IsVisible、SetVisible、SetInternelVisible、SetPos:這幾個函數同樣也是,當控件封裝了Win32子視窗後,重寫這幾個函數來控制子視窗的顯示和隐藏、和位置。

      這樣就建立完成了自定義控件。

識别新控件:

       自定義控件建立完畢後,需要做的就是讓控件可以被xml布局識别出來。為此我們需要完成Duilib的IDialogBuilderCallback接口,重寫這個接口中的CreateControl函數。

       通常情況下,可以讓窗體類繼承IDialogBuilderCallback接口并且重寫CreateControl(DuiLib自帶的WindowImplBase窗體類已經繼承了這個接口,如果是繼承WindowImplBase的話就直接重寫CreateControl就可以了)。函數處理方法是比較傳入的字元串,根據字元串來決定傳回什麼控件的指針,這個傳入的字元串就是xml檔案中控件的标簽,比如<Button />中的字元串Button。

      習慣上,在xml中自定義控件的标簽名稱應該和控件的GetInterface中的判斷字元串一緻,比如這裡就是SkinPickerPictureItem。這樣,在解析xml過程中,當解析到名為SkinPickerPictureItem的标簽時,就會建立出CSkinPickerPictureItemUI控件了。

       實際上,誰來繼承IDialogBuilderCallback接口肯定都可以,比如QQDemo和仿酷狗裡面,都給自定義控件本身繼承了這個接口。

       當程式響應WM_CREATE消息時,會建立一個CDialogBuilder對象,并且調用他的Create方法來解析xml檔案。

        這個函數 的第一個參數指定為xml檔案的路徑;第二個參數一般指定為NULL,我這裡不詳解了;第三個參數,就是識别自定義控件的關鍵了,這個參數要指定為繼承了IDialogBuilderCallback接口的類對象的指針,比如窗體類繼承IDialogBuilderCallback,這個參數就填寫窗體類對象的指針。隻有填寫了這個參數,自定義控件才會被識别,經常有人問自己的自定義控件為什麼無法被識别。多數情況就是這裡沒處理好;第四個參數指定CPaintManagerUI類對象指針,這個肯定會伴随着窗體類對象一起存在。最後一個參數一般為NULL。

        這幾步都完成後,你的自定義控件就可以被xml布局正确的識别并建立了。至此,建立自定義控件的基本過程就完成了!如果有不明白的,可以多看看仿酷狗的代碼、QQDemo等。

封裝Win32控件或者Win32子視窗:

       如果要給Duilib,增加一個視訊播放控件,一般來說視訊播放庫都需要依賴一個子視窗。這時,就應該自定義個控件,并且封裝維護一個子視窗了。

       封裝的子視窗有三種:第一種比較簡單、單純封裝一個子視窗、讓視訊庫一類的庫依賴;第二種麻煩一些、封裝子視窗、并且處理子視窗的消息;第三種和第二種類似、封裝Win32的控件并且處理他的消息。

      單純封裝子視窗:

      這時就需要重寫我之前提到的DoInit函數和SetVisbile等函數了。首先在自定義控件内聲明HWND類型的m_hWnd成員變量來儲存子窗體指針。

      在DoInit函數裡,調用CreateWindowEx函數,建立一個win32子窗體,并且用m_hWnd儲存句柄。比如:

      然後在SetVisible等函數内控制子窗體的顯示隐藏;在SetPos函數内控制子窗體的位置、限制在本控件的範圍内。

     這樣就封裝好了win32子視窗,然後可以把這個窗體句柄用于視訊播放等。

      封裝子視窗并處理他的消息:

      這時就比較麻煩了,參見Duilib的CEditUI控件等。我們需要繼承CWindowWnd另外封裝一個窗體類,窗體類的封裝不屬于本文範圍,我就不細說了。重寫窗體類的HandleMessage函數,來響應各種WM_XXX消息。

      然後在我們的自定義控件内,不再聲明HWND類型m_hWnd變量了,而是自定剛才的窗體類的對象。然後在DoInit函數内調用這個對應的Create函數函數來建立窗體類。然後同樣還是維護這個窗體的顯示隐藏、和位置。

      封裝Win32控件并處理他的消息:

      這個可以參考CEditUI控件的處理代碼,思想上和封裝子視窗并處理消息是一樣的。同樣也可以參考webkit核心浏覽器控件代碼。不過與之不同的是,我們需要重寫兩個函數

      這裡最主要的就是處理GetSuperClassName函數,這個函數的作用就是超類化,而封裝子視窗并處理消息是子類化,這兩個操作恰好相反。在GetSuperClassName函數内,要範圍Win32控件對應的類名、Duilib檢測到GetSuperClassName函數函數後就會建立Win32控件。這時我們處理HandleMessage函數,就可以處理到Win32控件的消息的。具體的處理邏輯請參見CEditUI控件。

總結:

        差不多就說道這裡了,把常見的自定義控件的基本步驟說明了一下,實際開發時還要多看Duilib的源碼,才能稱心如意的開發控件,希望對剛接觸Duilib的朋友有幫助!

Redrain  QQ:491646717  2014.9.19