第十篇:扩展SOUI的控件及绘图对象(ISkinObj)

尽管SOUI已经内置了大部分常用的控件,很显然内置控件很难满足各种应用的形式各异的需求。

因此只有提供足够的扩展性才能满足真实应用场景。

除了将系统尽可能的组件化外,SOUI在控件自绘(SWindow)及绘图对象(ISkinObj)两个方面提供用户扩展。

绘图对象(ISkinObj)的扩展

系统内置了如SSkinImgList, SSkinImgFrame, SSkinScrollbar等绘图对象,在窗口中通过引用这些绘图对象可以绘制出不同的预定义图形图象(如按钮,滚动条,九宫格等)。

实际上用户可以实现任意的绘图对象并把它们注册到系统里,以便在XML及代码中使用。

下面先看一下实现一个ISkinObj需要实现哪些接口:

    /**
    * @struct     ISkinObj
    * @brief      Skin 对象
    *
    * Describe
    */
    class SOUI_EXP ISkinObj : public SObject,public TObjRefImpl2<IObjRef,ISkinObj>
    {
    public:
        ISkinObj()
        {
        }
        virtual ~ISkinObj()
        {
        }

        /**
         * Draw
         * @brief    将this绘制到RenderTarget上去
         * @param    IRenderTarget * pRT --  绘制用的RenderTarget
         * @param    LPCRECT rcDraw --  绘制位置
         * @param    DWORD dwState --  绘制状态
         * @param    BYTE byAlpha --  透明度
         * @return   void
         * Describe
         */
        virtual void Draw(IRenderTarget *pRT, LPCRECT rcDraw, DWORD dwState,BYTE byAlpha=0xFF)=0;

        /**
         * GetSkinSize
         * @brief    获得Skin的默认大小
         * @return   SIZE -- Skin的默认大小
         * Describe  派生类应该根据skin的特点实现该接口
         */
        virtual SIZE GetSkinSize()
        {
            SIZE ret = {0, 0};

            return ret;
        }

        /**
         * IgnoreState
         * @brief    查询skin是否有状态信息
         * @return   BOOL -- true有状态信息
         * Describe
         */
        virtual BOOL IgnoreState()
        {
            return TRUE;
        }

        /**
         * GetStates
         * @brief    获得skin对象包含的状态数量
         * @return   int -- 状态数量
         * Describe  默认为1
         */
        virtual int GetStates()
        {
            return 1;
        }
    };

ISkinObj是一个派生自SObject及TObjRefImpl2<IObjRef,ISkinObj>的类,提供了几个状态查询相关的接口,也提供了一个Draw接口来在IRenderTarget上绘制该绘制对象。

注意的是,这些接口中只有Draw接口是纯虚接口。

在它的基类中,SOjbect使得ISkinObj可以方便的从XML配置文件中初始化,而TObjRefImpl2<IObjRef,ISkinObj>则提供引用计数的实现。

内置的ISkinObj不支持显示GIF图片,以显示GIF图象为例来分析如何扩展绘图对象来支持GIF图片显示。

对象定义(trunk\controls.extend\gif\SSkinGif.h)

namespace SOUI
{
    class SGifFrame
    {
    public:
        CAutoRefPtr<IBitmap> pBmp;
        int                  nDelay;
    };

    /**
    * @class     SSkinGif
    * @brief     GIF图片加载及显示对象
    *
    * Describe
    */
    class SSkinGif : public ISkinObj
    {
        SOUI_CLASS_NAME(SSkinGif, L"gif")
    public:
        SSkinGif():m_nFrames(0),m_iFrame(0),m_pFrames(NULL)
        {

        }

        //初始化GDI+环境,由于这里需要使用GDI+来解码GIF文件格式
        static BOOL Gdiplus_Startup();
        //退出GDI+环境
        static void Gdiplus_Shutdown();

        virtual ~SSkinGif()
        {
            if(m_pFrames) delete [] m_pFrames;
        }

        /**
         * Draw
         * @brief    绘制指定帧的GIF图
         * @param    IRenderTarget * pRT --  绘制目标
         * @param    LPCRECT rcDraw --  绘制范围
         * @param    DWORD dwState --  绘制状态,这里被解释为帧号
         * @param    BYTE byAlpha --  透明度
         * @return   void
         * Describe
         */
        virtual void Draw(IRenderTarget *pRT, LPCRECT rcDraw, DWORD dwState,BYTE byAlpha=0xFF);

        /**
         * GetStates
         * @brief    获得GIF帧数
         * @return   int -- 帧数
         * Describe
         */
        virtual int GetStates(){return m_nFrames;}

        /**
         * GetSkinSize
         * @brief    获得图片大小
         * @return   SIZE -- 图片大小
         * Describe
         */
        virtual SIZE GetSkinSize()
        {
            SIZE sz={0};
            if(m_nFrames>0 && m_pFrames)
            {
                sz=m_pFrames[0].pBmp->Size();
            }
            return sz;
        }

        /**
         * GetFrameDelay
         * @brief    获得指定帧的显示时间
         * @param    int iFrame --  帧号,为-1时代表获得当前帧的延时
         * @return   long -- 延时时间(*10ms)
         * Describe
         */
        long GetFrameDelay(int iFrame=-1);

        /**
         * ActiveNextFrame
         * @brief    激活下一帧
         * @return   void
         * Describe
         */
        void ActiveNextFrame();

        /**
         * SelectActiveFrame
         * @brief    激活指定帧
         * @param    int iFrame --  帧号
         * @return   void
         * Describe
         */
        void SelectActiveFrame(int iFrame);

        /**
         * LoadFromFile
         * @brief    从文件加载GIF
         * @param    LPCTSTR pszFileName --  文件名
         * @return   int -- GIF帧数,0-失败
         * Describe
         */
        int LoadFromFile(LPCTSTR pszFileName);

        /**
         * LoadFromMemory
         * @brief    从内存加载GIF
         * @param    LPVOID pBits --  内存地址
         * @param    size_t szData --  内存数据长度
         * @return   int -- GIF帧数,0-失败
         * Describe
         */
        int LoadFromMemory(LPVOID pBits,size_t szData);

        SOUI_ATTRS_BEGIN()
            ATTR_CUSTOM(L"src",OnAttrSrc)   //XML文件中指定的图片资源名,(type:name)
        SOUI_ATTRS_END()
    protected:
        LRESULT OnAttrSrc(const SStringW &strValue,BOOL bLoading);
        int LoadFromGdipImage(Gdiplus::Bitmap * pImg);
        int m_nFrames;
        int m_iFrame;

        SGifFrame * m_pFrames;
    };
}//end of name space SOUI

对象注册:

        theApp->RegisterSkinFactory(TplSkinFactory<SSkinGif>());//注册SkinGif

对象的使用(trunk\demo\uires\xml\dlg_main.xml):

  <skin>
    <!--局部skin对象-->
    <gif name="gif_horse" src="gif:gif_horse"/>
    <gif name="gif_penguin" src="gif:gif_penguin"/>
  </skin>

这里的gif标签与SSkinGif类中的宏SOUI_CLASS_NAME(SSkinGif,L"gif")中的“gif”是匹配的。

到此,在布局XML及程序中都可以获得这个SSkinGif对象的指针了。

控件的扩展

控件的扩展和绘图对象的扩展套路类似,也是先从系统提供的基础类派生,再注册到系统,最后再XML或者代码中使用。

和绘图对象不同在于,控件是UI,需要处理各种UI相关的消息以及向程序发出各种控件特有的事件。

同样我们还是以GIF显示的控件为例(trunk\controls.extend\gif\SGifPlayer.h):

namespace SOUI
{

    /**
    * @class     SGifPlayer
    * @brief     GIF图片显示控件
    *
    * Describe
    */
    class SGifPlayer : public SWindow
    {
        SOUI_CLASS_NAME(SGifPlayer, L"gifplayer")   //定义GIF控件在XM加的标签
    public:
        SGifPlayer();
        ~SGifPlayer();

        /**
         * PlayGifFile
         * @brief    在控件中播放一个GIF图片文件
         * @param    LPCTSTR pszFileName --  文件名
         * @return   BOOL -- true:成功
         * Describe
         */
        BOOL PlayGifFile(LPCTSTR pszFileName);

    protected://SWindow的虚函数
        virtual CSize GetDesiredSize(LPRECT pRcContainer);

    public://属性处理
        SOUI_ATTRS_BEGIN()
            ATTR_CUSTOM(L"skin", OnAttrGif) //为控件提供一个skin属性,用来接收SSkinObj对象的name
        SOUI_ATTRS_END()
    protected:
        HRESULT OnAttrGif(const SStringW & strValue, BOOL bLoading);

    protected://消息处理,SOUI控件的消息处理和WTL,MFC很相似,采用相似的映射表,相同或者相似的消息映射宏

        /**
         * OnPaint
         * @brief    窗口绘制消息响应函数
         * @param    IRenderTarget * pRT --  绘制目标
         * @return   void
         * Describe  注意这里的参数是IRenderTarget *,而不是WTL中使用的HDC,同时消息映射宏也变为MSG_WM_PAINT_EX
         */
        void OnPaint(IRenderTarget *pRT);

        /**
         * OnTimer
         * @brief    SOUI窗口的定时器处理函数
         * @param    char cTimerID --  定时器ID,范围从0-127。
         * @return   void
         * Describe  SOUI控件的定时器是Host窗口定时器ID的分解,以方便所有的控件都通过Host获得定时器的分发。
         *           注意使用MSG_WM_TIMER_EX来映射该消息。定时器使用SWindow::SetTimer及SWindow::KillTimer来创建及释放。
         *           如果该定时器ID范围不能满足要求,可以使用SWindow::SetTimer2来创建。
         */
        void OnTimer(char cTimerID);

        /**
         * OnShowWindow
         * @brief    处理窗口显示消息
         * @param    BOOL bShow --  true:显示
         * @param    UINT nStatus --  显示原因
         * @return   void
         * Describe  参考MSDN的WM_SHOWWINDOW消息
         */
        void OnShowWindow(BOOL bShow, UINT nStatus);

        //SOUI控件消息映射表
        SOUI_MSG_MAP_BEGIN()
            MSG_WM_TIMER_EX(OnTimer)    //定时器消息
            MSG_WM_PAINT_EX(OnPaint)    //窗口绘制消息
            MSG_WM_SHOWWINDOW(OnShowWindow)//窗口显示状态消息
        SOUI_MSG_MAP_END()    

    private:
        SSkinGif *m_pgif;
        int    m_iCurFrame;
    };

}

实现了该类以后,在WinMain中注册:

        theApp->RegisterWndFactory(TplSWindowFactory<SGifPlayer>());//注册GIFPlayer

我们可以在布局XML中创建GIF控件控件并显示了(trunk\demo\uires\xml\dlg_main.xml):

        <gifplayer pos="10,10" skin="gif_horse" name="giftest" cursor="ANI_ARROW"/>
        <button width="250" height="30" name="btnSelectGif">load gif file</button>
        <gifplayer pos="10,150" skin="gif_penguin"/>

上面的gifplayer节点即表示从XML中创建两个SGifPlayer类对象。

而gifplayer对象的skin属性则引用前面定义的SSkinGif对象。

备注:

实际上更多扩展技巧可以参考系统内置的控件的实现。内置控件与扩展控件唯一的区别就在于由谁实现将控件向系统注册。

内置控件在SOUI内核初始化的时候自动注册,而扩展控件则需要手动增加一行注册代码。

效果预览

时间: 2024-10-25 02:04:31

第十篇:扩展SOUI的控件及绘图对象(ISkinObj)的相关文章

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作) 一.简单说明 使用数据刷新框架: 该框架提供了两种刷新的方法,一个是使用block回调(存在循环引用问题,_ _weak),一个是使用调用. 问题:在进行下拉刷新之前,应该要清空之前的所有数据(在刷新数据这个方法中). 移除正在显示的cell: (1)把字典中的所有的值,都从屏幕上移除 (2)清除字典中的所有元素 (3)清除cell的frame,每个位置的cell的frame都要重新计算 (4)清除可复用的缓存池. 该部分的代码如下: 1

iOS开发UI篇—DatePicker和UIToolBar控件简单介绍

iOS开发UI篇—DatePicker和UIToolBar控件简单介绍 一.Date Picker控件 1.简单介绍: Date Picker显示时间的控件 有默认宽高,不用设置数据源和代理 如何改成中文的? (1)查看当前系统是否为中文的,把模拟器改成是中文的 (2)属性,locale选择地区 如果默认显示不符合需求.时间有四种模式可以设置,在model中进行设置 时间可以自定义(custom). 设置最小时间和最大时间,超过就会自动回到最小时间. 最大的用途在于自定义键盘:弹出一个日期选择器

iOS开发UI篇—自定义瀑布流控件(基本实现)

iOS开发UI篇—自定义瀑布流控件(基本实现) 一.基本实现 说明:在View加载的时候,刷新数据. 1.实现代码 YYViewController.m文件 1 // 2 // YYViewController.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-28. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYViewControll

Cocos2d-x 3.2 学习笔记(十)Joystick 搖杆控件

最近想做格鬥遊戲,那麼就要有搖杆控件,不想去看別人的代碼就自己寫了個搖杆控件,實現起來很簡單. 話不多說,看代碼: #ifndef __Joystick__ #define __Joystick__ #include "cocos2d.h" USING_NS_CC; enum JoystickEnum { DEFAULT, D_UP, D_DOWN, D_LEFT, D_RIGHT, D_LEFT_UP, D_LEFT_DOWN, D_RIGHT_UP, D_RIGHT_DOWN };

iOS开发UI篇—自定义瀑布流控件(蘑菇街实现)

iOS开发UI篇—自定义瀑布流控件(蘑菇街瀑布流) 一.简单说明 关于瀑布流 1.是使用UIScrollView实现的 2.刷新数据(reloadData)方法里面做哪些事情 3.layoutSubviews方法里面做哪些事情 4.模仿UItableView进行设计 完善: 瀑布流控件第一次显示到屏幕上的时候自动的向数据源索要数据,而不需要手动调用.这需要监听View的显示,View的显示有一个方法,叫做willMoveToSuperview:在该方法中直接刷新一次数据即可. 二.把自定义的瀑布

学习IOS开问题篇--视图的模型控件属性写在私有分类中的原因

在说原型模式之前,我们先来看java里面的深复制和浅复制: 1. 浅复制:被复制的对象的所有变量都持有和原来对象的变量相同的值,而所有的对其他对象的引用都指向原来的对象. 2. 深复制:被复制对象的所有变量都含有与原来对象相同的值,除去那些引用其他变量的对象.那些引用其他对象的变量将指向被复制过来的新对象,而不是原来那些被引用的对象.深复制需要把要复制的对象的所有引用都复制一遍. 这两者的区别就是关于引用对象的处理,浅复制是不考虑引用对象,而深复制需要考虑引用对象的问题. 对java中的clon

iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 - 文顶顶

原文  http://www.cnblogs.com/wendingding/p/3771047.html iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 一.实现效果 说明: 点击随机按钮,能够自动选取,下方数据自动刷新. 二.实现思路 1.picker view的有默认高度为162,不可修改. 2.显示数据,需要设置数据源,也有两种方式(成为数据源,遵守协议) 3.实现数据源里面的两个方法 1)返回一共有多少列 2)在这一列中一共有多少行 4.通过代理告诉它那一列的哪

iOS开发UI篇—自定义瀑布流控件(接口设计)

iOS开发UI篇—自定义瀑布流控件(接口设计) 一.简单说明 1.关于瀑布流 电商应用要展示商品信息通常是通过瀑布流的方式,因为每个商品的展示图片,长度和商都都不太一样. 如果不用瀑布流的话,展示这样的格子数据,还有一种办法是使用九宫格. 但利用九宫格有一个缺点,那就是每个格子的宽高是一样的,如果一定要使用九宫格来展示,那么展示的商品图片可能会变形. 为了保证商品图片能够按照原来的宽高比进行展示,一般采用的是瀑布流的方式. 2.瀑布流的特点: 由很多的格子组成,但是每个格子的宽度和高速都是不确定

iOS开发UI篇—自定义瀑布流控件(cell的循环利用)

iOS开发UI篇—自定义瀑布流控件(cell的循环利用) 一.简单说明 当滚动的时候,向数据源要cell. 当UIScrollView滚动的时候会调用layoutSubviews在tableView中也是一样的,因此,可以用这个方法来监听scrollView的滚动,可以在在这个地方向数据源索要对应位置的cell(frame在屏幕上的cell). 示例: 当scrollView在屏幕上滚动的时候,离开屏幕的cell应该放到缓存池中去,询问即将(已经)进入到屏幕的cell,对于还没有进入到屏幕的ce