2011-01-10写于iteye的一篇文章,今迁至此,之后这里即是三哥的大本营!
AWTUtilities.setWindowOpaque的功能在Java7中能够经过Window.setBackground来实现,但问题依旧!如下为原文:
java
上个周末,将三号管家更新到了V1.0.4,其实主要是修改了SwingC,管家自己只改了版本号。更新说明里我写的是“优化菜单字体显示”。请你们仔细观察下面的截图:
字体
这张图片是在三号管家V1.0.3中抓取的,左边是菜单有一部分超出了主界面的范围(那部分是全透明的,因此不能说人家不存在),右边是菜单所有显示在主界面区域内。很明显,左边菜单中字体的质量要差不少,这仍是我在绘制的时候修改了某些参数优化过的,未经参数优化的比如今看到的效果还要差不少。为何会这样呢?TWaver的那位刀客在他的“Swing是一把刀”系列中曾经提到过,我在这里再说一下。能够认为这是JDK的一个bug,具体问题是:java.awt.Window及其子类若是使用com.sun.awt.AWTUtilities.setWindowOpaque(window, false)设为全透明,那么这个窗体及其下的全部子组件所显示的字体都会变得很粗糙。既然是窗体的问题,为何会影响到菜单呢?缘由就在于JPopupMenu显示的区域若是未超出其父窗体的范围,那么它是一个轻量级的JComponent,可是一旦超出了,那这个时候显示出来的其实是一个重量级的Window。为了实现弹出菜单边框半透明的模糊效果,我重写了setVisible,若是显示的是重量级的Window,直接使用AWTUtilities.setWindowOpaque(window, false)将其置为全透明,因此字体质量就严重降低了。优化
这是个很棘手的问题,我使劲的谷歌、百度一番事后,仍一无所得。TWaver的刀客采用的解决方案是在JFrame之上放一个JDialog,而后采起一些手段使它们保持同步,因此看起来仍然是一个窗体,但我这个仅仅只是弹出菜单,若是使用这个方案感受成本过高。通过几番研究和尝试以后,发如今绘制字体的时候改改某些参数的默认值能够稍稍优化一下,不至于和正常字体的效果差太多,但终究仍是有差,这也就是上面图片中你们看到的效果。心头之痛哪,可是没有更好的解决方案只能如此了。this
再而后就到了2011年的某一天,灵感忽然告诉我,有个办法或许可行,因而试之,果真没让老三失望。我先把代码帖上来吧,后面再慢慢说闲话。
spa
public void paint(Graphics g) { if(!UIUtil.isTranslucencySupported() || !buffered) { super.paint(g); } else { Insets insets = this.getInsets(); int x = insets.left; int y = insets.top; int width = this.getWidth(); int height = this.getHeight(); int contentWidth = width - insets.left - insets.right; int contentHeight = height - insets.top - insets.bottom; BufferedImage image = UIUtil.getGraphicsConfiguration(this).createCompatibleImage(width, height, Transparency.TRANSLUCENT); BufferedImage contentImage = UIUtil.getGraphicsConfiguration(this).createCompatibleImage(contentWidth, contentHeight, Transparency.OPAQUE); Graphics2D g2d = image.createGraphics(); Graphics2D contentG2d = contentImage.createGraphics(); contentG2d.translate(-x, -y); super.paint(g2d); super.paint(contentG2d); g2d.dispose(); contentG2d.dispose(); g.drawImage(image, 0, 0, this); g.drawImage(contentImage, x, y, this); } }
代码很简单,建立了两个BufferedImage。注意createCompatibleImage中最后一个参数,第一张图片使用Transparency.TRANSLUCENT,这种类型的图片能够设置透明度,用它来绘制边框的半透明模糊效果,为了不复杂的计算,直接将菜单的全部内容都绘制上去,但这种类型的图片上面显示的字体效果依然不好,因此在中间内容部分用另一张图片来填充,前提是这张图片不能覆盖掉边框的效果,因而它的长和宽都比第一张图片小,起始位置也跳过了边框,这就是第二个BufferedImage,类型为Transparency.OPAQUE(即彻底不透明,默认背景为黑色)。在这种类型的图片上绘制字体,效果与JComponent组件正常的字体彻底一致,最后再将这两张图片绘制到组件上。问题就这么解决了,这也就是三号管家V1.0.4中的菜单效果。上面这段代码呢,只作讲解使用,你们若是要用的话可能还须要实际状况实际绘制,可是方法就是这么个方法,万变不离其踪。code