• 第二十八篇:SOUI中自定义控件开发过程


    在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件。

    学习一个新东西,最简单的办法就是依葫芦画瓢。事实上在SOUI系统中内置控件和自定义控件的开发流程是完全一样的,因此只需要打开SOUI的源代码,随便找一个控件看一下就大体差不多了。

    下面我以controls.extend目录下的的SRadioBox2控件为例对控件开发过程需要注意的地方做一点说明。

    要开发一个控件,首先要确定的是应该从哪个控件来继承。选择一个合适的基类是正确开发自定义控件的前提。

    之所以要开发一个SRadioBox2控件,我需要解决的问题很简单:SRadioBox控件总是在左边显示一个圆圈,这个圆圈有时候不是我想要的。

    因此我需要做的就是继承SRadioBox控件的行为,重写WM_PAINT的处理。

    因此就有了下面的代码:

     class SRadioBox2 : public SRadioBox
        {
        public:
            SRadioBox2(void);
            ~SRadioBox2(void);
        }

    需要注意的是,所有SOUI控件都是在namespace SOUI中,因此自定义控件也最好是放在SOUI这个namespace里。

    有了上面的骨架,下面来逐步添加内容。

    首先我们需要给自定义控件定义一个在XML中可以识别的标签。

    只需要在类的最开始增加一行:

     class SRadioBox2 : public SRadioBox
        {
        SOUI_CLASS_NAME(SRadioBox2,L"radio2")
        public:
            SRadioBox2(void);
            ~SRadioBox2(void);
        }

    SOUI_CLASS_NAME告诉XML解析器,碰到radio2时自动创建SRadioBox2对象。

    实际上这一行更重要的作用是用来做对象类型运行时识别(RTTI),有了这个机制,在编译器关闭C++的RTTI时仍然可以安全的进行类型转换。

    然后我们需要处理控件的WM_PAINT消息。

    为了处理这个消息,我们需要加入消息映射表及消息处理函数:

    class SRadioBox2 : public SRadioBox
        {
        SOUI_CLASS_NAME(SRadioBox2,L"radio2")
        public:
            SRadioBox2(void);
            ~SRadioBox2(void);
            
    
        protected:       
            void OnPaint(IRenderTarget *pRT);
    
            SOUI_MSG_MAP_BEGIN()
                MSG_WM_PAINT_EX(OnPaint)
            SOUI_MSG_MAP_END()
        };

    SOUI控件的消息处理机制是和WTL中抄过来的,和MFC也很相似。只是对于部分消息,由于对于消息的参数的解释不一样,消息映射的宏会有一点变化。

    如这里的WM_PAINT消息,在SOUI里wparam传递的是一个IRenderTarget指针,而传统的Win32传递的是一个HDC。因此我们需要使用MSG_WM_PAINT_EX代替WTL中使用的MSG_WM_PAINT。

    大家可能会问有哪些消息映射宏SOUI和WLT不一样?

    实际上对于一个有经验的程序员,他应该可以找到MSG_WM_PAINT_EX的宏定义,并且在定义附近就可以找到所有的SOUI和WLT不同的映射宏。

    下面是到目前为止SOUI中所有和WTL不同的宏:

    // BOOL OnEraseBkgnd(IRenderTarget * pRT)
    #define MSG_WM_ERASEBKGND_EX(func) 
        if (uMsg == WM_ERASEBKGND) 
        { 
        SetMsgHandled(TRUE); 
        lResult = (LRESULT)func((IRenderTarget *)wParam); 
        if(IsMsgHandled()) 
        return TRUE; 
        }
    
    // void OnPaint(IRenderTarget * pRT)
    #define MSG_WM_PAINT_EX(func) 
        if (uMsg == WM_PAINT) 
        { 
        SetMsgHandled(TRUE); 
        func((IRenderTarget *)wParam); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
        }
    
    // void OnNcPaint(IRenderTarget * pRT)
    #define MSG_WM_NCPAINT_EX(func) 
        if (uMsg == WM_NCPAINT) 
    { 
        SetMsgHandled(TRUE); 
        func((IRenderTarget *)wParam); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }
    
    // void OnSetFont(IFont *pFont, BOOL bRedraw)
    #define MSG_WM_SETFONT_EX(func) 
        if (uMsg == WM_SETFONT) 
        { 
        SetMsgHandled(TRUE); 
        func((IFont*)wParam, (BOOL)LOWORD(lParam)); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
        }
    
    // void OnSetFocus()
    #define MSG_WM_SETFOCUS_EX(func) 
        if (uMsg == WM_SETFOCUS) 
    { 
        SetMsgHandled(TRUE); 
        func(); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }
    
    // void OnKillFocus()
    #define MSG_WM_KILLFOCUS_EX(func) 
        if (uMsg == WM_KILLFOCUS) 
    { 
        SetMsgHandled(TRUE); 
        func(); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }
    
    // void OnNcMouseHover(int nFlag,CPoint pt)
    #define MSG_WM_NCMOUSEHOVER(func) 
        if(uMsg==WM_NCMOUSEHOVER)
    {
        SetMsgHandled(TRUE); 
        func(wParam,CPoint(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam))); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }
    
    // void OnNcMouseLeave()
    #define MSG_WM_NCMOUSELEAVE(func) 
        if(uMsg==WM_NCMOUSELEAVE)
    {
        SetMsgHandled(TRUE); 
        func(); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }
    
    
    // void OnTimer(char cTimerID)
    #define MSG_WM_TIMER_EX(func) 
        if (uMsg == WM_TIMER) 
    { 
        SetMsgHandled(TRUE); 
        func((char)wParam); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }
    
    #define WM_TIMER2    (WM_USER+5432)    //定义一个与HWND定时器兼容的SOUI定时器
    
    #define MSG_WM_TIMER2(func) 
        if (uMsg == WM_TIMER2) 
    { 
        SetMsgHandled(TRUE); 
        func(wParam); 
        lResult = 0; 
        if(IsMsgHandled()) 
        return TRUE; 
    }

    通常情况下,自定义控件还需要处理一些自定义的属性,这时我需要还需要增加一个属性映射表(如SGifPlayer):

            SOUI_ATTRS_BEGIN()        
                ATTR_CUSTOM(L"skin", OnAttrSkin) //为控件提供一个skin属性,用来接收SSkinObj对象的name
            SOUI_ATTRS_END()

    完成上面几步,一个自定义控件基本上就完成了。

    可能还需要实现几个修改基类行为的虚函数。

  • 相关阅读:
    react native ios 报错SyntaxError: Unexpected end of JSON input
    微信小程序父组件给子组件传参
    xcode10 Command PhaseScriptExecution failed with a nonzero exit code
    html中的video放置在微信上打开会全屏显示,并且丢失文件上的其他内容
    vue cli使用vue-awesome-swiper动画
    vue 使用swiper
    react native 配置leancloud推送 ios版
    iOS----------jenkins
    iOS----------学习路线思维导图
    iOS学习路线
  • 原文地址:https://www.cnblogs.com/setoutsoft/p/4711308.html
Copyright © 2020-2023  润新知