工具提示
让我们使应用工具提示变得透明怎么样?对于轻量级工具提示,实现这一目标是相当容易的,因为它们被作为 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 窗口。
图 5. 工具提示
现在我们做相同的动画。当工具提示显示时将颜色调淡些,当它被隐藏起来时把它的颜色渐隐如何?一旦您熟悉了 AWTUtilities API,上述操作不难实现。下面给出 show() 方法的代码:
@Override public void show() { if (this.toFade) { // mark the popup with 0% opacity this.currOpacity = 0; com.sun.awt.AWTUtilities.setWindowOpacity(popupWindow, 0.0f); } 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); if (this.toFade) { // start fading in this.fadeInTimer = new Timer(50, new ActionListener() { public void actionPerformed(ActionEvent e) { currOpacity += 20; if (currOpacity <= 100) { com.sun.awt.AWTUtilities.setWindowOpacity(popupWindow, currOpacity / 100.0f); // workaround bug 6670649 - should call // popupWindow.repaint() but that will not repaint the // panel popupWindow.getContentPane().repaint(); } else { currOpacity = 100; fadeInTimer.stop(); } } }); this.fadeInTimer.setRepeats(true); this.fadeInTimer.start(); } }
这时我们用 0% 的不透明度标记弹出窗口。然后我们启动重复计时器进行五次迭代。每一次跌代我们增加窗口不透明度 20% 并重新绘画。最后我们停止计时器。最终的视觉结果是工具提示外观的平滑退色序列,这一序列持续大约 250 毫秒。
hide() 方法非常类似:
@Override public void hide() { if (this.toFade) { // cancel fade-in if it's running. if (this.fadeInTimer.isRunning()) this.fadeInTimer.stop(); // start fading out this.fadeOutTimer = new Timer(50, new ActionListener() { public void actionPerformed(ActionEvent e) { currOpacity -= 10; if (currOpacity >= 0) { com.sun.awt.AWTUtilities.setWindowOpacity(popupWindow, currOpacity / 100.0f); // workaround bug 6670649 - should call // popupWindow.repaint() but that will not repaint the // panel popupWindow.getContentPane().repaint(); } else { fadeOutTimer.stop(); popupWindow.setVisible(false); popupWindow.removeAll(); popupWindow.dispose(); currOpacity = 0; } } }); this.fadeOutTimer.setRepeats(true); this.fadeOutTimer.start(); } else { popupWindow.setVisible(false); popupWindow.removeAll(); popupWindow.dispose(); } }
首先检查退色序列是否仍在运行,根据需要将它删除。然后,不立即隐藏窗口,而是将不透明度以 10% 的增量从 100% 改为 0(因此渐隐序列是退色序列的两倍)然后隐藏并处置弹出窗口。注意两种方法参阅了 Boolean toFade 变量 —— 它在工具提示上被设置为 true。弹出窗口的其他类型(菜单、组合框下拉列表)没有退色动画。