在比较AWT、SWING的不同之处时,InSets和Border是个有趣的话题。
InSets是AWT时代就有的概念,多译为“边衬”,是指容器的边衬部分,布局管理器在安置容器的组件时,不会占用inSets部分。而且,AWT不提供动态改变inSets大小的方法,只提供getInsets()供子类型重载,即如想改变容器的inSets,只能继承该类,并重载该方法。
而Border是Swing引入的概念。Border绘制在inSets内——可以简单的理解成Border会占据整个InSets空间,事实上getInsets已经被重载以返回其Border的“边衬”(即返回Border接口的getBorderInsets方法的返回值)。通过动态的为Component设置Border(通过setBorder方法),就可以改变Component的inSets的大小。因此,Swing时代,基本上已经不再重载getInsets方法了——如果使用空Border(EmptyBorder),就可以得到“传统意义”上的InSets。
只所以会有如此奇怪的设置,我猜测原因是这样的。
AWT中的容器多是重量的,即有其相应的同位体。容器本身的绘制是由其同位体完成的,同位体有自身的Border,而inSets的引入也是为了腾出绘制Border位置。AWT其实是不鼓励改变InSets大小的,因为如果设置太小的InSets,组件有可能占据组件Border的位置。又由于Border的绘制是由同位体负责的,AWT就没有理由引入Border的概念了。
Swing的情况完全不同。所有的Swing组件(Swing组件都是容器)都是轻量的,它们由Swing负责绘制——包括容器的Border。因此,Swing可控制Border的绘制,Border类的引入就顺理成章了。
Border不是组件,只是组件用来委托绘制“边框”而已,所以不同的组件当然可以共享一个Border对象作为委托对象。BorderFactory被引入,尽量应该使用“工厂”来生产Border对象——对于能够共享的Border对象,“工厂”被实现成singleton模式——我们只管为每个Component创建Border即可,优化的工作就交给工厂来完成吧。
Border接口有3个方法:
(1) isBorderOpaque();
(2) paintBorder(Component , Graphics, int, int, int, int );
(3) getBorderInsets(Component)
AbstractBorder是顶层的Border类实现,正如它名字所示,它是个Abstract类。如要实现自己的Border,则需要继承AbstractBorder类——通常需要重载paintBorder和getBorderInsets——AbstractBorder不绘制边框,会返回一个(0,0,0,0)的Insets。
需要注意的是,某些组件拥有默认的Border,有些Border还帮助组件实现特定的界面样式。当更改这些组件的Border时,需要特别小心。
Icon的概念与Border及其相似,它们都不是Component,但都被Component用来委托绘制一些东西——不同的是,Border绘制在Insets中,而Icon绘制在Component的非Insets部分。
同Border不同,Component并没有类似setIcon方法——支持Icon的Component通常会添加一个使用Icon作为参数的构造函数。
所有的Icon都实现Icon接口:
int |
getIconHeight() |
int |
getIconWidth() |
void |
paintIcon(Component c,Graphics g, int x, int y) |
最常用的Icon实现是IamgeIcon类,IamgeIcon内部使用MediaTracker的一个实例来完全装载image。如果装载失败,其后调用paintIcon()将不会有任何操作,而getIconHeight()、getIconWidth()会返回-1。
有大量的Swing组件支持Icon——这很容易理解,因为Swing完全控制Component的绘制,它当然可以在Component上绘制一个Icon。
与Icon相比,Action不太好理解,JDK帮助文档对Action接口的描述如下:
Action 接口提供 ActionListener 接口的一个有用扩展,以便若干控件访问相同的功能。 除了 ActionListener 接口定义的 actionPerformed 方法之外,此接口还允许应用程序在一个位置定义:
|
而AbstractAction是Swing提供的实现了Action接口的抽象基类,供用户继承——你必须自行实现actionPerformed方法。
我们看一个例子:
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Test extends JApplet { public void init() { JMenuBar mb = new JMenuBar(); JMenu fileMenu = new JMenu("File"); fileMenu.add(new ExitAction()); mb.add(fileMenu); setJMenuBar(mb); } } class ExitAction extends AbstractAction { public ExitAction() { super("exit"); } public void actionPerformed(ActionEvent e) { System.exit(0); } } |
显然,我们并没有为菜单项设置String(我们甚至没有去创建菜单项)——菜单项自动获取了Action的String;如果Action带有Icon,菜单项还可以获取这个Icon。
除了String、Icon之外,Action还有一个状态值,当带有状态特征的Component使用该Action后,也会以此状态作为自己的状态(如按钮就带有状态),以下方法用于设置、获取Action的状态。
boolean |
isEnabled() |
void |
setEnabled(boolean b) |
Action还定义了一些列的常量作为Key值,可以通过这些常量进行获取、设置,如:
static String |
NAME (即上例中,显示在菜单上的“String”) |
|
static String |
SMALL_ICON 即可以显示在菜单上的“Icon” |
|
Object |
getValue(String key) |
|
void |
putValue(String key,Object value) |
这样,就可以动态的修改Action的这些信息——事实上,Component也是通过这些方法来获取自己需要的信息的。
还有一点需要注意的是,任何Action的属性修改后,都会激发属性修改事件,当然Action接口提供了相应的方法来添加、去除Listener:
void |
addPropertyChangeListener(PropertyChangeListener listener) |
void |
removePropertyChangeListener(PropertyChangeListener listener) |
同Icon、Border一样,Action也能被多个组件共享,共享Action的Component会具有同样的属性——来自Action。
Border、Icon和Action都有两个共同特点:
(1) 它们都不是组件;
(2) 多个组件可以共享它们。
为多个组件共享不近节省了资源,还提供了一致性。对Action而言,还为GUI提供了控制中心点。