分类: Java
2011-03-02 18:55:06
在第3章,我们简要介绍了JFC/Swing工程组件所用的Model-View-Controller(MVC)模式。在本章中,我们将会开始探讨如何使用许多可用组件中的关键部分。
所有的Swing组件都是以JComponent类为起点的。尽管Swing库的某些部分并不以JComponent类为根,但所有的组件在其继承的某些级别上共享JComponent类作为通用父类。JComponent类定义通用的行为与属性。在本章中,我们将会了解一些通用功能,例如组件绘制,自定义义,工具提示以及变化大小。
随着特定JComponent子孙类被关注 ,我们将会特别了解JLabel,JButton以及JPanel,三个更为广泛使用的Swing组件类。为了组件内显示图像,我们需要理解Icon接口,以及当使用预定义图像时的ImageIcon类与GrayFilter类的支持。另外,我们将会了解AbstractButton类,他是JButton类的父类。所有的AbstractButton的子类所共享的数据模型是ButtonModel接口;我们将会探讨这个接口及其特定实现,DefaultButtonModel。
4.1 JComponent类JComponent类是所有的Swing组件继承的抽象基类。JComponent类有42个派生子类,每一个都继承了JComponent的功能。图4-1显示了继承层次结构。
尽管JComponent类是所有Swing组件的共同基类,但是Swing工程库中的许多类并不是由JComponent类派生类。这包括所有的高层窗口对象,例如JFrame,JApplet以及JInternalFrame;所有的MVC相关的类;事件处理相关的接口与类;等。所有这些类将会后面的章节中进行讨论。
尽管所有的Swing组件扩展JComponent,JComponent类扩展AWT的Container类,相应的,其扩展AWT的Component类。这就意味着许多的JComponent方面都是由AWT的Component与Container类所共享的。
4.1.1 组件分片JComponent类定义了许多超出原始的AWT组件集合功能的AWT组件面。这包括自定义绘制行为以及自定义显示设置的不同方法,例如颜色,字体以及其他的客户端设置。
绘制JComponent对象
因为Swing的JComponent类是由Container类扩展而来的,因而会遵循基本的AWT绘制模型:所有的绘制都是通过paint()方法来完成的,而repaint()方法则用来触发更新。然而,许多任务的完成是不同的。JComponent类优化了绘制的许多方面从而改进性能与可扩展性。另外,RepaintManager类可以用来自定义绘制行为。
为了改进绘制性能与扩展性,JComponent将绘制操作分为三个任务。public void paint(Graphics g)方法被分为三个独立的protected方法调用。由调用的顺序,他们依次为paintComponent(g), paintBorder(g)以及paintChildren(g),通过原始的paint()调用传递Graphics参数。组件本身首先通过paintComponent(g)进行绘制。如果我们希望自定义Swing组件的绘制,我们可以重写paintComponent()方法而不是paint()方法。除非我们希望完全替换所有的绘制,我们需要首先调用super.paintComponent(),正如下面所示的,来获得默认的paintComponent()行为。
paintBorder()与paintChildren()方法是不可重写的。paintBorder()方法绘制组件周围的边框,第7章会对其概念进行更为完全的描述。如果在Swing容器对象内存在组件,则paintChildren()方法会绘制这些组件。
为了优化绘制,JComponent类提供了三个额外的绘制属性:opaque, optimizedDrawingEnabled以及doubleBuffered。其作用如下:
JComponent的public void revalidate()方法也提供绘制支持。当这个方法被调用时,组件的高级容器会验证其本身。这与AWT的直接调用高级组件的revalidate()方法不同。
Swing组件绘制加强的最后一个方面就是RepaintManager类。
RepaintManager类
RepaintManager类负责保证当前显示的Swing组件之上的重绘请求的高效,确保当一个区域无效时只更新屏幕的最小“脏”区域。
尽管不能进行自定义,RepaintManager是公开并且提供了一个静态的安装例程来使用自定义管理器:public static void setCurrentManager(RepaintManager manager)。要获得当前的管理器,只需要调用public static void currentmanager(JComponent)方法。参数通常为null,除非我们已经自定义了管理器来提供组件级别的支持。一旦我们拥有管理器在,我们可以做的一件事就是将屏幕缓冲区获取为图像。因为缓冲区就是实际显示在屏幕上的内容,这可以使得我们高效的实现窗体内部的屏幕复制。
表4-1显示了RepaintManager的两个属性。他可以使得我们禁止一个组件(层次结构)的所有绘制操作的双缓冲,并且设置最大的双缓冲尺寸,默认为终端用户的屏幕尺寸。
RepaintManager属性
属性名
|
数据类型
|
可访问性 |
doubleBufferingEnabled
|
boolean
|
读写 |
doubleBufferMaximumSize
|
Dimension
|
读写 |
尽管很少实现,提供我们自己的RepaintManager子类确实允许我们自定义屏幕脏区域的绘制机制,或者是当绘制完成时的最少跟踪。重写下面四个方法的一个可以允许我们自定义机制:
UIDefaults类
UIDefaults类表示为当前的观感所安装的包含显示设置的查询表,例如JList中所用的字体,在JTree节中所显示的颜色或图标。UIDefaults的使用将会在第20章探讨Java可插拨的观感体系结构时进行详细讨论。在这里,我们只是简要介绍UIDefaults表。
当我们创建一个组件时,组件会自动的请求UIManager在UIDefaults表中查找组件所用的当前设置。大多数的颜色,字体相关的组件设置,以及其他的一些与颜色与字体无关的设置,都是可配置的。如果你不喜欢一个特定的设置,我们可以简单的通过更新UIDefaults查询表中的相应项目进行修改。
首先我们需要知道我们希望修改的UIDefaults设置的名字。我们可以在本书的附录A中找到这些设置名字,在这个附录中包含J2SE 5.0中预定义观感的所有已知设置的完整列表。(由于发行版本的不同会略有不同。)另外,包含有每个组件描述的是一个包含UIResource相关属性元素的表。(要查找本书中特定组件部分,请查看内容表或是索引。)
一旦我们知道了设置的名字,我们可以使用UImanager的public static void put(Object key, Object value)方法来存放一个新的设置,其中key是键值字符串。例如,下面的代码会将新创建的按钮的背景颜色改变黑色,而前景色改变红色:
获取UIResource属性
如果我们正在创建自己的组件,或者是我们只需要查看当前的设置值,我们可以请求UIManager。尽管public static Object get(Object key)方法是最为通用的,却需要我们将返回值转换为合适的类型。相对应的,我们可以下列更为特定的getXXX()方法,这些方法会为我们执行錾,从而返回合适的类型:
这是一组接受Locale参数的重载的方法集合。
客户属性
除了UIManager维护一个key/value对设置以外,每一个组件实例还维护一个自己的key/value对集合。这对于维护一个不同于一定观感的组件或者是维护与一个组件关联的数据而不需要新类或是方法来存储这些数据的情况十分有用。
例如,JTree类具有一个属性通过Metal观感来连接线风格或是显示JTree中的节点。因为设置特定于一个观感,因而向树API中添加一些内容并不合理。相反,我们可以通过下面的代码在一个特定的树实例上设置属性:
然后,当观感是默认的Metal时,树的节点将会用线连接。如果安装了其他的观感,客户属性就会被忽略。图4-2显示具有与不具有线的树。
4.1.2 JComopnent属性我们已经了解了一些不同的JComponent子类所共享的属性。现在是了解JavaBean属性的时候了。表4-2显示了JComponent所定义的完整属性列表,包括由AWT Container为戌Component类所继承的属性。
JComponent属性
属性名 | 数据类型 | 组件访问 | 容器访问 | JComponent访问 |
accessibleContext | AccessibleContext |
只读
|
N/A
|
只读 |
actionMap
|
ActionMap
|
N/A
|
N/A
|
读写 |
alignmentX
|
float
|
只读
|
只读
|
读写 |
alignmentY
|
float
|
只读
|
只读
|
读写 |
ancestorListeners
|
AncestorListener[]
|
N/A
|
N/A
|
只读 |
autoscrolls
|
boolean
|
N/A
|
N/A
|
读写 |
background
|
Color
|
读写绑定
|
N/A
|
只写 |
backgroundSet
|
boolean
|
只读
|
N/A
|
N/A |
border
|
Border
|
N/A
|
N/A
|
读写绑定 |
bounds
|
Rectangle
|
读写
|
N/A
|
N/A |
colorModel
|
ColorModel
|
只读
|
N/A
|
N/A |
componentCount
|
int
|
N/A
|
只读
|
N/A |
componentListeners
|
ComponentListener[]
|
只读
|
N/A
|
N/A |
componentOrientation
|
ComponentOrientation
|
读写绑定
|
N/A
|
N/A |
componentPopupMenu
|
JPopupMenu
|
N/A
|
N/A
|
读写 |
components
|
Component[]
|
N/A
|
只读
|
N/A |
containerListeners
|
ContainerListener[]
|
N/A
|
只读
|
N/A |
cursor
|
Cursor
|
读写
|
N/A
|
N/A |
cursorSet
|
boolean
|
只读
|
N/A
|
N/A |
debugGraphicsOptions
|
int
|
N/A
|
N/A
|
读写 |
displayable
|
boolean
|
只读
|
N/A
|
N/A |
doubleBuffered
|
boolean
|
只读
|
N/A
|
读写 |
dropTarget
|
DropTarget
|
读写
|
N/A
|
N/A |
enabled
|
boolean
|
读写
|
N/A
|
只写绑定 |
focusable
|
boolean
|
读写绑定
|
N/A
|
N/A |
focusCycleRoot
|
boolean
|
N/A
|
读写绑定
|
N/A |
focusCycleRootAncestor
|
Container
|
只读
|
N/A
|
N/A |
focusListeners
|
FocusListener[]
|
只读
|
N/A
|
N/A |
focusOwner
|
boolean
|
只读
|
N/A
|
N/A |
focusTraversalKeyEnabled
|
boolean
|
读写
|
N/A
|
N/A |
focusTraversalPolicy
|
FocusTraversalPolicy
|
N/A
|
读写绑定
|
N/A |
focusTraversalPolicyProvider
|
boolean
|
N/A
|
读写绑定
|
N/A |
focusTraversalPolicySet
|
boolean
|
N/A
|
只读
|
N/A |
font
|
Font
|
读写绑定
|
只写
|
只写 |
fontSet
|
boolean
|
只读
|
N/A
|
N/A |
foreground
|
Color
|
读写绑定
|
N/A
|
只写 |
foregroundSet
|
boolean
|
只读
|
N/A
|
N/A |
graphics
|
Graphics
|
只读
|
N/A
|
只读 |
graphicsConfiguration
|
GraphicsConfiguration
|
只读
|
N/A
|
N/A |
height
|
int
|
只读
|
N/A
|
N/A |
hierarchyBoundsLIsteners
|
HierarchyBoundsListener[]
|
只读
|
N/A
|
N/A |
hierarchyListeners
|
HierarchyListener[]
|
只读
|
N/A
|
N/A |
ignoreRepaint
|
boolean
|
读写
|
N/A
|
N/A |
inheritsPopupMenu
|
boolean
|
N/A
|
N/A
|
读写 |
inputContext
|
InputContext
|
只读
|
N/A
|
N/A |
inputMap
|
InputMap
|
N/A
|
N/A
|
只读 |
inputMethodListeners
|
InputMethodListener[]
|
只读
|
N/A
|
N/A |
inputMethodRequests
|
InputMethodRequests
|
只读
|
N/A
|
N/A |
inputVerifiers
|
InputVerifier
|
N/A
|
N/A
|
读写绑定 |
insets
|
Insets
|
N/A
|
只读
|
只读 |
keyListeners
|
KeyListener[]
|
只读
|
N/A
|
N/A |
layout
|
LayoutManager
|
N/A
|
读写
|
N/A |
lightweight
|
boolean
|
只读
|
N/A
|
N/A |
locale
|
Locale
|
读写绑定
|
N/A
|
N/A |
location
|
Point
|
读写
|
N/A
|
N/A |
locationOnScreen
|
Point
|
只读
|
N/A
|
N/A |
maximumSize
|
Dimension
|
读写绑定 |
只读 |
读写 |
maximumSizeSet
|
boolean
|
只读
|
N/A
|
N/A |
minimumSize
|
Dimension
|
读写绑定
|
只读
|
读写 |
minimumSizeSet
|
boolean
|
只读
|
N/A
|
N/A |
mouseListeners
|
MouseListener[]
|
只读
|
N/A
|
N/A |
mouseMotionListeners
|
MouseMotionListener[]
|
只读
|
N/A
|
N/A |
mousePosition
|
Point
|
只读
|
N/A
|
N/A |
mousWheelListeners
|
MouseWheelListener
|
只读
|
N/A
|
N/A |
name
|
String
|
读写
|
N/A
|
N/A |
opaque
|
boolean
|
只读
|
N/A
|
读写绑定 |
optimizedDrawingEnabled
|
boolean
|
N/A
|
N/A
|
只读 |
paintingTile
|
boolean
|
N/A
|
N/A
|
只读 |
parent
|
Container
|
只读
|
N/A
|
N/A |
preferredSize
|
Dimension
|
读写绑定
|
只读
|
读写 |
preferredSizeSet
|
boolean
|
只读
|
N/A
|
N/A |
propertyChangeListeners
|
PropertyChangeListener[]
|
只读
|
N/A
|
N/A |
registeredKeyStrokes
|
KeyStroke[]
|
N/A
|
N/A
|
只读 |
requestFocusEnabled
|
boolean
|
N/A
|
N/A
|
读写 |
rootPane
|
JRootPane
|
N/A
|
N/A
|
只读 |
showing
|
boolean
|
只读
|
N/A
|
N/A |
size
|
Dimension
|
读写
|
N/A
|
N/A |
toolkit
|
Toolkit
|
只读
|
N/A
|
N/A |
tooltipText
|
String
|
N/A
|
N/A
|
读写 |
topLevelAncestor
|
Container
|
N/A
|
N/A
|
只读 |
transferHandler
|
TransferHandler
|
N/A
|
N/A
|
读写绑定 |
treeLock
|
Object
|
只读
|
N/A
|
N/A |
uiClassID
|
String
|
N/A
|
N/A
|
只读 |
valid
|
boolean
|
只读
|
N/A
|
N/A |
validateRoot
|
boolean
|
只读
|
N/A
|
N/A |
verifyInputWhenFocusTarget
|
boolean
|
N/A
|
N/A
|
只读 |
vetoableChangeListeners
|
vetoableChangeListener[]
|
N/A
|
N/A
|
只读 |
visible
|
boolean
|
读写
|
N/A
|
只写 |
visibleRect
|
Rectangle
|
N/A
|
N/A
|
只读 |
width
|
int
|
只读
|
N/A
|
只读 |
x
|
int
|
只读
|
N/A
|
只读 |
y |
int
|
只读
|
N/A
|
只读 |
包括由父结构继承的属性,JComponent共有92个属性。正如这个数字所表明的,JComponent类极为适用于可视化开发。
4.1.3 处理JComponnent事件所有的JComponent子类共享许多不同类型的事件。大多数的事件类型来自于父类,例如Component与Container。首先我们将会探讨由Container继承而来的PropertyChangeListener。然后我们会了解一下所有的JComponent子类所共享的两种事件处理功能的使用:VetoableChangeListener与AncestorListener。最后,我们来了解一下由Component继承的完全监听器集合。
使用PropertyChangeListener监听组件事件
JComponent类具有一些直接或间接的组件绑定属性。通过将PropertyChangeListener绑定到组件,我们可以监听特定的JComponent属性变化,并进行相应的响应。
为了演示的目的,列表4-1中的PropertyChangeListener演示当监听JButton组件中Action类型属性的变化时我们所需要的行为。属性的变化可以决定执行哪一个if语句块。
使用VetoableChangeListener监听组件事件
VetoableChangeListener是Swing组件所使用的另一个JavaBean监听器。他使用限制属性,而PropertyChangeListener只使用绑定属性。这两个监听器之间的一个关键区别就在于如果监听器不能处理所请求的变化,则public void vetoableChange(PropertyChangeEvent propertyChangeEvent)方法会抛出一个PerpotyVetoException异常。
使用AncestorListener监听JComponent事件
我们可以使用AncestorListener可以确定组件何时移动,何时可见,以及何时不可见。如果我们允许用户通过在屏幕上移动组件以及由屏幕中移除组件进行屏幕定制,则AncestorListener就十分有用。
为了演示,列表4-2将一个AncestorListener与JFrame的根面板相关联。当程序首次启动时我们会看到Removed, Added以及Move信息。另外,当我们拖动窗体是我们会看Moved消息。
监听JComponent的继承事件
除了监听JComponent的AncestorEvent或是PropertyChangeEvent实际的功能,JComponent由其父类Container与Component继承了监听其他事件的能力。
表4-4列出了十个事件监听器。我们也许我们使用了相当多的JComponent监听器,但是旧版本也可以工作。使用最合适的来解决我们的任务。
JComponent继承的事件监听器
类
|
事件监听器
|
事件对象 |
Component
|
ComponentListener
|
componentHidden(ComponentEvent)
|
Component
|
FocusListener
|
focusGained(FocusEvent)
|
Component
|
HierarchyBoundsListener
|
ancestorMoved(HierarchyEvent)
|
Component
|
HierarchyListener
|
hierarchyChanged(HierarchyEvent) |
Component
|
InputMethodListener
|
carePositionChanged(InputMethodEvent)
|
Component
|
KeyListener
|
keyPressed(KeyEvent)
|
Component
|
MouseListener
|
mouseClicked(MouseEvent)
|
Component
|
MouseMotionListener
|
mouseDragged(MouseEvent)
|
Component
|
MouseWheelListener
|
mouseWheelMoved(MouseWheelEvent) |
Container
|
ContainerListener
|
componentAdd(ContainerEvent)
|