8.5 JApplet类
JApplet类是AWT Applet类的扩展。为了在使用Swing组件的applet中能正确的进行事件处理,我们applet必须继承JApplet,而不是Applet。
JApplet的作用与其他的实现了RootPaneContainer接口的高层窗口相同。JApplet与Applet之间一个重要的区别就是默认的布局管理器。因为我们向JApplet的内容面析添加组件,其默认的布局管理器为BorderLayout。这与Applet的默认布局管理器FlowLayout不同。另外,Swing applet还可以具有一个工具栏,或者更为特定的JMenuBar,这是applet的JRootPane的另一个属性。
如果我们计划部署一个使用Swing组件的applet,最好是使用Sun Microsystems所提供的Java插件,因为这会随运行时安装Swing库。
如查我们要扩展JApplet类,他只有一个重要的protected方法:
protected
JRootPane createRootPane()
8.6 配合桌面使用
Spring提供了对一个通常窗口或是桌面内的窗体集合进行管理。正如我们在第1章所讨论的,这种管理通常被称之为MDI。窗体可以位于其他的窗体之上,或者是可以被拖动,而其外观适当当前的观感。这些窗体是JInternalFrame类的实例,而桌面是一个称之为JDesktopPane的特殊JLayeredPane。桌面内窗体的管理是DesktopManager的责任,其中所提供的默认实现是DefaultDesktopManager。桌面上的JInternalFrame的图标形式是通过JDesktopIcon的内联类JInternalFrame来表示的。同时有InternalFrmaeListener,InternalFrameAdapter以及InternalFrameEvent用于事件处理。
首先,我们来看一下构成桌面的部分,然后我们会看到使用这些部分的一个完整示例。
8.6.1 JInternalFrame类
JInternalFrame类与JFrame类类似。他是一个高层窗口,使用RootPaneContainer接口,但是并不是一个顶层窗口。我们必须将内部窗体放在另一个顶层窗口中。当我们拖动时,内部窗体会停留在其窗口的边界之内,这通常是一个JDesktopPane。另外,内部窗体是轻量级的,并且提供了一个UI委托从而使得内部窗体看起来类似当前配置的观感。
创建JInternalFrame
JInternalFrame有六个构造函数:
public JInternalFrame() JInternalFrame frame = new JInternalFrame(); public JInternalFrame(String title) JInternalFrame frame = new JInternalFrame(" The Title "); public JInternalFrame(String title, boolean resizable) JInternalFrame frame = new JInternalFrame(" The Title ", true ); public JInternalFrame(String title, boolean resizable, boolean closable) JInternalFrame frame = new JInternalFrame(" The Title ", false , true ); public JInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable) JInternalFrame frame = new JInternalFrame(" The Title ", true , false , true ); public JInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable) JInternalFrame frame = new JInternalFrame(" The Title ", false , true , false , true );
这些构造函数以一个向另一个添加参数的方式进行级联。无参数时,所创建的JInternalFrame没有标题,并且不能调整大小,关闭,最大化或是图标化。然而,内部窗体总是可以拖动的。
JInternalFrame属性
表8-9列出了JInternalFrame类的30个不同属性。layer属性列出了两次,因为他具有两个设置方法,其中一个用于int,而另一个用于Integer。
JInternalFrame属性
属性名 |
数据类型 |
访问性 |
accessibleContext |
AccessibleContext |
只读 |
closable |
boolean |
读写绑定 |
closed |
boolean |
读写绑定 |
contentPane |
Container |
读写绑定 |
defaultCloseOperation |
int |
读写 |
desktopIcon |
JInternalFrame.JDesktopIcon |
读写绑定 |
desktopPane |
JDesktopPane |
只读 |
focusCycleRoot |
boolean |
读写 |
focusCycleRootAncester |
Container |
只读 |
focusOwner |
Component |
只读 |
frameIcon |
Icon |
读写绑定 |
glassPane |
Component |
读写绑定 |
icon |
boolean |
读写绑定 |
iconifiable |
boolean |
读写 |
internalFrameListeners |
InternalFrameListener[] |
只读 |
jMenuBar |
JMenuBar |
读写绑定 |
layer |
int |
读写 |
layer |
Integer |
只写 |
layeredPane |
JLayeredPane |
读写绑定 |
layout |
LayoutManager |
只写 |
maximizable |
boolean |
读写绑定 |
maximum |
boolean |
读写 |
mostRecentFocusOwner |
Component |
只读 |
normalBounds |
Rectangle |
读写 |
resizable |
boolean |
读写绑定 |
rootPane |
JRootPane |
读写绑定 |
selected |
boolean |
读写绑定 |
title |
String |
读写绑定 |
UI |
InternalFrameUI |
读写 |
UIClassID |
String |
只读 |
warningString |
String |
只读 |
对于Java 1.3及以后的版本,JInternalFrame的初始defaultCloseOperation属性设置为DISPOSE_ON_CLOSE。以前版本的默认设置为HIDE_ON_CLOSE。我们可以将这个属性设置为前面的表8-6中列出的WindowConstants的值。
normalBounds属性描述了当一个图标化的内部窗体取消息图标化时应该在哪里显示。focusOwner属性在特定的JInternalFrame被激活时提供了一个实际带有输入焦点的Component。
在Swing类中,JInternalFrame只包含四个限制属性:closed, icon, maximum以及selected。他们与四个boolean构造函数参数直接相关。每一个都可以允许我们在改变其设置时检测当前的属性状态。然而,因为属性是受限制的,当我们要设置一个属性时,我们所做的尝试必须位于一个try-catch块中,捕捉PropertyVetoException:
try { // Try to iconify internal frame internalFrame.setIcon( false ); } catch (PropertyVetoException propertyVetoException) { System.out.println(" Rejected "); }
为了有助于我们使用这些绑定属性,JInternalFrame类定义了一个11个常量,如表8-10所示。他们表示在PropertyChangeListener中通过PropertyChangeEvent的getPropertyName()方法返回的字符串。
JInternal属性常量
属性名常量 |
关联属性 |
CONTENT_PANE_PROPERTY
|
contentPane |
FRAME_ICON_PROPERTY
|
frameIcon |
GLASS_PANE_PROPERTY
|
glassPane |
IS_CLOSED_PROPERTY
|
closed |
IS_ICON_PROPERTY
|
icon |
IS_MAXIMUM_PROPERTY
|
maximum |
IS_SELECTED_PROPERTY
|
selected |
LAYERED_PANE_PROPERTY
|
layeredPane |
MENU_BAR_PROPERTY |
jMenuBar |
ROOT_PANE_PROPERTY
|
rootPane |
TITLE_PROPERTY
|
title |
下面的类示例演示了在PropertyChangeListener中常量的使用。
package swingstudy.ch08; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JInternalFrame; public class InternalFramePropertyChangeHandler implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent event) { // TODO Auto-generated method stub String propertyName = event.getPropertyName(); if (propertyName.equals(JInternalFrame.IS_ICON_PROPERTY)) { System.out.println(" Icon property changed. React. "); } } }
处理JInternalFrame事件
为了帮助我们像使用JFrame一样来使用JInternalFrame,有一个额外的事件监听器来负责内部窗体的打开与关闭的相关事件。这个接口名为InternalFrameListener,其定义如下。其作用类似于AWT的WindowListener接口,但是所用的JInternalFrame类,而不是AWT的Window类。
public interface InternalFrameListener extends EventListener { public void internalFrameActivated(InternalFrameEvent internalFrameEvent); public void internalFrameClosed(InternalFrameEvent internalFrameEvent); public void internalFrameClosing(InternalFrameEvent internalFrameEvent); public void internalFrameDeactivated(InternalFrameEvent internalFrameEvent); public void internalFrameDeiconified(InternalFrameEvent internalFrameEvent); public void internalFrameIconified(InternalFrameEvent internalFrameEvent); public void internalFrameOpened(InternalFrameEvent internalFrameEvent); }
另外,与具有所有WindowListener方法桩的WindowApapter类类似,也有一个具有所有InternalFrameListener方法桩的InternalFrameAdapter类。如果我们并不是对JInternalFrame发生的所有事件感兴趣,我们可以继承InternalFrameAdapter类,并且只重写我们所感兴趣的方法。例如,列表8-5中所示的监听器只对图标化方法感兴趣。无需提供InternalFrameListener的其他五个方法的桩实现,我们只需要继承InternalFrameAdapter,并重写两个相关的方法。
package swingstudy.ch08; import javax.swing.JInternalFrame; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; public class InternalFrameIconifyListener extends InternalFrameAdapter { public void internalFrameIconified(InternalFrameEvent event) { JInternalFrame source = (JInternalFrame)event.getSource(); System.out.println(" Iconified: "+source.getTitle()); } public void internalFrameDeiconified(InternalFrameEvent event) { JInternalFrame source = (JInternalFrame)event.getSource(); System.out.println(" Deiconified: "+source.getTitle()); } }
InternalFrameEvent是AWTEvent的子类。为了定义由AWTEvent的public int getID()方法返回的值,InternalFrameEvent每一个可用的特定事件子类定义了一个常量。表8-11列出了九个常量。我们也可以通过事件的getInternalFrame()方法来获得实际的JInternalFrame。
InternalFrameEvent事件子类型
事件子类型ID
|
关联的接口方法 |
INTERNAL_FRAME_ACTIVATED
|
internalFrameActivated |
INTERNAL_FRAME_CLOSED
|
internalFrameClosed |
INTERNAL_FRAME_CLOSING
|
internalFrameClosing |
INTERNAL_FRAME_DEACTIVATED
|
internalFrameDeactivated |
INTERNAL_FRAME_DEICONIFIED
|
internalFrameDeiconified |
INTERNAL_FRAME_FIRST
|
N/A |
INTERNAL_FRAME_ICONIFIED
|
internalFrameIconified |
INTERNAL_FRAME_LAST
|
N/A |
INTERNAL_FRAME_OPENED
|
internalFrameOpened |
自定义JInternalFrame观感
因为JInternalFrame是一个轻量级组件,他具有可安装的观感。每一个可安装的Swing观感提供了一个不同的JInternalFrame外观以及默认的UIResource值集合。图8-5预安装的观感类型集合的JWindow窗口外观。
表8-12中列出了JInternalFrame的可用UIResource相关属性的集合。对于JInternalFrame常量,有60个不同的属性,包括内部窗体的标题面板的属性。
JInternalFrame UIResource元素
属性字符串 |
对象类型 |
InternalFrame.actionMap |
ActionMap |
InternalFrame.activeBroderColor |
Color |
InternalFrame.activeTitleBackground |
Color |
InternaleFrame.activeTitleForeground |
Color |
InternalFrame.activeTitleGradient |
List |
InternalFrame.border |
Border |
InternalFrame.borderColor |
Color |
InternalFrame.borderDarkShadow |
Color |
InternalFrame.borderHighlight |
Color |
InterenalFrame.borderLight |
Color |
InternaleFrame.borderShadow |
Color |
InternaleFrame.borderWidth |
Integer |
InternalFrame.closeButtonToolTip |
String |
InternalFrame.closeIcon |
Icon |
InternalFrmae.closeSound |
String |
InternalFrame.icon |
Icon |
InternalFrame.iconButtonToolTip |
String |
InternalFrame.iconifyIcon |
Icon |
InternalFrame.inactiveBorderColor |
Color |
InternalFrame.inactiveTitleBackground |
Color |
InternalFrame.inactiveTitleForeground |
Color |
InternalFrame.inactiveTitleGradient |
List |
InternalFrame.layoutTitlePaneAtOrigin |
Boolean |
InternalFrame.maxButtonToolTip |
String |
InternalFrame.maximizeIcon |
Icon |
InternalFrame.maximizeSound |
String |
InternalFrame.minimizeIcon |
Icon |
InternalFrame.minimizeIconBackground |
Color |
InternalFrame.minimizeSound |
String |
InternalFrame.optionDialogBorder |
Border |
InternalFrame.paletteBorder |
Border |
InternalFrame.paletteCloseIcon |
Icon |
InternalFrame.paletteTitleHeight |
Integer |
InternaleFrame.resizeIconHighlight |
Color |
InternalFrame.resizeIconShadow |
Color |
InternalFrame.restoreButtonToolTip |
String |
InternalFrame.restoreDownSound |
String |
InternalFrame.restoreUpSound |
String |
InternalFrame.titlebuttonHeight |
Integer |
InternalFrame.titleButtonWidth |
Integer |
InternalFrame.titleFont |
Font |
InternalFrame.titlePaneHeight |
Integer |
InternalFrame.useTaskBar |
Boolean |
InternalFrame.windowBindings |
Object[] |
InternalFrameTitlePane.closebuttonAccessibleName |
String |
InternalFrameTitlePane.closebuttonText |
String |
InternalFrameTitlePane.closeIcon |
Icon |
InternalFrameTitlePane.iconifyButtonAccessibleName |
String |
InternalFrameTitlePane.iconifyIcon |
Icon |
InternalFrameTitlePane.maximizeButtonAccessiblName |
String |
InternalFrameTitlePane.maximizeButtonText |
String |
InternalFrameTitlePane.minimizeIcon |
Icon |
InternalFrameTitlePane.moveButtonText |
String |
InternalFrameTitlePane.restoreButtonText |
String |
InternalFrameTitlePane.sizeButtonText |
String |
InternalFrameTitlePane.titlePaneLayout |
LayoutManager |
InternalFrameTitlePaneUI |
String |
InternalFrameUI |
String |
除了表8-12中许多可配置属性以外,对于Metal观感,我们还可以通过特殊的客户端属性JInternalFrame.isPalette来将内部窗体设计为一个palette。当设置为Boolean.TRUE时,内部窗体的外观会与其他窗体略微不同,并且具有较短的标题栏,如图8-6所示。
如果我们同时在桌面的PALETTE_LAYER上添加了一个内部窗体,则这个窗体会位于其他所有窗体之上(如图8-6所示):
JInternalFrame palette = new JInternalFrame(" Palette ", true , false , true , false ); palette.setBounds(150, 0, 100, 100); palette.putClientProperty(" JInternalFrame.isPalette ", Boolean.TRUE); desktop.add(palette, JDesktopPane.PALETTE_LAYER);
创建图8-6所示的程序的完整代码显示在本章稍后的列表8-6中。
修改JDesktopIcon
JInternalFrame依赖一个内联类JDesktopIcon来为JInternalFrame的图标化显示提供UI委托。这个类只是用来这种功能的一个特殊的JComponent,而不是如其名字暗示的一个特殊的Icon实现。事实上,JDesktopIcon类的注释表明这个类是临时的,所以我们不应直接对其进行自定义。(当然,这个类会存在一段时间。)
如果我们需要自定义JDesktopIcon,我们可以修改一些UIResource相关的属性。表8-13列出了JDesktopIcon组件的八个UIResource相关属性。
JInternalFrame.JDesktopIcon UIResource元素
属性字符串
|
对象类型 |
DesktopIcon.background
|
Color |
DesktopIcon.border
|
Border |
DesktopIcon.font
|
Font |
DesktopIcon.foreground
|
Color |
DesktopIcon.icon
|
Icon |
DesktopIcon.width
|
Integer |
DesktopIcon.windowBindings
|
Object[] |
DesktopIconUI
|
String |
8.6.2 JDesktopPane类
与内部窗体组合配合使用的另一个类就是JDesktopPane类。桌面面板的目的就是包含内部窗体集合。当内部窗体被包含在一个桌面面板中时,他们将其行为的大部分委托给桌面面板的桌面管理器。我们将会在本章稍后详细了解DesktopManager接口。
创建JDesktopPane
JDesktopPane只有一个无参数的构造函数。一旦创建,我们通常将其放在由BorderLayout管理的容器的中部。这可以保证桌面占据容器的所有空间。
将内部窗体添加到JDesktopPane
JDesktopPane并没有实现RootPaneContainer。我们并不能直接将组件添加到JRootPane内的不同面板中,而是直接将其添加到JDesktopPane:
desktop.add(anInternalFrame);
JDesktopPane属性
如表8-14所示,JDesktopPane有八个属性。位于allFrames属性数组索引0外的JInternalFrame是位于桌面前面的内部窗体(JInternalFrame f = desktop.getAllFrames()[0])。除了获取JDesktopPane中的所有窗体以外,我们还可以仅获取特定层的窗体:public JInternalFrame[] getAllFramesInLayer(int layer)。
可用的dragMode属性设置可以为类的LIVE_DRAG_MODE与OUTLINE_DRAG_MODE常量。
JDesktopPane属性
属性名
|
数据类型
|
访问性 |
accessibleContext
|
AccessibleContext
|
只读 |
allFrames
|
JInternalFrame[]
|
只读 |
desktopManager
|
DesktopManager
|
读写 |
dragMode
|
int
|
读写绑定 |
opaque
|
boolean
|
只读 |
selectedFrame
|
JInternalFrame
|
读写 |
UI
|
DesktopPaneUI
|
读写 |
UIClassID
|
String
|
只读 |
自定义JDesktopPane观感
回到图8-5,我们可以看到JDesktopPane中的JInternalFrame对象。JDesktopPane的基本观感与每一个观感相同。如表8-15所示,对JDesktopPane并没有太多可以配置的UIResource相关属性。
JDesktopPane UIResource元素
属性字符串
|
对象类型 |
desktop
|
Color |
Desktop.ancestorInputMap
|
InputMap |
Desktop.background
|
Color |
Desktop.windowBindings
|
Object[] |
DesktopPane.actionMap
|
ActionMap |
DesktopPaneUI
|
String |
完整的桌面示例
现在我们已经了解了主要的桌面相关类,现在我们来看一下完整的示例。基本的过程包括创建一组JInternalFrame对象,然后将放在一个JDesktopPane中。如果需要,可以对每一个内部窗体的单个组件进行事件处理,也可以对单个窗体进行事件处理。在这个示例中简单的使用了前面的列表8-5中所给出的InternalFrameIconifyListener类来监听正在图标化和取消图标化的内容窗体。
图8-6显示了程序启动时的样子。一个特定的内部窗体被设计为palette,并且允许了拖放模式。
列表8-6显示了这个示例的完整代码。
package swingstudy.ch08; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JDesktopPane; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.event.InternalFrameListener; public class DesktopSample { /** * @param args */ public static void main( final String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { String title = (args.length==0 ? " Desktop Sample " : args[0]); JFrame frame = new JFrame(title); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JDesktopPane desktop = new JDesktopPane(); JInternalFrame internalFrames[] = { new JInternalFrame(" Can Do All ", true , true , true , true ), new JInternalFrame(" Not Resizable ", false , true , true , true ), new JInternalFrame(" Not Closable ", true , false , true , true ), new JInternalFrame(" Not Maximizable ", true , true , false , true ), new JInternalFrame(" Not Iconifiable ", true , true , true , false ) }; InternalFrameListener internalFrameListener = new InternalFrameIconifyListener(); int pos = 0; for (JInternalFrame internalFrame: internalFrames) { // Add to desktop desktop.add(internalFrame); // Position and size internalFrame.setBounds(pos*25, pos*25, 200, 100); pos++; // Add listener for iconification events internalFrame.addInternalFrameListener(internalFrameListener); JLabel label = new JLabel(internalFrame.getTitle(), JLabel.CENTER); internalFrame.add(label, BorderLayout.CENTER); // Make visible internalFrame.setVisible( true ); } JInternalFrame palette = new JInternalFrame(" Palette ", true , false , true , false ); palette.setBounds(350, 150, 100, 100); palette.putClientProperty(" JInternalFrame.isPalette ", Boolean.TRUE); desktop.add(palette, JDesktopPane.PALETTE_LAYER); palette.setVisible( true ); desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); frame.add(desktop, BorderLayout.CENTER); frame.setSize(500, 300); frame.setVisible( true ); } }; EventQueue.invokeLater(runner); } }
DesktopManager接口
使用桌面的最后一部分就是桌面管理器了,他是DesktopManager接口的实现,其定义如下:
当JInternalFrame位于JDe
public interface DesktopManager { public void activateFrame(JInternalFrame frame); public void beginDraggingFrame(JComponent frame); public void beginResizingFrame(JComponent frame, int direction); public void closeFrame(JInternalFrame frame); public void deactivateFrame(JInternalFrame frame); public void deiconifyFrame(JInternalFrame frame); public void dragFrame(JComponent frame, int newX, int newY); public void endDraggingFrame(JComponent frame); public void endResizingFrame(JComponent frame); public void iconifyFrame(JInternalFrame frame); public void maximizeFrame(JInternalFrame frame); public void minimizeFrame(JInternalFrame frame); public void openFrame(JInternalFrame frame); public void resizeFrame(JComponent frame, int newX, int newY, int newWidth, int newHeight); public void setBoundsForFrame(JComponent frame, int newX, int newY, int newWidth, int newHeight); }
sktopPane中时,他们不应尝试例如图标化或是最大化的操作。相反,他们应该请求他们所安装在的桌面面板的桌面管理器来执行这些操作:
getDesktopPane().getDesktopManager().iconifyFrame(anInternalFrame);
DefaultDesktopManager类提供了DesktopManager的一个实现。如果默认实现还足够,观感会提供他们自己的DesktopManager实现类,例如Windows观感的WindowsDesktopManager。我们也可以定义自己的管理器,但是通常并不需要这样。
8.7 小结
在本章中,我们探讨了JRootPane类,以及如何实现依据JRootPane对内部组件进行管理的RootPaneContainer接口。我们同时了解了在Swing中我们如何使用JFrame, JDialog, JWindow, JApplet或是JInternalFrame类的JRootPane。根面板可以借助JLayeredPane来布局组件,其方式是工具提示文本以及弹出菜单总是显示在相关联组件的上面。
JInternalFrame同时也可以存在于桌面环境中,在这种情况下,JDesktopPane以及DesktopManager管理如何以及在哪里放置并显示内部窗体。我们还可以通过将InternalFrameListener实现也JInternalFrame关联来响应内部窗体事件。
在第9章中,我们将会探讨Swing库中的特定弹出组件:JColorChooser, JFileChooser, JOptionPane以及ProgressMonitor