Chinaunix首页 | 论坛 | 博客
  • 博客访问: 499218
  • 博文数量: 1496
  • 博客积分: 79800
  • 博客等级: 大将
  • 技术积分: 9940
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-09 13:22
文章分类

全部博文(1496)

文章存档

2011年(1)

2008年(1495)

我的朋友

分类:

2008-09-09 17:22:07

    支持透明和不规则窗口已经成为 AWT 和 Swing 团队长久以来梦寐以求的功能。尽管本机应用程序在主要操作系统上使用这项功能已经为时已久,但在核心 中还不能使用它。即将发布的 “Consumer JRE”正在进行修改,也就是对 SE 6 进行重大更新。Java SE 6 将为创建不规则、全透明和每个像素透明的顶级窗口提供 API.

    历史本机应用程序的开发人员通常在开发 UI 应用程序中享受了更高级的灵活性。但是为此而付出的代价是将应用程序限制在某一特定平台上,在许多情况中,这种灵活性不如获得更为丰富的 UI 体验和桌面紧密集成那么重要。从传统上讲,跨平台 UI 工具箱,例如 Swing、SWT、QT 和 wxWidgets 趋向于被动应付众所周知的两难问题。当只有某些目标平台支持所要求的功能时怎么办?在这种情况下,模拟缺失的功能可能只会让您南辕北辙。

    不规则和透明窗口是跨平台 UI 工具箱局限性的最好例子。如果在特定目标平台不支持此项功能,那么在该平台上就没有什么更多事情要做了,此项功能可能用作强有力的参数向工具箱添加该项功能。但是,Swing 开发人员社区长久以来一直争论主要目标平台不久就会提供这些功能。事实上, 自从 95 (参见 MSDN 上的 SetWindowRgn 文档 )就已经支持不规则窗口了。在 X11 中匹配功能自从 1989 年 ( 参见 X Nonrectangular Window Shape Extension Library PDF 文档 )就已经可用了。在 OS X 中您仅能在 JFrame 上设置透明的背景颜色。

    直到现在,对跨平台透明和不规则窗口有兴趣的 Swing 应用程序有三种主要可选方式。

    在显示目标窗口之前使用 java.awt.Robot 捕获桌面。这种方法在 Joshua Marinacci 和 Chris Adamson 编写的 《 Swing Hacks 》 书中的 第 41 章 中已经进行了评述。

    使用 JNI 包装目标平台的本机 API.使用由 Timothy Wall 开发的 JNA 库。该库在 2007 年问世,Timothy 对于 不规则窗口 和 字母掩码透明度 已经发表过博客。

    第一种方法的主要问题是要使用 Robot 类。即使您有权限获得屏幕截图,您也必须在显示窗口之前完成。此外,如何保持桌面后台同步?假设在后台正在播放 YouTube 视频。与窗口生成的事件不同( 调整大小,移动 ),AWT 并不在任何交叉窗口的重画上提供注册侦听器的任何方式。虽然 Chris 和 Joshua 通过在至少每秒内进行快照提供解决方法,这对于覆盖后台视频播放还不够。而且在每次快照前需要对窗口加以隐藏;这可能导致可见的闪烁。

    使用 JNI 和 JNA 导致显著的视觉保真性改进。纯 JNI 会带来开销的急剧下降:您必须将目标平台的每一个相关的 API 绑定,还要捆绑本机库。JNA 为您分担这项重任; 它捆绑主机库并提供能在运行时提取并加载它们的类加载器。它支持 Linux、 OS X、 Windows、 Solaris 和 FreeBSD.

    Consumer JRE Java SE 6 Update N, 通常称作 Consumer JRE, 是 Sun 公司的努力成果,为重新配置 Java 将其作为开发富桌面应用程序的可行方法。在 Consumer JRE 中的新功能和主要改进列表相当广泛,并将特别闪耀的宝石隐藏在最新一周构建代码之一的发行说明中。Bug 6633275 被简单地赋予“需要支持不规则/透明窗口”的标题。但是该实现核心 JDK 新功能的可能性所带给 Swing 开发人员的意义是深远的。本文的剩余部分将显示能够实现和如何实现该功能的几个示例。

    在进一步研究之前,有一个非常重要的注意事项。由于 Consumer JRE 被官方认为是对稳定 JDK 发行的一个次要更新,因此在“公共”包中不能添加任何新的 API( 类、方法等等 ),例如 java.awt 或 javax.swing.在本文中讨论的所有 API 在新 com.sun.awt.AWTUtilities 类中出现,该类不是官方支持的部分 API.它在 Java SE 7 中的位置最有可能发生改变,签名方法可能在现在和最终的 Consumer JRE 发行之间发生轻微变化。所以当这种改变发生时准备更改您自己的代码。

    AWTUtilities 类我首先讨论 com.sun.awt.AWTUtilities 类,请参见 在核心 Java 中的透明和不规则窗口 博客条目。首先我们从图 1 中的简单窗口入手:

    图 1. 带有控件的窗口

    要使窗口透明,您可以使用 AWTUtilities.setWindowOpacity(Window, float) 方法,如图 2 所示:

    图 2. 相同的窗口,但是有 50% 的不透明度

    要使窗口不规则,您可以使用 AWTUtilities.setWindowShape(Window, Shape) 方法,如图 3 所示:

    图 3. 相同的窗口,但是被一个椭圆剪裁

    正如您从图 3 中能看到的,不规则的窗口看起来不是很好。窗口的边缘呈锯齿状并且整体印象也不是很干净。要获得不规则窗口的更佳视觉效果,您必须使用 AWTUtilities.setWindowOpaque(Window, boolean) API,并使用柔性裁剪绘画窗口背景。这在后续的 Swing 窗口的柔性裁剪和每像素透明度 博客条目中进行了阐明。对于窗口的左上角和右上角,该条目采用 Chris Campbell 的 柔性裁剪教程 以及 Romain Guy 的 反射教程, 其中包括 Sebastien Petrucci 的改进。图 4 显示了每个像素透明的柔性裁剪窗口:

    图 4. 柔性裁剪和每个像素透明的窗口

    现在我们手头上已经有了这些 API,我们打算做些什么呢?对它们进行探索这种可能性当然是另人好奇的,我们正打算看看几个多样混合的示例。

    工具提示让我们使应用工具提示变得透明怎么样?对于轻量级工具提示,实现这一目标是相当容易的,因为它们被作为 Swing 顶级窗口的一部分加以绘画。( 要获得关于轻量级弹出菜单的详细信息,请参见 玻璃窗格和轻量级弹出菜单 条目。)但是,一旦工具提示成为重量级并“打破”窗口绑定,您必须继续采用 Robot 或 JNI/JNA.现在让我们看一看使用 AWTUtilities API 如何完成这项任务。

    javax.swing.PopupFactory 是创建弹出菜单的厂。工具提示只是弹出功能的一个例子;其他例子包括组合框下拉列表和菜单。PopupFactory.setSharedInstance API 可以被用于设置自定义弹出厂,这就是我们想要做的。当前的弹出厂被用于创建所有应用弹出窗口,我们将在所有的工具提示上安装自定义不透明厂。

    核心弹出厂的实现是相当复杂的。首先尝试创建轻量级弹出窗口,当要求创建重量级窗口时,系统要管理高速缓存以便重用先前创建的弹出窗口。实现过程将创建一个新的重量级弹出窗口;在相对较新的膝上型电脑上运行不同的方案还未显示任何突出的性能突破。让我们从自定义弹出厂着手研究:

    public class TranslucentPopupFactory extends PopupFactory { @Override public Popup getPopup(Component owner, Component contents, int x, int y) throws IllegalArgumentException { // A more complete implementation would cache and reuse // popups return new TranslucentPopup(owner, contents, x, y); }}TranslucentPopup 的实现相当简单。构造器创建新的 JWindow,将工具提示的不透明度设置为 0.8,从 Looks 项目安装提供拖放阴影的自定义边框:

    TranslucentPopup(Component owner, Component contents, int ownerX, int ownerY) { // create a new heavyweight window this.popupWindow = new JWindow(); // mark the popup with partial opacity com.sun.awt.AWTUtilities.setWindowOpacity(popupWindow, (contents instanceof JToolTip) ? 0.8f : 0.95f); // determine the popup location popupWindow.setLocation(ownerX, ownerY); // add the contents to the popup popupWindow.getContentPane()。add(contents, BorderLayout.CENTER); contents.invalidate(); JComponent parent = (JComponent) contents.getParent(); // set the shadow border parent.setBorder(new ShadowPopupBorder()); }现在我们需要重写 Popup 的 show() 方法来标记整个弹出窗口为透明样式。这要求拖放阴影边框的每个像素具有透明性。

    @Override public void show() { this.popupWindow.setVisible(true); this.popupWindow.pack(); // mark the window as non-opaque, so that the // shadow border pixels take on the per-pixel // translucency com.sun.awt.AWTUtilities.setWindowOpaque(this.popupWindow, false); }hide() 方法只是隐藏并处置弹出窗口:

    @Override public void hide() { this.popupWindow.setVisible(false); this.popupWindow.removeAll(); this.popupWindow.dispose(); }要安装该弹出窗口,仅简单调用

    PopupFactory.setSharedInstance(new TranslucentPopupFactory()); 图 5 显示了一个具有透明工具提示的示例帧。注意,与工具提示保持视觉(透明性和拖放阴影边框)上的一致性跨越 Swing 帧绑定并扩展到后台 Eclipse 窗口。

[1]  

【责编:Ken】

--------------------next---------------------

阅读(196) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~