用IFrame跟上潮流-将IFrame窗口转换为炫目的应用程序窗口

IFrame. 是一种应用程序窗口体系结构,它可以有自定义颜色、自定义边框、 自定义形状、自定义组件、甚至透明性。有了 JFrame,就不需要在应用程序中总 是使用乏味的、老的 JFram 了。在本文中,IT 专家 Michael Aberethy 介绍了 IFrame. 类,并展示了如何用它将标准 JFrame. 应用程序窗口立刻转换为先进的 IFrame。

自 Java 1.0 开始,UI 开发人员就一直在寻找自定义应用程序窗口的方法。 在引入了 Swing 后,这个问题愈发突出了,因为开发人员可以创建具有令人惊叹 的更复杂的、更高级 widget,但是却受到它所在的 Frame. 或者 JFrame. 和它们 的特定于操作系统的外观的限制。常常可以看到应用程序在框架边框内部看起来 很好,但是 Microsoft Windows 的蓝色标题栏却大刹风景的情况。更糟的是,应 用程序有 70 年代的 Motif 外观,但却使用了 Microsoft Windows 的颜色光滑 渐变的标题栏。

JFrame. 的外观问题

如图 1 所示的这些外观问题,展现了 UI 开发人员所面临的许多问题:应用 程序在各个平台上看起来是不一样的,因为 JFrame. 的属性(标题栏颜色、边框 、形状等)无法使用公共外观,它们是特于定操作系统的。

图 1. JFrame. 的外观是如何变化的

Metal 外观

Motif 外观

Windows 外观

解决方案看起来很很明显:一个独立于操作系统的框架,它具有 JFrame. 提供 的所有功能——注册到 OS 窗口管理器、调整大小/重绘管理、最大化/最小化/恢 复,同时又可以设置这些组件的外观。Java 1.3 提供了 JFrame. 的功能,但牺牲 了定制能力。

在 Java 1.4 中,Sun 在 Frmae 类中引入了一个新的 setUndecorated() 函 数,它走到了另一个极端——它可以自定义框架中的所有内容,但是缺少了 Java 1.3 中大多数特定于操作的好处。

IFrame. 类是对这个问题的期待已久的解决方案。通过弥合这两个问题之间的 距离,IFrame. 提供了一个使用简单、同时又很强大的解决方案,可以根据需要对 框架进行或多或少的自定义,同时不会牺牲使用 JFrame. 时所习惯的任何特定于 操作系统的功能。不用再守着同样的陈旧的蓝色标题栏、同样的陈旧的三个窗口 按钮和同样的陈旧的斜面边框。虽然对于普通应用程序来说,JFrame. 仍然很有用 ,但是有些应用程序需要比它所能提供的更多的功能,如:

所有具有自定义外观的应用程序。

所有用于体现公司的某种品牌形象的应用程序。

所有需要对其窗口具有更多控制的应用程序。

IFrame. 通过提供一个容易使用的 API 而突破了 JFrame. 的限制,这个 API 可以生成具有任何状态的框架、可以有任何颜色和状态的标题栏、可用于更多功 能的标题栏按钮、具有任何颜色和大小的边框、按钮旁边的组件和标题栏中的标 签及甚至是透明性。

本文介绍 IFrame. 框架。首先描绘这个体系结构中的每一个类及其作用。当然 ,学习如何使用 IFrame. 的最好方法是使用例子,所以我举了四个使用 IFrame. 的应用程序的例子,它们具有不同的复杂性。阅读了本文及学习 IFrame. 体系结 构后,应当能够让应用程序窗口从乏味窗口(图 2)变为炫目窗口(图 3)。

图 2. 使用 IFrame. 之前

图 3. 使用 IFrame. 之后

IFrame. 体系结构

IFrame. 体系结构非常容易和直观,这可以从图 4 展示的类图中看出来。用于 作为应用程序窗口的主类是 IFrame. 。IFrame. 包含类 IBorderPanel 的一个到多 个实例,这个类的子类包括 IWindowTitleBar 和 IContentPane 。 IBorderPanels 通过触发 WindowChangeEvents 与 IFrame. 通信,IFrame. 对它进 行处理并作出响应。

通过分析体系结构中每一个组件,就会清楚为了定制自己的 IFrame,需要在 什么地方和什么时候改写默认功能。

图 4. IFrame. 体系结构

IFrame

IFrame. 是用于创建自定义框架的主类。除了从 IFrame. 的父类 JFrame. 继承 的方法外, IFrame. 还增加了几个 public 方法,可以用来改变 框架的基本外观 组件。

IFrame. 的默认行为与 JFrame. 一样,所以 JFrame. 和默认 IFrame. 可以互换 。不过,通过调用 IFrame. 中可以使用的几个 public 方法,可以只用几行代码 迅速改变窗口的外观。

注:在 JFrame. 对应的 getContentPane() 和 setContentPane() 的位置上应 当使用 IFrame. 的 getIContentPane() 和 setIContentPane() 方法。在 IFrame. 中添加的所有组件都应当调用 myIFrame.getIContentPane().add() 。用 getContentPane() 在 IFrame. 中添加组件或者用 setContentPane() 设置内容窗 格会导致不可预测的、并且很可能是错误的行为。

IBorderComponent

IBorderComponent 是在 IFrame. 中加入的所有可以控制 Windows 的位置和调 整大小的 JPanel 的父类。乍看之下,最可能的子类是 IWindowTitleBar 和 IContentPane ,因为它们看起来是惟一进行调整大小和移动的类。不过在当前的 应用程序中,有许多是用应用程序窗口内的组件调整应用程序窗口大小或者移动 它的。例如,所有 Microsoft Office 应用程序现在都在应用程序窗口右下角有 一个小的 widget,可以用来调整大小(可以去自己试一试)。所以如果设计一个 包含所有类型的调整大小和移动窗口的 widget,那么所要做的就是继承 IBorderComponent ,这些 widget 就会具有与用 IWindowTitleBar 和 IContentPane 得到的同样的移动和调整大小的能力。

IWindowTitleBar

自定义框架的大部分工作都是在 IWindowTitleBar 中进行的。与 IFrame. 一 样, IWindowTitleBar 有许多 public 方法,不用继承它或者自己编写方法就可 以改变标题栏的属性。虽然用这些方法可以容易地对外观进行改变,但是所有高 级的设计都应当继承 IWindowTitleBar 以使用 paintComponent() ,这个方法可 以做出更复杂的标题栏。(关于复杂的标题栏以及它是如何继承 IWindowTitleBar 的内容,请参阅下面 例 3。)

除了改变标题栏的背景颜色, IWindowTitleBar 还提供了许多功能,可以用 来控制标题栏中的 IWindowButtons 。在默认情况下, IWindowTitleBar 包含 Microsoft Windows 应用程序中可以看到的三个标准窗口按钮,但是可以容易地 删除它们或者添加自己的具有不同功能的窗口按钮。可以改变按钮的大小和颜色 ,如果创建高级的窗口按钮,那么可以继承 IWindowButton 以绘制所希望的任何 形状和颜色方案(尽管改变 JButton 的外观超出了本文的范围)。

也许自定义 IWindowTitleBar 的最有技巧性的方面是管理边框。在默认情况 下,窗口标题栏是标准的矩形边框,但是如果创建一个具有非标准边框的复杂标 题栏(请参阅 例 4),那么必须继承 IWindowTitleBar 并覆盖 isMouseOnBorder() 和 isInsideTitleBar() ,以自己管理边框。

IContentPane

IContentPane 提供了在其中加入所有应用程序组件的基本容器,很像 JFrame. 中的 JFcontentPane。 因为它也继承了 IBorderComponent ,所以在默认情况下 它也管理自己的边框。 IContentPane 边框的默认实现也是矩形。要想得到非矩 形的复杂边框,需要继承 IContentPane 并通过 重载 isMouseOnBorder() 自己 处理边框。

IWindowButton

IWindowButton 提供了出现在标准 Microsoft Windows JFrame. 标题栏中的三 个按钮(最小化、还原和关闭)的默认实现,但是它们还为想要在标题栏中创建 和添加的所有自定义窗口按钮提供了基类。如果希望在标题栏中加入第四个按钮 (如果认为自己比 Microsoft 更了解情况),那么可以继承 IWindowButton 并 重载 paintComponent() 。不过,对于创建自定义按钮的介绍不在本文的范围之 内。

WindowChangeEvent/WindowChangeListener

Swing 为窗口中会发生的几乎所有事情提供了事件。有大约 15 种事件和处理 窗口事件的相应方法。但是,就算有了所有这些事件,Swing 也没有包括窗口可 以生成的所有事件,最明显的是特定于操作系统的事件。最后,使用 WindowChangeEvent 和 WindowChangeListener 类,可以确保接收窗口会发生的 所有事件。

WindowChangeEvent 处理所有窗口变化的情况(因而类的名字变化)。它可以 改变其大小、在屏幕上定位、恢复状态或者最小化/最大化状态。将这五个事件加 上 15 种已有的事件,就包括了所有基本情况。现在窗口中发生的所有事情都可 以掌握了。

例子

现在可以编写几行代码并改变应用程序窗口的整个外观、亲自体验 IFrame. 的 强大功能了。记住,IFrame. 可以很简单,也可以很复杂,完全取决于您的需要。

在这一节,我将完成几个展示开始使用 IFrame. 所需要完成的基本步骤的例子 。学习这些例子并在自己的计算机中运行它们,会看到仅凭阅读说明或者 API 所 想像不到的效果。运行所有四个例子并分析每个例子的代码,我相信您将会理解 为什么 IFrame. 可以成为应用程序中一个强大的工具。

所有例子都包含在 com.ibm.iwt.examples 包中,可以从 参考资料 中下载这 个包,它们都有可以运行的 main() 方法。它们是用 JDK 1.4 编写的。

例 1:默认 IFrame

为了保持 IFrame. “向后兼容”,我让 IFrame. 的默认实现看上去与 JFrame. 的完全一样,如图 5 所示:

图 5. 默认 IFrame

因为 IFrame. 不从本机操作系统中得到其信息,所以我只能选择一种操作系统 进行模拟。默认的 IFrame. 实现看起来就像在 Microsoft Windows 2000 中的 JFrame. 一样,我们就保持使用它了。如果在 Windows 2000 计算机中运行应用程 序,那么将可以互换 JFrame. 与 IFrame,不会有看得出来的差别。如果运行的不 是 Windows 2000 — 那么,第一个练习应用程序可以是模拟自己的操作系统。清 单 1 显示了创建一个 IFrame. 是多么容易:

清单 1. IFrame. 例 1 public TestApp1()  {   setTitle("Window");  }

是的,就是这么容易(想象一下如果所有应用程序开发都这么容易,那该会怎 样)。

建议用法:在希望向后兼容 JFrame. 时。

例 2: 改变默认颜色、边框和大小

现在看一些更有意思的代码。在这个例子中,我将标题栏框架周围的边框的背 景颜色改为红色,改变窗口按钮的颜色、还改变了标题栏和窗口按钮的大小。图 6 显示了在例 2 中创建的 IFrame。

图 6. 改变颜色、边框和大小

仅就所说的这些改变,可以看出它们在 JFrame. 中都是不可能的,但是用 IFrame. 就可以很容易地实现。清单 2 显示了如何创建例 2 中使用的 IFrame:

清单 2. IFrame. 例 2 public TestApp2()  {   IWTUtilities.setBorderSize(new Insets(3,3,3,3));   setIContentPaneBorder(new LineBorder(Color.red, 3));   setTitleBarHeight(35);   setTitleBarBackground(Color.red);   setTitleBarButtonColors(Color.red, Color.white);   setTitleBarButtonSize(new Dimension(26, 26));   setTitle("Window");  }

这样就行了。改变框架的外观所要做的就是这些。尽管这只是 IFrame. 的一个 基本的例子,只使用了六行代码,但是我们完成了一些 UI 开发人员多年来一直 想要做的事情。这个基本的例子已经比当前使用的应用程序窗口中的 99% 都更先 进。

建议用法: 如果希望迅速改变框架的外观,同时又不想使它与特定于操作系 统的框架有大的改变时使用。

例 3: 利用 IWindowTitleBar 的子类

如果希望做比颜色、大小和标题栏中的按钮这样的基本改变更多的事情,就必 须继承 IWindowTitleBar 类以充分利用它提供的各种可能性。创建了子类后,就 可以对标题栏做很多新的操作了,包括更高级的绘制选项以及更强大的、在标题 栏中加入任何组件的能力。为什么让标题栏中的按钮和标签把自己限制住呢?加 上一直想要的 JTable 吧。只要调用 IFrame. 中的 setTitleBar() ,就可以创建 一个应用程序开发史上最先进的标题栏子类,并在任何 IFrame. 上使用它。图 7 描绘了创建自定义窗口组件所可能产生的外观:

图 7. 创建自定义窗口组件

在这个例子中,通过建立 清单 2 中的框架,并用一个新的、动态的边框取代 单调的、静态的红色标题栏,充分利用了所有这些新的可能性。可以从图中看到 ,标题已经从左边移到了中间,并使用了更有可读性的字体。我用一个在左边的 “关闭”按钮取代右边三个标准按钮。最后,也许是最有创造性的,我在标题栏 的右边增加了一个 JSlider,可以让这个 IFrame. 的用户动态改变标题栏背景的 渐变色。清单 3 中的代码片段显示了将例 2 转变为例 3 所需要的额外代码。这 些对于 JFrame. 来说是不可能的。

清单 3. IFrame. 例 3 public TestApp3()  {   IWTUtilities.setBorderSize(new Insets(3,3,3,3));   getIContentPane().setBorder(new LineBorder(Color.red, 3));   setTitleBar(new TitleBar());  }  private class TitleBar extends IWindowTitleBar implements ChangeListener  {   private Color c = new Color(0,0,0);   private JSlider slider;   public TitleBar()   {    setPreferredSize(new Dimension(0, 26));    removeWindowDecorations();    addWindowButton(IWindowButton.CLOSE, SwingConstants.LEFT);    setWindowButtonColors(Color.RED, Color.WHITE);    addTitle(getTitle(), SwingConstants.CENTER, new Font ("Verdana", Font.BOLD, 14), Color.WHITE);    slider = new JSlider();    add(slider, new GroupFlowLayoutConstraints (SwingConstants.RIGHT, new Insets(3,3,3,3)));    slider.addChangeListener(this);    slider.setMaximum(255);    slider.setMinimum(0);    slider.setOpaque(false);   }   public void paintComponent(Graphics g)   {    super.paintComponent(g);    PaintUtilities.paintGradient(g, 0, 0, getWidth(), getHeight(), c, Color.WHITE,    SwingConstants.HORIZONTAL);   }   public void stateChanged(ChangeEvent e)   {    c = new Color(slider.getValue(), 0, 0);    repaint();   }  }

分析创建这个 IFrame. 的代码,可以看到它不比 清单 2 中的代码更复杂。不 过,出于下面两个理由,我将所有代码移到了 IWindowTitleBar 的子类中:

通过 重载 IWindowTitleBar 中的 paintComponent() 提供外观更精致的标题 栏

加入动态改变标题栏背景颜色的 JSlider

因为可以在任何位置上添加任何 JComponent,所以在为标题栏创建新 widget 时可以尽情发挥想象力。对于在标题栏中创建新功能这方面来说,改变背景颜色 的 JSlider 只是冰山的一角。可以开发出许多在标题栏中使用的有创造性的自定 义组件。

建议用法: 适合使用 IWindowTitleBar 的子类的情况有:

希望在标题栏中创建更复杂的图像,而不是一种单纯的颜色

常常会希望动态改变框架的标题栏属性,并且不希望每改变次它们时调用多个 函数

希望在标题栏中加入默认组件以外的其他组件

例 4:结合在一起并加上透明性

最后一个例子将其他例子结合到一起并加入了 IFrame. 的最新特性——透明性 。这个例子是最复杂的,并且很好地体现了 IFrame. 在用最少的工作创建具有出 色外观的应用程序窗口方面的强大能力。图 8 显示了具有某种透明性的复杂应用 程序窗口,这种透明性使它区别其他应用程序窗口。

图 8. 加入透明性

首先,让我们介绍一下透明性。几年前,Microsoft Windows 应用程序开始有 了标准矩形以外的框架。其中使用最多的就是 Windows Media Player,从那之后 ,使应用程序具有非矩形形状就成了一种趋势和很酷的事情。是的,Java 应用程 序一直没有这种能力,并且在透明性方面总是差强人意,特别是当与本机绘制像 素交互时。

幸运的是,IFrame. 改变了这种局面,可以开发具有透明性、甚至对于本机绘 制像素透明的应用程序窗口。IFrame. 中的 setTransparent() 在指定的边界内绘 制指定的组件透明性。在大多数情况下,组件将是 IWindowTitleBar 或 IContentPane 的子类。应当在子类的 paintComponent() 中调用 setTransparent() ,以使它可以用它下面的正确像素重绘。

最后提醒一下,绘制透明性速度相对来说是慢的,应当尽可能使透明区域相对 较小。

最后这个例子使用了 IFrame. 的其他更高级的功能。从 清单 8 中可以看到, 标题栏不再是标准的矩形标题栏了。它是自定义的形状,具有完全不同于矩形的 边框。因此,在所创建的 IWindowTitleBar 子类中,必须 重载 isMouseOnBorder() 和 isInsideTitleBar() 方法,以使标题栏在绘制光标时具 有正确的行为,并可以调整大小。清单 4 显示了生成例 4 中看到的应用程序窗 口所需要的代码。

清单 4. IFrame. 例 4 public TestApp4()  {   setTitle("Window");   IWTUtilities.setBorderSize(new Insets(0,7,7,7));   IWTUtilities.setDiagonalSize(20);   getIContentPane().setBorder(new AppBorder());   getIContentPane().setBackground(new Color(255, 255, 102));   setTitleBar(new TitlePanel());  }  private class TitlePanel extends IWindowTitleBar  {   public TitlePanel()   {    setPreferredSize(new Dimension(800,35));    setFont(new Font("Verdana", Font.BOLD, 22));    removeWindowDecorations();   }   protected boolean isInsideTitleBar(int x, int y)   {    if (x (int)getWidth()*.9)      return false;    return true;   }   protected void isMouseOnBorder(int x, int y)   {    if (y > 10 && y > 16 && ! isInsideTitleBar(x, y))     isMouseOnBorder = true;    else     isMouseOnBorder = false;   }   public void paintComponent(Graphics g)   {    super.paintComponent(g);    // ... paint code here    setTransparent(this, g, 0, 0, w+1, 10);    PaintUtilities.paintDropShadow(g, (int)(w*.1), 0, (int)(w*.8), 27);    Color c1 = new Color(67, 118, 135);    Color c2 = new Color(105, 152, 199);    PaintUtilities.paintGradient(g, (int)(w*.1), 0, (int)(w*.9), 14, c1, c2);    PaintUtilities.paintGradient(g, (int)(w*.1), 14, (int)(w*.9), 13, c2, c1);    Graphics2D g2 = (Graphics2D)g;    g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);    g.setColor(Color.white);    int strW = SwingUtilities.computeStringWidth(g.getFontMetrics (), getTitle());    int strH = g.getFontMetrics().getMaxAscent();    g2.drawString(getTitle(), w/2-strW/2, h-strH/2);    g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);    }   private void drawBorder(Graphics g, int x, int y, int w, int h)    {    g.drawLine(x, 10+y, x, h);    g.drawLine(x, 10+y, w-x, 10+y);    g.drawLine(w-x, 10+y, w-x, h);   }  }  private class AppBorder extends AbstractBorder  {   public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)   {    // ... paint border   }  }

建议用法:创建一个非百分之百矩形的框架是当前 UI 开发中的一种趋势(仅 就 Windows XP 而言)。使用 IFrame. 后,Java 应用程序就不会落伍了。使用透 明性并 重载 IWindowTitleBar 及其所有高级函数,就可以创建具有非常精致外 观的框架,可以作为整个公司应用程序的默认框架。先进的功能使 UI 开发人员 可以开发出这样的框架,它可以使用户自动与某家公司关联到一起(而不只限于 那种使用户自动关联到 Remond,Washington 的某家公司的框架)。

结束语

通过让 UI 开发人员可以完全控制他们的框架的功能 和和外观,IFrame. 最终 弥补了 Java 开发中的缺撼。它使 UI 开发人员可以创建只改变标题栏字体的简 单 IFrame,也可以创建改变整个公司外观的复杂 IFrame。IFrame. 的好处在于开 发人员容易使用。它提供了开发人员改变框架所需要的所有功能,而且还非常易 于扩展,使开发人员可以只改变需要改变的地方,而不会干扰其他默认行为。

从我们完成的这些例子中可以看到,框架可能有的外观只受我们的想象力的限 制。我相信在阅读过程中,您会在脑子里产生一些想法,希望读过本文后,可以 用 IFrame. 很快地将这些想法落实到屏幕上。

我很想知道您开发出了什么样的 IFrame,所以请将您得到的任何 IFrame. 的 屏幕快照发给我。真想看到其他人是如何利用 IFrame. 的。

本文配套源码

即将转出来的那一面,是快乐或痛苦,是爱还是恨。

用IFrame跟上潮流-将IFrame窗口转换为炫目的应用程序窗口

相关文章:

你感兴趣的文章:

标签云: