开始想得很简单, 异型窗口+贴PNG图就搞定了, 仔细一看没这么简单, 这个钟表窗口, 边缘部分是透明的, 中间部分是不透明的, 如果是全透明窗口, 创建LayeredWindow之后调用SetLayeredWindowAttributes即可, 但现在这个部分透明的窗口, 是要用到LayeredWindow针对每个像素的特性, 传说中的东西, 一直没实践过.
看了一下,也很简单, LayeredWindow提供两种模式:
1,使用SetLayeredWindowAttributes去设置透明度, 完成窗口的统一透明,此时窗口仍然收到PAINT消息, 其他应用跟普通窗口一样.
2, 使用UpdateLayeredWindow方法, 向系统提交包含bitmap的DC, 交由系统统一管理,此时再也收不到paint消息, 任何对窗口的改变,只能通过UpdateLayeredWindow来修改.
如果你不需要针对像素级别的不同透明,只需要使用SetLayeredWindowAttributes模式即可,用法与普通窗口用法一样,有一点不同,系统会缓存窗口的bitmap,所以当窗口上面的其他窗口被移开时,这是系统会去自己绘制,不会发送paint消息。使用这种模式的好处时,你基本不用改变你使用窗口的方法,你收到paint消息后,绘制的图像会被系统重定向到另一个函数里面,进行组合,从而得出透明效果。
如果你需要达到针对像素级别的不同透明,也就是上图的想过,或者你想更加直接的去控制窗口的绘制,就必须使用UpdateLayeredWindow方法了,这个方法不重定向你的绘制结果,也不缓存窗口的bitmap,而是完全由你自己来绘制,这样在内存上来说,是更高效的。需要注意的是,一旦你调用了SetLayeredWindowAttributes,UpdateLayeredWindow的调用就会失败,除非重新设置WS_EX_LAYERED,所以他们是互斥的。
对layeredwindow来说,在不完全透明的地方,是可以接收到鼠标消息的,在完全透明的地方,鼠标的点击将会被穿过,还有一种情况是对窗口设置了WS_EX_TRANSPARENT属性,鼠标消息也会穿过。
特别注意的是,WS_EX_LAYERED属性是不可以设置给子窗口的。
看了一下QQ的个人信息展示栏,底部带有这种透明效果,抓起窗口,发现就是使用layeredwindow的UpdateLayeredWindow模式:
而百度Hi的信息栏,就没有这种效果了:
虽然也是采用的layeredwindow,但是只是为了渐隐效果采用的。采用的,是SetLayeredWindowAttributes模式。
对于这种透明的效果,另外又有几种旁门左道可以实现,但终归有缺陷,总结一下,有如下几种:
1,利用异形窗口实现,由于子窗口不能被设置WS_EX_LAYERED,所以,可以给定A、B、C三个窗口,其中,A为全透明窗口,B为UpdateLayeredWindow型的layered窗口,C为正常子窗口,C的父亲为A,B为A的popup窗口。这样,在C上面有不透明的东西,B上有任意透明的东西,唯一要做的,就是当ABC三窗口有任意一个移动或者改变大小时,其他两个都要相应变化。
这个方案的缺点,一是不适合大规模使用,第二是有同事试验过,移动时如果移动过快会留下残影,这种方案适用于不移动窗口的简单程序。
2,利用截图背景实现,窗口显示时,对窗口区域的屏幕进行截图,截完之后,设置为窗口的背景。这种方案的缺点,也是窗口不能移动,并且背后不能有动画。只能是一种假透明。此种方案适用于复杂程序,在手机上变成使用的比较多,因为手机的窗口很多是全屏,不移动。