转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/43532791
半透明异形窗体的功能在之前维护的老版本的duilib里面已经有了基本的功能,但是因为一直存在较多的缺陷,所以我一直建议少用,就连我自己写仿酷狗项目也只是在几个小地方用了半透明异形窗体。不过今天在群里和其他几位朋友讨论后,发现了之前的许多问题以及解决方法。所以我立马修复了当前的库,并且写了一个半透明异形窗体的demo来测试效果。这里的半透明窗体是用UpdateLayeredWindow函数实现的,并不是双层窗体。
总体来说,为了给官方原本的duilib391版本库增加窗体半透明异形功能,需要修改的类如下:
1、CManagerUI类的WM_PAINT消息处理代码
2、CRenderEngine类的文字渲染函数、和背景填充函数、贴图函数
3、CEditUI类的win32 edit创建函数
4、CRichEditUI类的光标绘制函数(自绘光标,需要结合CManagerUI类的相关代码)
5、给CManagerUI类增加自绘CRichEditUI光标的一系列函数
6、处理窗体类的最小化还原小(需要结合CManagerUI类的相关代码)
7、给CManagerUI类增加最小化还原后的消息处理代码,为了在半透明状态下完全刷新窗体
在这篇博客里,我主要说明一下一下几点:
1、之前版本使用半透明异形窗体存在的一些问题
2、新版本里的大致解决方法
3、还没有完全解决的地方
4、使用半透明异形窗体应该注意的地方
废话不多说,先把demo的效果图展示一下:
存在的问题和解决办法:
问题1:字体穿透
这是之前的半透明窗体的最大问题,由于GDI本身的缺陷,导致渲染文字时缺少透明通道信息,导致文字穿透,之前使用heat群主的alpha修复方法,但是还不能完全解决这个问题。为此我把文字渲染函数修改。当窗体是透明模式或者用户开启了gdi+文字渲染模式,就用gdi+来渲染文字。开启半透明模式的方法是设置xml布局的Window标签的bktrans属性为true。开启gdi+文字渲染模式的方法是设置xml布局的Window标签的gdiplustext属性为true。bktrans属性和gdiplustext属性是独立的。(如果感觉默认的GDI文字渲染效果不够好时,就可以使用gdiplustext属性得到更好的渲染效果)。
GDI+的渲染效率是出名的低,不过据我测试文字渲染才用GDI+的话看不出影响效率。
问题2:字体穿透2
还是字体穿透的文字,即使把文字渲染改成gdi+再配合alpha修复,但是依然发现会出现字体穿透的问题,这也是之前让我纳闷的地方。而今天我做了许多测试后发现,原来和控件的背景色有关。如果不设置控件的背景色或者背景色是半透明的,渲染文字就不会穿透,而设置了背景色而且不透明就会穿透,背景色填充是CRenderEnghine类的DrawColor函数负责的。观察源码后发现。如果背景色的透明度设置为FF(也就是不透明)的话,就会使用GDI函数来填充背景色,而如果设置了透明度,则使用AlphaBlend函数来使用一个位图来填充背景。所以问题还是出在透明通道上(说明alpha修复还不能完全解决相关的问题)。
所以只要让duilib填充背景色时包含透明信息就不会导致穿透了,而后阅读代码发现duilib默认的alpha背景色填充代码比较多。我个人感觉不如直接用gdi+来填充背景色。代码如下:
if( color <= 0x00FFFFFF ) return; Gdiplus::Graphics graphics( hDC ); Gdiplus::SolidBrush brush(Gdiplus::Color((LOBYTE((color)>>24)), GetBValue(color), GetGValue(color), GetRValue(color))); graphics.FillRectangle(&brush, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
这个代码比较简单,应该不会影响效率吧?
问题3:窗体刷新不完整
原来的库编译的半透明异形程序,有时会出现窗体刷新不完整的情况,比如:把一个半透明程序的一般拖到屏幕外侧,然后双击标题栏让他最大化,接着再最小化,然后把程序再拖到屏幕内,就会发现原来在屏幕外部的界面完全没有绘制出来。
导致这个问题的原因,是因为要使用异形窗体就需要使用UpdateLayeredWindow函数,而使用了这个函数后窗体的绘制就由UpdateLayeredWindow来接管了,许多情况下WM_PAINT消息就不会触发。跟踪代码后发现是因为程序还原后调用GetUpdateRect函数获取更新区域信息时信息不完整导致的。所以我在CManagerUI类中拦截了WM_SYSCOMMAND消息,发现程序从最大化还原后就设置m_bIsRestore为真,把m_bIsRestore作为标志。当在WM_PAINT中刷新界面时如果m_bIsRestore为真则刷新整个程序界面而不只是GetUpdateRtect获取的区域。另外我添加了m_rcInvalidate变量,来自己管理需要更新到区域,拟补GetUpdateRect函数获取信息不足的问题。
还没有完全解决的地方
前面说明的一些问题解决后,配合alpha修复的代码。基本就可以使用半透明异形窗体了。不过还是存在一些没有解决的问题:
1、半透明模式下Edit控件的效果不好。duilib的Edit控件内部调用了Win32的edit控件,为了在半透明模式下使用它,不得不使用WS_POPUP样式窗体Win32的edit控件。但是这样窗体后会在外观上出现一些问题。就是单击duilib的Edit控件时会闪一下。所以建议使用RichEdit控件来代替Edit,而且RichEdit控件是支持半透明或者全透明背景的。
2、RichEdit控件虽然可以正常使用,但是由于他内部渲染文字时没有处理透明通道,所以会导致文字穿透。出现文字穿透的现象只有在RichEdit控件完全无背景色或者背景图片的情况下。
使用半透明异形窗体应该注意的地方
目前需要注意的就是RichEdit控件的使用,如果让RichEdit直接暴露在全透明的地方,文字渲染就会有问题。如果RichEdit设置了背景色或者背景色,或者他的父控件设置了背景色背景图,则全完没有问题。不过好在,应该没有什么地方需要让RichEdit直接暴露在全透明的位置。前面发的效果图的文本框就是用RichEdit做的。
如果使用RichEdit的时候发现了穿透现象,建议更换textcolor的值或者bkcolor的值来修复问题。
总结:
由于这次修改的地方比较多,设计的文件和代码也比较乱,我就没办法单独的提供某个源文件了。具体的修改可以通过svn来对比新版本和旧版本的代码来得知。源码和demo我都更新到了我的库中,支持duilib和uilib:点击打开链接
Redrain 2015.2.5