JSeparator类是一种特殊的组件,他在JMenu上提供分隔符。JPopupMenu与JToolBar类也支持分隔,但是每一个都使用JSeparator类的相应子类。除了可以放置在菜单上以外,JSeparator类也可以放置在任何我们希望使用水平或是垂直线来分隔屏幕不同区域的地方。
JSeparator是一个严格的可视化组件,所以,他没有数据模型。
创建JSeparator组件
要为菜单创建一个分隔,我们并不直接创建一个JSeparator,尽管我们可以这样做。相反,我们调用JMenu的addSeparator()方法,而菜单会创建分隔符并将其添加为下一个菜单项。他是一个JSeparator(不是JMenuItem子类)的事实是隐藏的。JMenu还有一个insertSeparator(int index)方法,这个方法可以使得我们在菜单上指定的位置添加分隔,这并不必须是下一个位置。
如果我们希望在菜单以外使用JSeparator(例如,在布局中分隔两个面板),我们应该使用JSeparator的两个构造函数:
public JSeparator() JSeparator jSeparator = new JSeparator(); public JSeparator(int orientation) JSeparator jSeparator = new JSeparator(JSeparator.VERTICAL);
这两个构造函数使得我们可以创建一个水平或是垂直分隔。如果没有指定方向,则为水平方向。如果我们希望显示式指定方向,我们可以使用JSeparator的常量HORIZONTAL或是VERTICAL。
JSeparator属性
在我们拥有JSeparator以外,我们就可以像其他的组件一样将其添加到屏幕中。组件的初始维度是空的(宽度与高度均为0),所以如果屏幕的布局管理器询问组件的尺寸应是多少,分隔符将会回复他不需要空间。另一方面,如果布局管理器提供一定量的空间,如果方向合适则分隔就会使用这个空间。例如,将一个水平JSeparator添加到BorderLayout面板的北侧则会在屏幕上绘制一个分隔线。然而,如果将水平JSeparator添加到相同面板的东侧则不会绘制任何内容。对于垂直JSeparator,其行为则是相反的:北侧将是空的,而在东侧则会出现垂直线。
表6-7显示了JSeparator的四个属性。
JSeparator属性
属性名 |
数据类型 |
访问性 |
accessibleContext |
AccessibleContext |
只读 |
orientation |
int |
读写绑定 |
UI |
SeparatorUI |
读写绑定 |
UIClassID |
String |
只读 |
自定义JSeparator观感
预安装的观感类型集合下的JSeparator外观以及其他的菜单组件显示在图6-3中。
表6-8列出了JSeparator的UIResource相关属性集合。对于JSeparator组件,有五个不同的属性。
JSeparator UIResource元素
属性字符串 |
对象类型 |
Separator.background |
Color |
Separator.foreground |
Color |
Separator.insets |
Insets |
Separator.thickness |
Integer |
SeparatorUI |
String |
JPopupMenu组件是弹出菜单组件的容器,可以显示在任何地方并且为JMenu所支持。当一个编程者定义的触发事件发生时,我们显示JPopupMenu,并且菜单显示所包含的菜单组件。与JMenuBar类似,JpopupMenu使用SingleSelectionModel来管理当前被选中的元素。
创建JpopupMenu组件
JPopupMenu有两个构造函数:
public JPopupMenu() JPopupMenu jPopupMenu = new JPopupMenu(); public JPopupMenu(String title) JPopupMenu jPopupMenu = new JPopupMenu("Welcome");
如果需要,只有一个函数允许我们初始化菜单标题。标题的处理方式会依赖于所安装的观感。当前安装的观感会忽略标题。
向JPopupMenu添加菜项
与JMenu类似,一旦我们有了JPopupMenu,我们需要向其添加菜单项;否则,菜单将会是空的。有三个JPopupMenu方法可以添加菜单项,一个用于添加分隔符。
public JMenuItem add(JMenuItem menuItem); public JMenuItem add(String label); public JMenuItem add(Action action); public void addSeparator();
另外还有一个由Container所继承的add()方法可以用于添加通常的AWT组件:
public Component add(Component component);
添加菜单项的通常方法是使用第一个add()方法。我们独立于弹出菜单创建菜单项,包含其行为行定,然后将其关联到菜单。使用第二个add()方法,我们必须将事件处理器关联到由方法返回的菜单;否则,当被选中时菜单并不会响应。下面的代码显示了两种方法。我们使用哪一种方法完全依赖于我们的喜好。可视化编程环境,例如JBuilder,会使用第一种。因为第一种方法并不是十分复杂,如果不是全部,绝大多数的程序员应该使用第一种方法。
JPopupMenu popupenu = new JPopupMenu(); ActionListener anActionListener = ...; // The first way JMenuItem firstItem = new JMenuItem("Hello"); firstItem.addActionListener(anActionListener); popupMenu.add(firstItem); // The second way JMenuItem secondItem = popupMenu.add("World"); secondItem.addActionListener(anActionListener);
使用Action来创建与JPopupMenu结合使用的菜单项的方式类似于JMenu。然而,依据JPopupMenu类的Javadoc,并不鼓励使用add()方法的Action变体。相反,可以将Action传递给JMenuItem的构造函数,或者是使用setAction()方法进行配置,然后将其添加到JPopupMenu。为什么这个方法没有被deprecated并不清楚。
最后,我们可以使用addSeparator()方法添加分隔。
除了在菜单尾部添加菜单项,我们可以在指定的位置添加菜单项,或者是在指定的位置添加分隔。
public JMenuItem insert(Component component, int position); public JMenuItem insert(Action action, int position);
与JMenu不同,并不存在insertSeparator()方法。但是我们可以使用由Container继承的add(Component component, int position)方法。如果我们希望移除组件,可以使用JPopupMenu特定的remove(Component component)方法。
显示JPopupMenu
与JMenu不同,简单的组装弹出菜单并不够。我们需要将弹出菜单与合适的组件相关联。在Swing 5.0版本之前,我们需要添加事件处理代码来触发弹出菜单的显示。现在,我们所需要做的就是为我们希望关联弹出菜单的组件调用setComponentPopupMenu()方法。当平台特定的触发事件发生时,弹出菜单会自动显示。
我们只需要简单的创建JPopupMenu的实例,并将其关联到我们希望显示弹出菜单的组件,如下所示:
JPopupMenu popupMenu = ...; aComponent.setComponentPopupMenu(popupMenu);
对于弹出菜单比较重要的JComponent方法主要有getComponentPopupMenu(), setComponentPopupMenu(), getInheritsPopupMenu(), setInheritsPopupMenu()以及getPopupLocation()方法。setInheritsPopupMenu()方法会接受一个boolean参数。当为true时,并没有直接为组件设置组件弹出菜单,则会查找父容器用于弹出菜单。
JPopupMenu属性
表6-9列出了JPopupMenu的16个属性。更多的属性是由JComponent,Container与Component继承的。
JPopupMenu属性
属性名 |
数据类型 |
访问性 |
accessibleContext |
AccessibleContext |
只读 |
borderPainted |
boolean |
读写 |
component |
Component |
只读 |
invoker |
Component |
只读 |
label |
String |
读写绑定 |
lightWeightPopupEnabled |
boolean |
读写 |
margin |
Insets |
只读 |
menuKeyListeners |
MenuKeyListener[] |
只读 |
popupMenuListeners |
PopupMenuListener[] |
只读 |
popupSize |
Dimension |
只写 |
selected |
Component |
只写 |
selectionModel |
SingleSelectionModel |
只写 |
subElements |
MenuElement[] |
只读 |
UI |
PopupMenuUI |
读写绑定 |
UIClassID |
String |
只读 |
visible |
boolean |
读写 |
JPopupMenu最有趣的属性就是lightWeightPopupEnabled。通常来说,JPopupMenu会尝试避免为显示其菜单项而创建新的重量级组件。相反,当JPopupMenu可以完整的显示在最外层的窗体框架内时弹出菜单使用JPanel。否则,如果菜单项不适合时,JPopupMenu使用JWindow。然而,如果我们在不同的窗体层上混合使用轻量级与重量级组件,在一个JPanel内显示弹出菜单并不会起作用,因为在菜单层显示的一个重量级组件会在JPanel之前出现。要纠正这种行为,弹出菜单使用Panel用来显示菜单选项。默认情况下,JPopupMenu绝不使用Panel。
如果我们需要允许Panel显示,我们可以在单个的JPopupMenu级别或是整个的Applet或是程序进行配置。在单独的弹出级别,只需要将lightWeightPopupEnable属性设置为false。在系统级别,可以通过如下的代码进行设置:
// From now on, all JPopupMenus will be heavyweight JPopupMenu.setDefaultLightWeightPopupEnabled(false);
这个方法必须在创建弹出菜单之前调用。JPopupMenu对象会在修改具有原始值(默认为true)之前创建。
监视弹出菜单可见性
类似于JMenu,JPopupMenu具有一个特殊的事件/监听器组合来监听弹出菜单何时可见,何时不可见或是何时关闭。这个组合中的事件就是PopupMenuEvent,而监听器就是PopupMenuListener。事件类只是简单的引用事件的源弹出菜单。
public class PopupMenuEvent extends EventObject { public PopupMenuEvent(Object source); }
当JPopupMenu触发事件时,所注册的PopupMenuListener对象会通过他的三个接口方法得到通知。这可以使得我们依据系统状态或是弹出菜单的调用是谁来自定义当前的菜单项。PopupMenuListener接口定义如下:
public interface PopupMenuListener extends EventListener { public void popupMenuCanceled(PopupMenuEvent e); public void popupMenuWillBecomeInvisible(PopupMenuEvent e); public void popupMenuWillBecomeVisible(PopupMenuEvent e); }
自定义JPopupMenu观感
每一个所安装的Swing观感都会提供不同的JPopupMenu外观与一个默认的UIResource值集合。图6-6显示了预安装的观感类型集合:Motif,Windows,Ocean的JPopupMenu组件外观。注意,在预安装的观感类中,只有Motif使用JPopupMenu的title属性。
表6-10显示了JPopupMenu相关的UIResource属性。对于JPopupMenu组件,有五个不同的属性。
JPopupMenu UIResource元素
属性字符串 |
对象类型 |
PopupMenu.actionMap |
ActionMap |
PopupMenu.background |
Color |
PopuMenu.border |
Border |
PopupMenu.consumeEventOnClose |
Boolean |
PopupMenu.font |
Font |
PopupMenu.foreground |
Color |
PopupMenu.popupSound |
String |
PopupMenu.selectedWindowInputMapBindings |
Object[] |
PopupMenu.selectedWindowInputMapBindings.RightToLeft |
Object[] |
PopupMenuSeparatorUI |
String |
PopupMenuUI |
String |
JPopupMenu.Separator类
JPopupMenu类维护了其自己的分隔符从而允许自定义JPopupMenu上的分隔符的观感。这个自定义的分隔符是JPopupMenu的内联类。
当我们调用JPopupMenu的addSeparator()方法时,则会自动生成这个类的一个实例并添加到弹出菜单中。另外,我们也可以通过调用无参数的构造函数来创建这个分隔符:
JSeparator popupSeparator =new JPopupMenu.Separator();
这两种都会创建水平分隔符。
注意:如果我们要修改分隔符的方向,我们必须使用JPopupMenu.Separator.VERTICAL作为参数调用由JSeparator所继承的setOrientation()方法。然而在弹出菜单中具有一个垂直分隔符并不合适。
一个完整的弹出菜单使用示例
列表6-3中的程序将JPopupMenu使用的所有方面组合在一起,包括监听所有菜单项的选中,同时监听菜单何时显示。程序的输出如图6-7所示。
/** * */ package net.ariel.ch06; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; /** * @author mylxiaoyi * */ public class PopupSample { // Define ActionListener static class PopupActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Selected: "+event.getActionCommand()); } } // Define PopupMenuListener static class MyPopupMenuListener implements PopupMenuListener { public void popupMenuCanceled(PopupMenuEvent event) { System.out.println("Canceled"); } public void popupMenuWillBecomeInvisible(PopupMenuEvent event) { System.out.println("Becoming Invisible"); } public void popupMenuWillBecomeVisible(PopupMenuEvent event) { System.out.println("Becoming Visible"); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { // Create frame JFrame frame = new JFrame("PopupMenu Sample"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ActionListener acitonListener = new PopupActionListener(); PopupMenuListener popupMenuListener = new MyPopupMenuListener(); // Create popup menu, attach popup menu listener JPopupMenu popupMenu = new JPopupMenu("Title"); popupMenu.addPopupMenuListener(popupMenuListener); // Cut JMenuItem cutMenuItem = new JMenuItem("Cut"); cutMenuItem.addActionListener(acitonListener); popupMenu.add(cutMenuItem); // Copy JMenuItem copyMenuItem = new JMenuItem("Copy"); copyMenuItem.addActionListener(acitonListener); popupMenu.add(copyMenuItem); // Paste JMenuItem pasteMenuItem = new JMenuItem("Paste"); pasteMenuItem.addActionListener(acitonListener); popupMenu.add(pasteMenuItem); // Separator popupMenu.addSeparator(); // Find JMenuItem findMenuItem = new JMenuItem("Find"); findMenuItem.addActionListener(acitonListener); popupMenu.add(findMenuItem); JButton label = new JButton(); frame.add(label); label.setComponentPopupMenu(popupMenu); frame.setSize(350, 250); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }