多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > [置頂] duilib開發基礎:創建自定義控件的過程

[置頂] duilib開發基礎:創建自定義控件的過程

來源:程序員人生   發布時間:2015-05-14 09:21:19 閱讀次數:5727次

轉載請說明原出處,謝謝~?http://blog.csdn.net/zhuhongshu/article/details/45362751


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

       在群里常常會有剛接觸Duilib的朋友問題怎樣建立自己的自定義控件,或建立的控件沒法正常創建出來。我簡單寫1篇博客,把創建自定義控件的完全進程,和1些注意事項說明1下。另外說1下如果把win32的子窗體封裝為控件,希望能有幫助。

       創建自定義控件包括兩個進程:
       1、繼承現有的控件類創建新的控件類
       2、讓程序辨認新的控件并可以在xml中使用


創建新的控件類:

       首先從的現有的Duilib控件當選擇1個最適合的控件類,繼承他然后重寫幾個接口。
       我這里拿仿酷狗的換膚窗體中的1個自繪控件做例子(原文地址:http://blog.csdn.net/zhuhongshu/article/details/38491389)
       仿酷狗音樂盒源代碼:http://blog.csdn.net/zhuhongshu/article/details/41037875



      
      


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

     
#ifndef SKIN_PICKER_PICTURE_ITEM_H #define SKIN_PICKER_PICTURE_ITEM_H //xml sample:<SkinPikerPictureItem name="" width="118" height="70" bkimage="UIBKImage1small.jpg" bkname="測試" author="Redrain" /> //類名和接口名,在CreateControl函數中會用到 const TCHAR kSkinPickerPictureItemClassName[] = _T("SkinPikerPictureItemUI"); const TCHAR kSkinPickerPictureItemInterface[] = _T("SkinPikerPictureItem"); //黑色的前景圖的位置 const TCHAR kSkinPickerPictureItemForeImage[] = _T("file='UILeftTablistitemListBk.png' fade='150'"); //邊框的色彩、圖片名稱的文字色彩、作者信息的文字色彩 const DWORD kBorderColor = 0xFF64B0FA; const DWORD kBkNameColor = 0xFFFFFFFF; const DWORD kAuthorColor = 0xFFAAAAAA; class CSkinPikerPictureItemUI : public CButtonUI { public: CSkinPikerPictureItemUI(); LPCTSTR GetClass() const; LPVOID GetInterface(LPCTSTR pstrName); void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); void PaintStatusImage(HDC hDC); private: CDuiString m_BkName; CDuiString m_Author; }; #endif // SKIN_PICKER_PICTURE_ITEM_H


CSkinPikerPictureItemUI::CSkinPikerPictureItemUI() { m_Author = _T("作者:"); } LPCTSTR CSkinPikerPictureItemUI::GetClass() const { return kSkinPickerPictureItemClassName; } LPVOID CSkinPikerPictureItemUI::GetInterface(LPCTSTR pstrName) { if( _tcscmp(pstrName, kSkinPickerPictureItemInterface) == 0 ) return static_cast<CSkinPikerPictureItemUI*>(this); return CButtonUI::GetInterface(pstrName); } void CSkinPikerPictureItemUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) { if( _tcscmp(pstrName, _T("bkname")) == 0 ) m_BkName = pstrValue; else if( _tcscmp(pstrName, _T("author")) == 0 ) m_Author += pstrValue; CButtonUI::SetAttribute(pstrName, pstrValue); } void CSkinPikerPictureItemUI::PaintStatusImage(HDC hDC) { CButtonUI::PaintStatusImage(hDC); if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED; else m_uButtonState &= ~ UISTATE_FOCUSED; if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED; else m_uButtonState &= ~ UISTATE_DISABLED; if( (m_uButtonState & UISTATE_PUSHED) != 0 || (m_uButtonState & UISTATE_HOT) != 0) { DrawImage(hDC, kSkinPickerPictureItemForeImage) ; //計算作者信息文字和背景圖片名字文字的顯示位置,這里是用了硬編碼,請使用者自己修改 RECT rcBkName = m_rcItem; LONG nTextPadding = (m_rcItem.right - m_rcItem.left - CRenderEngine::GetTextSize(hDC, GetManager(), m_BkName.GetData(), m_iFont, m_uTextStyle).cx) / 2; rcBkName.left += nTextPadding; rcBkName.right -= nTextPadding; rcBkName.top += 15; rcBkName.bottom = rcBkName.top + 20; RECT rcAuthor = m_rcItem; nTextPadding = (m_rcItem.right - m_rcItem.left - CRenderEngine::GetTextSize(hDC, GetManager(), m_Author.GetData(), m_iFont, m_uTextStyle).cx) / 2; rcAuthor.left += nTextPadding; rcAuthor.right -= nTextPadding; rcAuthor.top += 40; rcAuthor.bottom = rcAuthor.top + 20; CRenderEngine::DrawText(hDC, m_pManager, rcBkName, m_BkName, kBkNameColor, m_iFont, m_uTextStyle); CRenderEngine::DrawText(hDC, m_pManager, rcAuthor, m_Author, kAuthorColor, m_iFont, m_uTextStyle); CRenderEngine::DrawRect(hDC, m_rcItem, 2, kBorderColor); } }



       新的控件名為CSkinPickerPictureItemUI。1般來講,建立新控件后,最早應當重寫的兩個函數是GetClass和GetInterface。他們后用來辨別控件的類型的虛函數,用于動態辨認控件類型和做控件的類型轉換。

       從Duilib的自帶控件上可以看出,比如當前的自定義控件類名為CSkinPickerPictureItemUI,那末GetClass函數返回的字符串SkinPickerPictureItemUI。而GetInterface函數是根據傳入的參數,是不是與本身的字符串匹配,來決定能否把自己轉換為需要的控件類型。GetInterface中用來匹配的字符串,應當與xml中的對應的控件的標簽名稱1直,這里應當是SkinPickerPictureItem。

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


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

virtual void DoEvent(TEventUI& event); virtual void DoPaint(HDC hDC, const RECT& rcPaint); virtual void PaintBkColor(HDC hDC); virtual void PaintBkImage(HDC hDC); virtual void PaintStatusImage(HDC hDC); virtual void PaintText(HDC hDC); virtual void PaintBorder(HDC hDC); virtual void DoInit(); virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue); virtual bool IsVisible() const; virtual void SetVisible(bool bVisible = true); virtual void SetInternVisible(bool bVisible = true); // 僅供內部調用,有些UI具有窗口句柄,需要重寫此函數 virtual void SetPos(RECT rc);






       以上列出的函數,是最常被重寫的。
       
       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程序框架已完成,當需要做1些界面的初始操作時可以重寫此函數,常見的用法就是在此建立Win32子窗體并且封裝它,相干內容我在后面再說。


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


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

辨認新控件:


       自定義控件創建終了后,需要做的就是讓控件可以被xml布局辨認出來。為此我們需要完成Duilib的IDialogBuilderCallback接口,重寫這個接口中的CreateControl函數。

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

CControlUI* CSkinPickerDialog::CreateControl(LPCTSTR pstrClass) { if (_tcsicmp(pstrClass, kSkinPickerPictureItemInterface) == 0) return new CSkinPikerPictureItemUI(); return NULL; }


      習慣上,在xml中自定義控件的標簽名稱應當和控件的GetInterface中的判斷字符串1致,比如這里就是SkinPickerPictureItem。這樣,在解析xml進程中,當解析到名為SkinPickerPictureItem的標簽時,就會創建出CSkinPickerPictureItemUI控件了。


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

      
       當程序響應WM_CREATE消息時,會建立1個CDialogBuilder對象,并且調用他的Create方法來解析xml文件。


CControlUI* CDialogBuilder::Create(STRINGorID xml, LPCTSTR type, IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent)


        這個函數 的第1個參數指定為xml文件的路徑;第2個參數1般指定為NULL,我這里不詳解了;第3個參數,就是辨認自定義控件的關鍵了,這個參數要指定為繼承了IDialogBuilderCallback接口的類對象的指針,比如窗體類繼承IDialogBuilderCallback,這個參數就填寫窗體類對象的指針。只有填寫了這個參數,自定義控件才會被辨認,常常有人問自己的自定義控件為何沒法被辨認。多數情況就是這里沒處理好;第4個參數指定CPaintManagerUI類對象指針,這個肯定會伴隨著窗體類對象1起存在。最后1個參數1般為NULL。

        這幾步都完成后,你的自定義控件就能夠被xml布局正確的辨認并創建了。至此,創建自定義控件的基本進程就完成了!如果有不明白的,可以多看看仿酷狗的代碼、QQDemo等。


封裝Win32控件或Win32子窗口:


       如果要給Duilib,增加1個視頻播放控件,1般來講視頻播放庫都需要依賴1個子窗口。這時候,就應當自定義個控件,并且封裝保護1個子窗口了。

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


      單純封裝子窗口:


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

      在DoInit函數里,調用CreateWindowEx函數,創建1個win32子窗體,并且用m_hWnd保存句柄。比如:

m_hhWnd = CreateWindow(_T("#32770"), _T("WndMediaDisplay"), WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, m_PaintManager.GetPaintWindow(), (HMENU)0, NULL, NULL);


      然后在SetVisible等函數內控制子窗體的顯示隱藏;在SetPos函數內控制子窗體的位置、限制在本控件的范圍內。

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

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


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

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

     關于這類控件的封裝,可以參考我寫的webkit內核閱讀器控件、里面是完全的封裝了Win32子窗體、并且處理了他的消息,用于顯示webkit內核渲染的網頁。地址:http://blog.csdn.net/zhuhongshu/article/details/38540711


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


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

LPCTSTR GetWindowClassName() const; LPCTSTR GetSuperClassName() const;



      這里最主要的就是處理GetSuperClassName函數,這個函數的作用就是超類化,而封裝子窗口并處理消息是子類化,這兩個操作恰好相反。在GetSuperClassName函數內,要范圍Win32控件對應的類名、Duilib檢測到GetSuperClassName函數函數后就會創建Win32控件。這時候我們處理HandleMessage函數,就能夠處理到Win32控件的消息的。具體的處理邏輯請參見CEditUI控件。


總結:

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


Redrain  QQ:491646717  2014.9.19
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久婷婷五月综合色丁香 | 视频一区二区精品的福利 | 亚洲不卡免费视频 | 欧美国产日韩在线播放 | 亚洲天堂免费视频 | 亚洲欧美一区二区三区久久 | 亚洲高清在线 | 亚洲精品一区二区三区在 | 日韩特黄特色大片免费视频 | 欧美日韩a级a | 国产精品视频视频久久 | 91精品福利一区二区 | 欧美精品xx| 国产高清在线免费观看 | 伊人久久大香现线蕉 | 波多野结衣免费一区二区三区香蕉 | 日韩色综合 | 午夜高清免费观看视频 | 青娱乐伊人| 亚洲图片欧美在线 | 日韩高清片 | 免费羞羞网站 | 亚洲欧美专区精品伊人久久 | 亚洲肥妇 | 日本一区二区免费在线观看 | freexxx性欧美hd丝袜 | 国产专区在线视频 | 尤物tv | 在线视频一区二区三区四区 | 婷婷麻豆 | 久久精品国产400部免费看 | 亚洲精品中文字幕一区在线 | 大香伊人网 | 波多久久夜色精品国产 | 性做久久久久久久久浪潮 | 国产成人高清视频 | 看欧美毛片一级毛片 | 色人阁网站| 老司机午夜精品网站在线观看 | 校园春色欧美色图 | 欧美三级在线观看视频 |