第一节 第二节 第三节 第四节 第五节 第六节 第七节 第八节 第九节 第十节 第十一节
第八节    Swing中的Icon和Border
对所有的Swing组件,例如按钮、列表单等,都还可以绘制边框。在Swing中提供了各种边框类型,例如bevel、etched、line、titled等。Swing组件的边框是通过JComponent类来绘制的,该类是所有Swing组件的基类,实现了所有Swing组件公共的功能。在JComponent中有一个paintBorder()方法,该方法为组件绘制边框。Swing的开发人员可以象下面的例子中所示那样来绘制边框:
// 一段实现paintBorder()方法代码
protected void paintBorder(Graphics g) {
switch(getBorderType()) {
case LINE_BORDER: paintLineBorder(g);
break;
case ETCHED_BORDER: paintEtchedBorder(g);
break;
case TITLED_BORDER: paintTitledBorder(g);
break;
...
}
}
请注意上面的代码只是一种假设,事实上Swing的开发人员并没有这样实现paintBorder()方法。在上面的代码中,在JComponent中绘制边框的代码被直接写入了paintBorder()方法中,这意味着JComponent和绘制边框的功能被紧密地结合在了一起。很自然地大家会联想到如果需要实现一种新的边框类型,开发人员必须修改至少三处代码:首先增加一个常量,该常量代表新添加的边框的类型值;其次需要在Switch语句中增加一个case语句;最后开发人员需要实现paintXXXBorder()方法,其中XXX代表新边框的名称。
很显然要扩展上面paintBorder()方法的功能是一件很困难的事情,不仅仅是因为开发人员需要增加一种新的边框类型,更麻烦的是开发人员很难修改JComponent类。JComponent类已经被编译到了Swing的开发工具中,如果开发人员想修改它的话,必须获得Swing的源代码,修改后重新编译Swing。同时在用户的计算机上与需要使用新编译的Swing API。另外所有的Swing组件都可以使用开发人员新添加的边框类型。有可能开发人员只希望新的边框被某些组件使用,但是现在开发人员无法对使用该边框的组件进行限制。
开发人员有更好的实现方法吗?答案就是策略模式。通过策略模式,可以将JComponent和实现绘制边框的代码分离开来,这样开发人员在增加或修改绘制边框的代码使就不需要修改JComponent的代码。通过应用策略模式,开发人员将变化的概念(在这个例子中是绘制边框)封装起来,然后通过一个Border接口,使程序能够重用绘制边框的功能。下面让我们来看JComponent是如何利用策略模式来实现绘制边框的功能的:
// Swing中paintBorder()方法的源代码
protected void paintBorder(Graphics g) {
Border border = getBorder();
if (border != null) {
border.paintBorder(this, g, 0, 0, getWidth(), getHeight());
}
}
上面的paintBorder()方法通过一个border对象绘制了组件的边框。这样border对象替代了前一个例子中的JComponent封装了边框绘制的功能。我们还应该注意到JComponent将一个对自己的引用传递给了Border.paintBorder()方法,这是因为Border的实例必须知道它对应的组件的信息,这种方式通常被称为委托。通过这种方式,一个对象可以将功能委托给另一个对象来实现。
在JComponent类中引用了一个Border对象,通过JComponent.getBorder()方法可以获得该Border对象。下面的代码演示了如何设定和获得Border对象:
...
private Border border;
...
public void setBorder(Border border) {
Border oldBorder = this.border;
this.border = border;
firePropertyChange("border", oldBorder, border);
if (border != oldBorder) {
if (border == null || oldBorder == null || !(border.getBorderInsets(this).
equals(oldBorder.getBorderInsets(this)))) {
revalidate();
}
repaint();
}
}
...
public Border getBorder() {
return border;
}