本文将深入透视 Synth 外观,他是 java 5.0 中为 Swing 引入的最新内容。通过为 Java UI 编程引入“皮肤”的概念,Synth 使研发人员能够为应用程式创建和部署定制的外观。软件工程师 Michael Abernethy 将带您从头开始逐步构建一个具备 synth 外观的应用程式,让您充分了解 Synth 的概念。阅读本文之后,您应该能够在短时间内创建具备专业外观的 UI。
就在 Sun 一如既往地试图“再次引入 Java Desktop”之际,Java UI 研发人员的抱怨之词亦已表面化:要创建完全定制的外观实在太难。这样做不但要花费太多的时间,并且 Swing UI 代码的编写和文档的编制也极为不堪,经常是乱杂一气,缺乏规划。为了创建完整的外观,研发人员需要继续 Metal 外观的 39 个类,或继续 Basic 外观的 60 个类。谁想通过重写整个包来改变应用程式呈现外观的方式呢?用 Swing 创建定制外观有多难,通过下面的事实同样可窥见一斑:在很多研发人员为开源项目添砖加瓦的时代,Internet 上可用的自定义 Swing 外观几乎是凤毛麟角 ―― 总共大约是 20 个,其中少数在 SourceForge.net 上(请参阅参考资料)。
漂亮只是肤浅的东西
进入 Synth,Sun 希望他能使应用程式外观的个性化过程变得轻易。Synth 的目标很简单 ―― 让研发人员不必编写任何代码就能够创建新的外观。这似乎是个不错的解决方案。程式员一般没有突出的艺术才华,而图像设计人员通常也不是 Java 编程专家。Synth 把对外观的任何描述从代码中分离出来,而将其放入外部的 xml 文档和图像文档中,为上述问题提供了大快人心的解决之道。这种完全在外部文档中描述的外观被称作皮肤(SKIN)。
Sun 的皮肤概念并不是什么创新。例如,WinAMP 有数百种皮肤,Firefox 也有几十种皮肤,这些皮肤很轻易创建,只需更改一个 XML 文档即可。想像一下,仅仅修改一个 XML 文档,就能快速、轻易地为 Java 应用程式创建一个外观。再想想这样一来的结果 ―― 几百个互不相同的 Swing 外观。Java UI 研发人员当然有理由欢呼了。
本文将深入分析 Synth 外观,向您展示创建一个完整的外观或皮肤所需知道的一切。您会看到一个带有示例皮肤的应用程式,这个应用程式使用了 Synth 任何重要的概念。然后,我会逐步剖析这个皮肤,在构建 XML 文档的过程中,一一教会您 Synth 的各个概念。
本文最后一节将尽力回答研发人员关于 Synth 性能、bug 和缺陷连同 Synth 在省时方面的表现等种种问题。阅读本文之后,您应该会愿意拥护Synth 作为外观解决方案,并预备马上使用他来创建自己的 Swing 外观。
Synth 基础
Synth 是个白板(tabula rasa)外观 ―― 一块完全空白的画布,表现为一个完全空白的面板(panel),只有在 XML 文档中定义了组件时,他才会显示东西。一旦定义了组件,在应用程式上配置 Synth 外观就再轻易但是了,如清单 1 所示:
清单 1. 配置 Synth 外观
SynthLookAndFeel synth = new SynthLookAndFeel();
synth.load(SynthFrame.class.getResourceAsStream("demo.xml"), SynthFrame.class);
UIManager.setLookAndFeel(synth);
但是,对于 Synth,最重要的是要理解他是 XML 代码,而不是 Java 代码。虽然 Synth XML 格式一开始看上去比较吓人,但实际上很简单。假如使用 KISS (Keep It Simple Stupid)这道符咒,您能够快速地创建一个 XML 文档,并得到一个新的、能够运行的外观。
考虑到 KISS 指令,我将首先介绍 Synth XML 文档的主要构件 ―― <style> 标签。<style> 标签包含描述一个组件的式样的任何信息,例如颜色、字体、图像文档、状态,连同一些特定于组件的属性。虽然一个 <style> 标签能够描述多个组件,但构建 Synth 文档的最简便方法是为每个 Swing 组件创建一个式样。
创建好式样之后,便能够将式样链接到一个组件。<bind> 标签通知 Synth 引擎将一个已定义的式样链接到一个组件,如清单 2 所示。这样的组合便完全创建了组件的新外观。
清单 2. 将一种式样链接到一个组件
<style >
图 1. 具备 Ocean 外观的 Demo 应用程式
图 2. 具备 Synth 外观的 Demo 应用程式
更改颜色和字体
为 demo 应用程式创建外观的第一步是配置默认颜色和字体。您将把 white Aharoni 字体作为每个组件的默认字体,假如没有非凡配置组件的话,就使用这种字体。
您能够将更改字体的 XML 放在 <style> 标签内的任何地方。还能够将颜色嵌入到一个 <state> 标签中。在本文的后面部分,我将更具体地讨论 <state> 标签,但现在只需知道,一个简单的、不带属性的 <state> </state> 标签能够包含任何状态,这个标签正是您在这里所需要的。
color 标签本身需要两个属性:
value 能够是 java.awt.Color 常量的任何 String 表示(例如 RED、BLUE),或,他能够是一种颜色的十六进制表示,前面加上 "#" (例如#669966)。
type 描述文档应该配置哪个区域的颜色。选择有 BACKGROUND、FOREGROUND、TEXT_FOREGROUND、TEXT_BACKGROUND 和 FOCUS。
font 标签有两个必需的属性和一个可选属性。这三个属性直接映射到 java.awt.Font 类中的三个参数:
name :字体的名称(例如,Verdana、Arial)。
size :字体大小,以像素为单位。
style :假如不使用这个可选标签,那么将得到常规外观的字体。其他选项包括 BOLD 和 ITALIC。您还能够通过在这两个属性之间加一个空格来指定粗体加斜体的字体:BOLD ITALIC(这种组合属性的技术对于 Synth XML 文档中的任何属性都适用)。
最后,通过使用 .* wildcard,将这个式样绑定到应用程式中的每个组件,而不是将其绑定到每个 JLabel 和每个JButton。这个通配符告诉 Synth 外观为每个组件指定一个默认的 white Aharoni 字体。清单 3 展示了用于配置组件字体和颜色的完整 XML 代码:
清单 3. 更改多个组件的字体和颜色
<style >
图 3. 在 Synth 中图像怎样拉伸
图 3 中绿色填充区只会垂直拉伸。也就是说,当文本域比图像高的时候,这些区域就会变高。当文本域比图像长的时候,那些红色填充区只会水平拉伸。而黄色填充区则是大小固定的。不管文本域的大小怎样,这些区域都会如他们在图像文档中那样显示。因为这些区域不会拉伸,因此他们应该包含任何画布、非凡底色、阴影和任何一旦拉伸就会看起来很古怪的东西。最后,中间区域是可选的。您能够选择画出或忽略该区域。在我们的例子中,文本域的中间被忽略。此后,呈现程式使用这个区域来处理文本控制和 carat。也就是说,使用一个图像文档完全画出文本域。
imagePainter 标签提供了在外观中使用图像所需的任何信息。他只需要几个属性:
path :所使用的图像的路径。
sourceInsets :按像素计算的 insets,表示图 3 中绿色区域的宽度和粉红色区域的高度。他们依次映射到顶部、左部、底部和右部。
method :这也许是最令人费解的属性。他直接映射到 javax.swing.plaf.synth.SynthPainter 类中的一个函数。这个类包含大约 100 个函数,任何这些函数都以 paint 开始。每个函数映射到在一个 Swing 组件中某个特定的绘画任务。您只需找到一个合适的函数,然后去掉 paint 字符串,并使随后的首个字母为小写形式,便能够配置该属性。例如,paintTextFieldBorder 是 textFieldBorder 的属性。呈现程式(renderer)负责剩下的工作。
paintCenter :该属性答应您保留或舍弃图像的中间区域(例如在一个按钮中)。在这个例子中,textfield 舍弃了中间区域,以便显示文本。
使用图像画边框的最后一步是加大默认的 insets,以便处理用来画这些 insets 的图像。假如没有更改 insets,那么就看不见任何图像。您需要添加一个 <insets> 标签来增加 insets,以便在其中画出图像。在大多数情况下,insets 的值应该和在图像中使用的 insets 的值相同。
清单 4 展示了用于装载图像的 XML 代码。注重 sourceInsets 怎样确保图像只有适当的部分被拉伸。
清单 4. 装载图像
<style >
图 4. DEFAULT 状态下的 Cancel 按钮
图 5. MOUSE_OVER 状态下的 Cancel 按钮
<state> 标签只需要一个 value 属性,该属性定义了实际的组件状态。假如没有指定 value,如清单 3 和 4 所示,那么每种状态都使用默认值。假如指定 value 属性,那么能够选择 ENABLED、MOUSE_OVER、PRESSED、DISABLED、FOCUSED、SELECTED 和 DEFAULT。这些选择包含 Swing 中任何组件任何可能的状态。您还能够在不同选择间添加 and 来组合各种状态。例如,假如您想在鼠标位于按钮之上连同按钮被按下的时候改变按钮上的字体,那么能够使用状态值 MOUSE_OVER and PRESSED。
清单 5 展示了用于处理 demo 应用程式状态的 XML。注重每种状态是怎样定义不同的图像和文本颜色的。
清单 5. 处理状态
<style >”之类的责骂之词也将永远消失。