图 2 中的 textfield 边框不是常规外观的单像素矩形边框。可以使用一个图像来创建这些边框。这不是我们所熟悉的概念 —— 图像用在 button 和 label 中已经有些时候了 —— 但您可以想像在哪些地方会出问题。如何知道光标移动到什么地方,如何显示文本,如何创建不同大小的文本域?这些问题可以通过图像拉伸(image stretching)的概念来解决。一个图像文件必须描述应用程序中文本域各个边的长度,因此需要有一种方式来告诉 XML 文件如何适当地拉伸图像,以及如何处理常规的 textfield 活动(carat 和文本控制)。
幸运的是,从早期带皮肤的应用程序起,就有一个方法可用于处理这种类型的拉伸。图像必须分成 9 个区域 —— 顶部、右上、右部、右下、底部、左下、左部、左上和中间 —— 这些区域是通过 XML 文件中的一个属性来指定的。然后呈现程序可以通过一定的方式拉伸图像,以适合指定的空间。图 3 展示了文本域图像是如何拉伸的。
图 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,那么就看不见任何图像。您需要添加一个
清单 4 展示了用于装载图像的 XML 代码。注意 sourceInsets 如何确保图像只有适当的部分被拉伸。
清单 4. 装载图像
2 <opaque value="true"/>
3 <state>
4 <font name="Aharoni" size="14"/>
5 <color value="#D2DFF2" type="BACKGROUND"/>
6 <color value="#000000" type="TEXT_FOREGROUND"/>
7 </state>
8 <imagePainter method="textFieldBorder" path="images/textfield.png"
9 sourceInsets="4 6 4 6" paintCenter="false"/>
10 <insets top="4" left="6" bottom="4" right="6"/>
11 </style>
12 <bind style="textfield" type="region" key="TextField"/>
处理不同的状态
从前面的例子可以看到,
默认状态是在
图 4. DEFAULT 状态下的 Cancel 按钮
图 5. MOUSE_OVER 状态下的 Cancel 按钮
清单 5 展示了用于处理 demo 应用程序状态的 XML。注意每种状态是如何定义不同的图像和文本颜色的。
清单 5. 处理状态
2 <state>
3 <imagePainter method="buttonBackground" path="images/button.png"
4 sourceInsets="9 10 9 12" paintCenter="true" stretch="true"/>
5 <insets top="9" left="10" bottom="9" right="12"/>
6 <font name="Aharoni" size="16"/>
7 <color type="TEXT_FOREGROUND" value="#FFFFFF"/>
8 </state>
9 <state value="MOUSE_OVER">
10 <imagePainter method="buttonBackground" path="images/button_on.png"
11 sourceInsets="9 10 9 12" paintCenter="true" stretch="true"/>
12 <insets top="9" left="10" bottom="9" right="12"/>
13 <color type="TEXT_FOREGROUND" value="#FFFFFF"/>
14 </state>
15 <state value="PRESSED">
16 <imagePainter method="buttonBackground" path="images/button_press.png"
17 sourceInsets="10 12 8 9" paintCenter="true" stretch="true"/>
18 <insets top="10" left="12" bottom="8" right="9"/>
19 <color type="TEXT_FOREGROUND" value="#FFFFFF"/>
20 </state>
21 <property key="Button.margin" type="insets" value="0 0 0 0"/>
22 </style>
23 <bind style="button" type="region" key="Button"/>
处理