• 轻量级界面库_PaintUI_1


          这里谈一下PaintUI最基本的设计和界面绘制问题

          首先要说的第一点是单线程

          UI设计必须要在单线程中实现,因为界面操作的实时性,线程上下文切换带来的开销很大,效率不会比单线程更好。更重要的是,线程切换时线程间的同步是必不可少的。使用循环锁的话,当控件比较多层次比较深时,会给CPU带来负担。如果使用核心对象,在用户态和核心态切换时,大概要消耗300个CPU周期。更可怕的是当有人操作你界面时做了一些复杂的事,会使你的界面在一段时间内没有响应。

          使用消息是单线程的一个比较好的解决方案,但由于第一篇所说的原因,我没有使用消息驱动,所以我参照了COM的套间线程设计。

          控件的基类

          所有的控件、面板都要有从一个特定的基类继承,就像MFC的控件和窗口都是从CWnd继承的一样。假设这个基类叫做CPui,那么看起来像是这样的。

    class CPui{
        
    protected:
        RECT m_rct;
        map<WORD, CPui_ptr> m_pChildUIMap;
        CPui_ptr m_pParent;
        WORD m_wID;
    }

         PaintUI的所有控件和面板就是一个RECT区域(m_rct)。而控件、面板所有的效果、大小、位置的变化其实都是动态的改变缓冲图中m_rct区域内的内容,就像动画一样。m_wID是这个控件的ID,ID值在资源头文件中统一声明。m_pParent指向父控件的指针,如果当前窗口时根窗口则指向空。m_pChildUIMap是子控件列表。这样,整个界面和它之中的控件就形成了一个树形结构。

          编译时识别

    由于使用了继承、多态和模板,实现中免不了大量的函数重载和空实现。解决这一问题的办法是使用模板和编译时识别。如 __if_exists 之类的。为了结构的清晰,后面的实现中将忽略编译时识别。

          图像缓冲区和另一种重绘方式

          PaintUI中还要保存当前控件的图像缓冲区,当发起重绘事件时,当前控件的图像缓冲器要拷贝到父控件缓冲区的相应位置。在整个界面树中,这个变化时从叶子节点向跟节点逐次发生的。

          在MFC或一些界面库中,当界面某一部分需要重绘时,父窗口会查询该位置对应的子窗口,并发送重绘消息,而子窗口也要先查询它自己的子窗口一次类推,直到叶子节点。然后从叶子节点开始逐层绘制。这种绘制方法比较合理,但也造成了另一个问题,这就是重回过程比较慢。这个问题普遍的解决方式是双倍缓冲区或多倍缓冲区,但我认为它不能完全解决这个问题。

          在PaintUI中,我使用了另一种绘制方式。当最外层窗口的某个区域需要重绘时,它只是简单的将当时自己缓冲区相应位置的内容复制到屏幕缓冲区中,并不会向子窗口发送重绘消息,这样就像简单的在对话框中贴了一幅图片一样。而界面控件的效果,变化等,是子控件通过事件触发,主动地更新父控件的缓冲区的内容。通过封装,使这个过程不被察觉。

          PaintUI中还使用了重绘等级标记,当某个控件缓冲区更新后,需要父控件重绘缓冲区相应位置时,会同时向父控件提交一个重绘等级。当父控件更新缓冲区时会根据各个位置的重回等级,复制子空间缓冲区。当同一个位置有多个重回等级标志时,父控件只保留重绘等级最高的那个。

    class CPui{
    public:
        enum PaintRank{PD_NOPAINT=0, PD_PAINTFLAG=1, PD_PAINTALL=2};
    
    public:
        virtual BOOL ReBuf(){return TRUE;}
        VOID DirectPaitUI(Bitmap* pBitmap, CRect rctClient);
        VOID _PPaint(Bitmap* pBitmap);
    
    protected:
       Bitmap* m_pBMBuffer;
    }

          m_pBMBuffer是当前控件的缓冲区,如果当前控件只是完成指定功能或消息处理,不要为它创建实例保持这个指针为空就好。Rebuf()是绘制当前控件缓冲区的方法,当前控件的绘制都会在这个类中实现,如果需要绘制,子控件要实现这个方法。_PPaint()方法是将当前控件缓冲区绘制到父窗口时被调用地方法。这个方法被隐藏在控件基类中(就是CPui),不是特殊需要,不应该手动调用。DirectPaintUI是当初考虑到这种绘制机制可能会带来延迟而特意写的方法,它将一个缓冲区直接绘制到根窗口的缓冲区中,然后强制屏幕重绘这个区域。实际上,这个方法我从未用过,只要使用恰当,CPui每秒几十桢的动画都可以轻松应付。

  • 相关阅读:
    Wijmo 更优美的jQuery UI部件集:从wijwizard和wijpager开始
    设计规范基础
    libgdx的tmx地图处理工具gdxtiledpreprocessor.jar(TiledMapPacker)修正版
    Zookeeper简介
    翻译:Contoso 大学 6 – 更新关联数据
    jquery 插件ztree的应用简单的树(tree)
    使用EF构建企业级应用
    可复用的WPF或者Silverlight应用程序和组件设计(1)——应用程序级别
    Castor简单介绍
    Eclipse各种书籍资料整理包括书籍介绍和下载
  • 原文地址:https://www.cnblogs.com/liuyuxi/p/1914564.html
Copyright © 2020-2023  润新知