尽管Sun继续尝试“重新引入Java桌面”,但Java UI开发人员不断抱怨:创建一个完整的自定义外观太困难了。 这不仅耗费了时间,而且Swing UI代码的编写和记录也很差,常常看起来被黑在一起,计划不周。 要创建完整的外观,开发人员必须将Metal外观的39类或Basic外观的60类作为子类。 谁想要覆盖整个程序包以更改应用程序的外观? 在Swing中创建自定义外观有多么困难的另一个可靠指标是,在这个时代,许多开发人员免费为开源项目捐赠辛勤工作,互联网上几乎没有任何自定义的Swing外观-总共约20个,仅包括SourceForge.net上的少数几个(请参阅参考资料 )。
美容唯一的皮肤深层
输入Synth,Sun希望它可以简化个性化应用程序外观的过程。 Synth的目标很简单-让开发人员无需编写一行Java代码即可创建新的外观。 这似乎是一个很好的解决方案。 通常,程序员不是最有艺术性的人,而图形艺术家通常也不是Java编码方面的专家。 通过从代码中删除外观的整个描述并将其放置在外部XML文件和图像文件中,Synth提供了令人满意的折衷方案。 仅在外部文件中描述的这种外观称为皮肤 。
Sun并没有在皮肤方面开创任何新天地。 例如,Winamp有数百种外观,而Firefox有数十种外观,因为它们很容易创建,只需要更改XML文件即可。 想象一下,只需修改XML文件,即可快速轻松地为Java应用程序创建外观。 然后想象可能的结果-数百种独特的Swing外观。 Java UI开发人员有理由庆祝。
本文深入研究了Synth的外观。 我将向您展示创建完整外观或皮肤所需的一切。 您将检查一个使用Synth所有重要概念的外观皮肤应用程序示例。 然后,我将逐步分解内容,以在构建XML文件时教您Synth概念。
本文的最后一部分试图回答开发人员有关Synth性能,错误和缺陷以及Synth提供的节省时间的许多问题。 阅读本文之后,您应该愿意将Synth用作外观解决方案,并准备立即使用它来为Swing创建自己的皮肤。
合成器基础
Synth是一种表格形式的外观-一种完全空白的画布,显示为全白色面板,直到在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(保持简单愚蠢)的口头禅,则可以快速创建XML文件并获得新的外观并开始运行。
考虑到KISS指令,我将首先介绍Synth XML文件的主要构造块- <style>
标记。 <style>
标记包含描述组件样式所需的所有信息,例如颜色,字体,图像文件,状态和特定于组件的属性。 尽管单个<style>
标签可以描述多个组件,但是构建Synth文件的最简单方法是为每个单独的Swing组件创建一种样式。
完成样式创建后,将样式链接到组件。 <bind>
标记告诉Synth引擎将定义的样式链接到组件,如清单2所示。这种组合完全创建了组件的新外观。
清单2.将一种样式链接到一个组件
<style id="textfield">// describe colors, fonts, and states
</style>
<bind style="textfield" type="region" key="Textfield"/><style id="button">// describe colors, fonts, and states
</style>
<bind style="button" type="region" key="Button"/>
关于所述一个音符<bind>
标记:该key
内部属性<bind>
标签映射到在常量javax.swing.plaf.synth.Region
类。 Synth引擎使用这些常量将样式与实际的Swing组件链接在一起。 简单的组件(例如JButton
和JTextField
)使用一个常量。 一些更复杂的组件,例如JScrollBar
和JTabbedPane
,对于它们的不同部分具有多个常量。
我建议您使用每个组件一个样式的设置,直到您更熟悉Synth格式并可以在XML中设置继承模型为止。 这种结构没有利用XML的所有分层功能,但它是设置,编码和调试最简单的方法。
处理Synth XML文件时要记住的另一个重要点是,它不验证任何内容。 如果您犯了印刷错误或在XML中使用了不正确的属性,则只有在加载外观后引发运行时异常时,您的错误才会显示出来。 转换:在将XML文件发送给客户之前对其进行测试。
演示应用
我将引导您构建一个简单的登录屏幕作为示例应用程序,向您展示Synth XML文件的工作方式。 屏幕上提供了足够的组件,您可以看到XML文件的所有重要部分以及它们如何组合在一起以创建整体外观。
通过比较图1和图2可以看到,Ocean外观的登录屏幕看起来像您期望的那样-简单,直接和无聊。 Synth外观中的登录屏幕完全不同。
图1.具有海洋外观的演示应用程序
图2.具有Synth外观的演示应用程序
更改颜色和字体
为演示应用程序创建外观的第一步是设置默认的颜色和字体。 您将使白色Aharoni字体成为每个没有特别设置的组件的默认字体。
您可以将XML放在<style>
标记内的任何位置以更改字体。 您将颜色嵌入到<state>
标签中。 我将在本文的后面部分讨论<state>
标记,但是现在足以理解,一个没有属性的简单<state>
</state>
标记包含每个状态,这就是您所需要的。
color
标签本身需要两个属性:
-
value
可以是java.awt.Color
常量的任何String
表示形式(例如RED
,BLUE
),也可以是以“#
”#669966
的颜色的十六进制表示形式(例如#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外观,为每个组件提供默认的白色Aharoni字体。 清单3显示了用于设置组件的字体和颜色的完整XML代码:
清单3.更改多个组件的字体和颜色
<style id="default"><font name="Aharoni" size="14"/><state><color value="#FFFFFF" type="FOREGROUND"/></state>
</style>
<bind style="default" type="region" key=".*"/>
使用图像
图2中的textfield
边框不是看起来正常的单像素矩形边框。 您通过使用图像创建它们。 这不是一个陌生的概念-图像已经在按钮和标签中使用了一段时间了-但是您可以想象出现一些问题的地方。 光标如何知道要去哪里,如何绘制文本以及如何创建不同大小的文本字段? 通过图像拉伸的概念解决了这些问题。 一个图像文件必须描述应用程序中文本字段的每种大小,因此您需要一种方法来告诉XML文件如何正确拉伸图像以及如何处理正常的文本textfield
活动(克拉和文本控件)。
幸运的是,自从早期蒙皮应用以来,已经出现了执行此类拉伸的方法。 图像必须分为9个区域-顶部,右上,右,右下,底部,左下,左,左上和中心-由XML文件中的属性指定。 然后,渲染器可以某种方式拉伸图像以适合分配的空间。 图3显示了文本字段图像如何延伸。
图3.如何在Synth中拉伸图像
图3中的绿色区域仅在垂直方向上延伸。 也就是说,当文本字段比图像高时,它们会增长。 当文本字段比图像长时,红色区域只会水平伸展。 染成黄色的区域根本不会生长。 无论文本字段有多大,这些区域都将按照它们在图像文件中的样子准确绘制。 因为这些区域不会被拉伸,所以它们应该包含所有曲线,特殊的着色,阴影以及任何被拉伸后看起来很奇怪的东西。 最后,中心区域是可选的。 您可以指定绘制还是忽略它。 在我们的示例中,省略了文本字段的中心。 然后,渲染器使用此区域来处理文本控件和克拉。 就是这样-文本字段是使用单个图像文件完全绘制的。
imagePainter
标签提供了在外观上使用图像所需的所有信息。 它只需要几个属性:
-
path
:要使用的图像的路径。 -
sourceInsets
:以像素为单位的插图,代表图3中绿色区域的宽度和粉红色区域的高度。它们依次映射到顶部,左侧,底部和右侧。 -
method
:这可能是最令人困惑的属性。 它直接映射到javax.swing.plaf.synth.SynthPainter
类中的函数。 该类包含大约100个函数,所有这些函数都从paint
开始。 每个功能都映射到Swing组件中的特定绘画作业。 您只需要找到合适的一个,然后通过删除paint
字符串并在其后的第一个字母将其首字母小写来设置属性。 例如,paintTextFieldBorder
是textFieldBorder
的属性。 渲染器负责其余的工作。 -
paintCenter
:此属性使您可以保留图像的中心(例如在按钮中)或将其删除。 示例中的文本textfield
被删除以允许绘制文本。
使用图像绘制边框的最后一步是增加默认插图,以处理用于绘制边框的新图像。 如果您不更改插图,则根本看不到任何图像。 您需要添加<insets>
标记以增加插图,以便可以在其中绘制图像。 在大多数情况下,插图的值应与您在图像中使用的插图的值相同。
清单4显示了用于加载图像的XML代码。 请注意sourceInsets
如何确保仅拉伸图像的适当部分。
清单4.加载图像
<style id="textfield"><opaque value="true"/><state><font name="Aharoni" size="14"/><color value="#D2DFF2" type="BACKGROUND"/><color value="#000000" type="TEXT_FOREGROUND"/></state><imagePainter method="textFieldBorder" path="images/textfield.png"sourceInsets="4 6 4 6" paintCenter="false"/><insets top="4" left="6" bottom="4" right="6"/>
</style>
<bind style="textfield" type="region" key="TextField"/>
处理不同的状态
从到目前为止的示例中可以看到, <state>
标签是定义组件的主要重点。 在清单3和4中 ,颜色和字体标签位于<state>
标签内。 现在,我将解释<state>
标记的作用。
默认状态(在<state>
标记中未指定任何属性)足以定义文本字段和标签中的颜色和字体,因为它们的状态不会改变。 但是在状态会发生变化的组件(例如按钮)中,您可以为每个状态定义完全不同的外观。 每个州可以有自己的颜色,字体和图像。 将登录屏幕的默认状态(图4)的“ 取消”按钮与鼠标悬停状态(图5)进行比较。
图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显示了用于处理演示应用程序状态的XML。 注意每个状态如何定义不同的图像和文本颜色。
清单5.处理状态
<style id="button"><state><imagePainter method="buttonBackground" path="images/button.png"sourceInsets="9 10 9 12" paintCenter="true" stretch="true"/><insets top="9" left="10" bottom="9" right="12"/><font name="Aharoni" size="16"/><color type="TEXT_FOREGROUND" value="#FFFFFF"/></state><state value="MOUSE_OVER"><imagePainter method="buttonBackground" path="images/button_on.png"sourceInsets="9 10 9 12" paintCenter="true" stretch="true"/><insets top="9" left="10" bottom="9" right="12"/><color type="TEXT_FOREGROUND" value="#FFFFFF"/></state><state value="PRESSED"><imagePainter method="buttonBackground" path="images/button_press.png"sourceInsets="10 12 8 9" paintCenter="true" stretch="true"/><insets top="10" left="12" bottom="8" right="9"/><color type="TEXT_FOREGROUND" value="#FFFFFF"/></state><property key="Button.margin" type="insets" value="0 0 0 0"/>
</style>
<bind style="button" type="region" key="Button"/>
处理<state>
标记的一个重要方面是知道哪些组件具有哪些状态。 显然,在此示例中,按钮可以具有默认状态,鼠标悬停状态和按下状态。 您还可以为该示例定义一个集中状态和一个禁用状态。 但是,例如,对于面板,选择甚至不适用,而将鼠标悬停在面板上来更改面板的状态将很烦人。
处理特定于组件的属性
定义对每个组件足够通用的XML属性将始终忽略某些特定于组件的属性。 示例包括列表的行高,单选按钮的图标和菜单的箭头图标。 可以定义100多个特定于组件的属性,但是为每个属性定义XML属性将过多。 因此,Synth XML文件使您可以设置特定于组件的属性。 <property>
标记的作用类似于Hashtable
,定义了键/值对以设置属性。
登录屏幕示例的复选框演示了如何对特定于组件的属性进行编码。 通过定义imageIcon
,可以将CheckBox.icon
属性设置为默认状态和选定状态。 就这么简单-就像对100个属性列表进行排序以找到所需属性一样简单。
清单6显示了用于为登录屏幕编码特定于组件的属性的XML。 请注意,您首先定义了imageIcon
。 然后,通过使用图像图标的ID,可以为复选框的每个状态设置一个图标。
清单6.定义特定于组件的属性
<style id="checkbox"><imageIcon id="check_off" path="images/checkbox_off.png"/><imageIcon id="check_on" path="images/checkbox_on.png"/><property key="CheckBox.icon" value="check_off"/><state value="SELECTED"><property key="CheckBox.icon" value="check_on"/></state>
</style>
<bind style="checkbox" type="region" key="Checkbox"/>
与定制画家一起工作
定义图2中的示例登录屏幕的最后一步是绘制带有曲线的渐变背景。 坦白地说,这在XML中看起来很难做到。 但这使我有机会证明Synth不会将您限制在UI设计中仅使用图像和简单的颜色。 您可以使用它来绘制任何东西。
Synth通过子类化SynthPainter
并仅覆盖您希望自定义绘画的特定功能,使您可以覆盖其绘画方法( javax.swing.plaf.synth.SynthPainter
中的javax.swing.plaf.synth.SynthPainter
)。 在该示例中,您需要自定义绘画paintPanelBackground
方法,因为无法以Synth XML格式描述设计。
要使用自定义绘画工具或以任何方式在XML中创建类,请使用<object>
标记。 <object>
标记允许您创建和保留要用来补充Synth渲染的任何Java类。 <object>
标记包含两个元素:
-
class
:应创建的类的全名 -
id
:您将用来在XML文档中引用此类实例的ID名称。
通过使用对象,您不仅可以创建BackgroundPainter
类的实例,该类将绘制背景。 您还可以创建ColorUIResource
类的实例,在其中定义背景色。 想想看:在BackgroundPainter
类本身内部定义背景中使用的颜色将与Synth的目标相矛盾,该目标是在外部XML文件中定义所有内容,而不是在Java文件中对其进行硬编码。
使用自定义绘画程序的最后一步是告诉Synth渲染引擎,您将提供该功能,而不是SynthPainter
类。 在该示例中,您在BackgroundPainter
类中定义paintPanelBackground
函数,然后让Synth使用其SynthPainter
类来定义其余的paint函数。 <painter>
标记使您可以覆盖SynthPainter
函数。 它包含两个元素:
-
method
:自定义画家应该重写的方法。 从使用图像可以知道,您可以在javax.swing.plaf.synth.SynthPainter class
找到这些函数,但应在每个函数的开头删除paint
字符串。 (所以paintPanelBackground
中SynthPainter
应该panelBackground
在XML文件中,例如。) -
id
:对将覆盖该方法的类的引用。
为了在自定义绘画器中使用颜色,必须将它们存储在javax.swing.UIDefaults
类中。 如清单7和8所示,将颜色存储在UIDefaults
非常简单,对于使用UI创建的任何人都应该很熟悉。 您在XML中定义的键将是UIManager
的引用,您可以使用该UIManager
来获取BackgroundPainter
的实际Java代码中的颜色。
清单7显示了在示例应用程序中使用自定义绘画工具的XML代码。 请注意,您必须先定义颜色。
清单7.使用定制画家
<style id="panel"><object id="background" class="demo.synth.BackgroundPainter"/><object class="javax.swing.plaf.ColorUIResource" id="startColor"><int>30</int><int>123</int><int>235</int></object><defaultsProperty key="Panel.startBackground" type="idref" value="startColor"/><object class="javax.swing.plaf.ColorUIResource" id="endColor"><int>1</int><int>20</int><int>80</int></object><defaultsProperty key="Panel.endBackground" type="idref" value="endColor"/><painter method="panelBackground" idref="background"/>
</style>
<bind style="panel" type="region" key="Panel"/>
清单8显示了示例应用程序的自定义绘画类的Java代码:
清单8.用于自定义绘画的Java代码
public class BackgroundPainter extends SynthPainter
{public void paintPanelBackground(SynthContext context,Graphics g, int x, int y,int w, int h){Color start = UIManager.getColor("Panel.startBackground");Color end = UIManager.getColor("Panel.endBackground");Graphics2D g2 = (Graphics2D)g;GradientPaint grPaint = new GradientPaint((float)x, (float)y, start,(float)w, (float)h, end);g2.setPaint(grPaint);g2.fillRect(x, y, w, h);g2.setPaint(null);g2.setColor(new Color(255, 255, 255, 120));g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);CubicCurve2D.Double arc2d = new CubicCurve2D.Double(0, h/4, w/3, h/10, 66 * w, 1.5 * h, w, h/8);g2.draw(arc2d);g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);}
}
更高级的设置
本节介绍了几种技术,这些技术超出了您在登录屏幕示例中看到的技术。 您可能会发现它们对于创建自己的Synth外观很有用。
喷涂非摆动组件
能够更改每个Swing组件的外观是很棒的,但是Synth还需要能够更改其他组件的外观-开发人员已经创建了一些组件来填补Swing留下的空白。 在这种情况下,需要更改<bind>
标记以反映未绘制Swing组件。 type
属性可以具有两个值-如果region
被映射到Swing组件,则为region
如果name
被映射到非swing组件,则为name
。 因此,将<bind>
标记更改为<bind style="mystyle" type="name" key="Custom.*"/>
将会更改其类名称以Custom
开头的每个组件(例如, CustomTextField
或CustomLabel
)以使用mystyle
风格。
样式层次
不同于创建XML文件的KISS样式,您还可以构建样式的层次结构并将其应用于组件。 清单9应该清楚说明这一点。 请注意,Synth使用最后定义的属性来渲染组件。
清单9.层次结构示例
<style id="base"><color value="BLACK" type="BACKGROUND"/><state><font size="14"/></state></style><bind style="base" type="region" key=".*"/><style id="sublevel" clone="base"><color value="RED" type="BACKGROUND"/></style><bind style="sublevel" type="region" key="Label"/>
清单9中的代码为每个组件提供了黑色背景,字体大小为14(标签除外),字体为红色。 通过在sublevel
克隆base
样式,它将复制整个样式。 然后,您可以覆盖所需的任何特定属性。
检查Synth的性能,可靠性和效率
既然您已经了解了如何为Synth创建XML文件以及如何通过更改字体,更改颜色和添加图像来创建自定义外观,您可能已经对Synth产生了疑问。 如果您已经处理Swing一段时间了,那么我确定您首先想到了性能问题。 我已经设计了一些性能测试,这些测试表明Synth不会使您的UI爬行速度变慢。 我还检查了有关Java Synth问题的Java Bug Parade(请参阅参考资料 ),以调查您可能会看到的问题区域(并讨论我在使用它时遇到的问题)。 最后,我将回答您最重要的问题-Synth真的可以节省您的时间吗?
加载这么多图像会使Synth变慢吗?
我创建了两个测试来回答这个问题,让您更好地了解Synth在性能方面如何与其他外观相提并论。 第一次测试将对示例登录屏幕应用程序的加载进行计时。 该测试在Synth中加载六张图像,并将加载时间与开发人员可能创建的平均屏幕的加载时间进行比较。 第二项测试是对加载时间的压力测试-包含100多个组件的框架。
两项测试都会比较Ocean和Motif的外观,以进行比较。 为了做出公平的评估,我在三台机器上运行了这两项测试-Windows XP笔记本电脑,SuSE Linux机器和Red Hat Linux机器。 结果见表1和表2。
表1.登录屏幕的平均加载时间
机器设定 | 海洋 | 主题 | 合成器 |
---|---|---|---|
Windows XP-1.7GHz-2GB RAM | .32秒 | .29秒 | .57秒 |
SuSE Linux 9.0-3.3GHz-2GB RAM | .23秒 | .20秒 | .45秒 |
红帽Linux 3.0-1.4GHz-512MB RAM | .37秒 | .32秒 | .61秒 |
表2. 100个组件屏幕的平均加载时间
机器设定 | 海洋 | 主题 | 合成器 |
---|---|---|---|
Windows XP-1.7GHz-2GB RAM | .33秒 | .32秒 | .34秒 |
SuSE Linux 9.0-3.3GHz-2GB RAM | .23秒 | .23秒 | 。30秒 |
红帽Linux 3.0-1.4GHz-512MB RAM | .40秒 | .40秒 | .43秒 |
如您所见,Synth外观的加载速度仅比Ocean和Motif加载速度稍慢。 但是请注意,登录屏幕的加载速度比压力测试要慢。 乍一看似乎很奇怪,但是仔细检查发现了罪魁祸首。 压力测试不会加载复选框中使用的图像,而登录屏幕会加载该图像。 这得出结论,Synth外观中使用的每个其他图像都会增加加载时间。 使用具有相同映像的一百个组件将比具有包含两个映像的两个组件的应用程序更快地加载。 减少使用的图像数量将提高Synth加载时间的性能。
Synth是否像Swing初次发行时一样容易出错?
根据Sun Java开发人员网站上的Bug Parade判断,Synth似乎是一种干净且没有错误的产品。 但是,没有软件是完美的。 在某一时刻,有125个针对Synth的错误,其中不成比例的错误与Synth如何处理JTabbedPane
。 因此,如果您在那里遇到一些问题,请不要感到惊讶。 但是,在Sun的辩护中,这些缺陷都处于“已关闭”状态。 但是通常情况下,如果以前有问题,将来可能会出现问题。
尽管错误数据库为Synth提供了相对清晰的图像,但是在使用登录屏幕时,我还有其他一些问题。 我第一次尝试更改JPanel
的背景颜色失败。 我已经创建了一个专用于JPanel
的样式并将其绑定到所有JPanel
,但是那只是行不通。 当我决定使用自己的自定义画家时,事情确实奏效了。
更大的问题是状态更改时窗口小部件的重新绘制。 在使用按钮及其状态时,我发现按钮上的文本无法正确更改颜色。 白色的默认颜色在初始化时不会正确显示,并且在触发状态更改然后重新设置为默认值之前不会显示。 仔细阅读Synth上的文档,您会发现以下小窍门:“虽然您可以为每个状态提供不同的字体,但通常,状态更改时,小部件将不会重新验证,因此,如果尝试使用带有以下格式的字体,则可能会遇到尺寸问题。不同州的规模明显不同。” 听起来他们在尝试使Synth与旧的旧版Swing代码一起工作时遇到问题。 因此,当状态改变时尝试更改字体时要当心。
Synth似乎相对没有漏洞。 但是,如果代码应该可以工作但不起作用的地方到处都是小事,我不会感到惊讶。 不过,不难找到解决方法。 对于工作中出现的每个问题,我都能找到一个。
可以从Synth创建完整的专业外观吗?
答案是肯定的。 Java 1.4中发布的GTK +和Windows XP外观完全是使用Synth创建的。 (那时候还没有发布API。)所以绝对可以做到。
与用Java代码编写外观相比,用Synth创建完整外观的速度要快多少?
这应该很容易计算。 每种方法涉及两个步骤:
- 创建外观,通常由图形艺术家完成
- 将图形工作转换为代码
在Java编码和Synth方法中,图形设计工作都花费相同的时间。 根据我在创建自定义外观方面的经验,我估计图形艺术家大约需要两个人两个星期才能为应用程序创建完整外观。 图形工作需要四个人周。
同样,根据我的经验,三名Java编码人员需要大约两个月的时间,才能通过子类化将图形图形转换为完整的,可随时使用的外观。 这是六个人月的Java编码。 加上图形工作,通过覆盖UI类,在Swing中创建完整的自定义外观需要七个人月。 像这样的数字可以帮助您了解为什么在Internet上下载的内容如此之少。
Synth通过将图形工作转换为XML文件来节省大量时间。 用Java编码创建外观所需的六个月的时间,对于一个开发人员而言,将图形工作转换为Synth XML文件仅需两个星期。 这样一来,在Synth中创建完整外观所需的总时间就只有6个人周-使用Synth可以节省五个月以上的时间。 由两个图形设计师和两个程序员组成的团队,可以在短短三周内完成完整的Synth外观。
结论
Synth为Swing带来了外观的概念。 与使用Java代码编写自定义外观的传统方法相比,Synth的最大优势在于节省了时间。 整个Swing外观可以在不到一个月的时间内创建,比用Java语言编写代码所需的速度快五倍。 或者对于雄心勃勃的开发人员,可以创建五种Synth外观,而不仅仅是一种Java编码的外观。
但是,在Synth的世界中,并非所有事物都是完美的。 编写Java代码覆盖Swing外观可以让你彻底改变应用程序的外观和感觉。 Synth只允许您更改其外观。 这是一个很大的差异。 外观就是-应用程序中使用的颜色,字体和图像。 另一方面,感觉与应用程序在交互过程中显示的行为相对应-在此处单击鼠标右键,在此处按下键。 例如,如果要更改JList
的行为,以单击鼠标左键选择项目,然后单击鼠标右键将其删除,则无法使用Synth做到这一点。 您需要编写Java代码才能获得新的外观。 实际上,Synth应该被称为Swing的新外观,而不是外观。 您可以使用SynthSwift更改UI的外观,但是该感觉始终是默认的Swing感觉。
但是,如果您想通过给应用程序换一个新外观来修饰您的应用程序,或者渴望看到一个Swing应用程序看起来比腐烂的Metal外观更好-感谢Java 5.0的悠久历史-Synth是一个不错的选择。 它没有提供性能问题,并且似乎相对没有错误。 Sun已经表明,通过发布GTK +外观,可以使用Synth创建完整外观。
Synth文档和示例现在非常稀缺。 阅读本文之后,您应该对Synth的工作原理有更深入的了解,并能够使用“每样式一个标签”的设计生成完整的Synth XML文档。 Synth的继承模型和层次结构模型提供了创建样式标签的更强大的方法,但是如果没有它们,您可以创建完整的外观。 理想情况下,随着Synth知识的增长,Swing UI社区将开始看到皮肤数量的爆炸式增长。 可能有数百种外观可供选择,通常附加在Swing应用程序上的“看起来很糟”和“丑陋”的标签(我认为我不太苛刻)将永远消失。
翻译自: https://www.ibm.com/developerworks/java/library/j-synth/index.html