近几年来, 软件工程师们开始对这些模式进行汇总分类。这个领域的先驱者的灵感来源于建筑师 Christopher Alexander 的设计模式。
模型-视图-控制器模式并不是 AWT 和 Swing 设计中使用的唯一模式。下列是应用的另外几种模式:
•容器和组件是“ 组合(composite )” 模式
•带滚动条的面板是“ 装饰器(decorator)” 模式
•布局管理器是“ 策略(strategy)” 模式
Swing 设计者采用了一种很有名的设计模式(design pattern ) : 模型 - 视图 - 控制器 ( model-view-controller) 模式。这种设计模式同其他许多设计模式一样,都遵循第 5 章介绍过的面向对象设计中的一个基本原则: 限制一个对象拥有的功能数量。不要用一个按钮类完成所有的事情, 而是应该让一个对象负责组件的观感, 另一个对象负责存储内容。模型-视图- 控制器(MVC) 模式告诉我们如何实现这种设计,实现三个独立的类:
•模型 (model ): 存储内容。
•视图 (view): 显示内容。
•控制器(controller): 处理用户输入。
这个模式明确地规定了三个对象如何进行交互。模型存储内容, 它没有用户界面。
模型必须实现改变内容和查找内容的方法。 记住:模型是完全不可见的。显示存储在模型中的数据是视图的工作。
模型-视图-控制器模式的一个优点是一个模型可以有多个视图, 其中每个视图可以显示全部内容的不同部分或不同形式。 例如, 一个 HTML 编辑器常常为同一内容在同一时刻提供两个视图: 一个 WYSIWYG (所见即所得)视图和一个“ 原始标记” 视图。
控制器负责处理用户输入事件,如点击鼠标和敲击键盘。然后决定是否把这些事件转化成对模型或视图的改变。
除了“ 本职工作” 外,模型-视图- 控制器模式吸引 Swing 设计者的主要原因是这种模式允许实现可插观感。
对于大多数组件来说, 模型类将实现一个名字以 Model 结尾的接口, 例如,按钮就实现了 ButtonModel 接口。实现了此接口的类可以定义各种按钮的状态。
对于一个按钮来说, 仅凭模型无法知道它的外观(实际上, 在有关单选钮的 12.4.2 节中将会看到,这种纯粹的设计会给程序员带来一些麻烦)。
需要注意的是, 同样的模型(即 DefaultButtonModel ) 可用于下压按钮、单选按钮、复选框、甚至是菜单项。当然, 这些按钮都有各自不同的视图和控制器。当使用 Metal 观感时,JButton 类用 BasicButtonUI 类作为其视图; 用 ButtonUIListener 类作为其控制器。通常, 每个 Swing 组件都有一个相关的后缀为 UI 的视图对象, 但并不是所有的 Swing 组件都有专门的控制器对象。
在阅读 JButton 底层工作的简介之后可能会想到: JButton 究竟是什么? 事实上, 它仅仅是一个继承了 JComponent 的包装器类, JComponent 包含了一个 DefauUButtonModel 对象,一些视图数据(例如按钮标签和图标)和一个负责按钮视图的 BasicButtonUI 对象。
回顾上一章的程序, 我们设计了几个按钮,点击这些按钮可以改变框架的背景颜色。
这几个按钮被放置在一个 JPanel 对象中, 且用流布局管理器( flow layout manager) 管理, 这是面板的默认布局管理器。当一行的空间不够时,会将显示在新的一行上。
另外, 按钮总是位于面板的中央, 即使用户对框架进行缩放也是如此。
通常, 组件放置在容器中, 布局管理器决定容器中的组件具体放置的位置和大小。
注释: 可惜的是, 继承层次有两点显得有点混乱。 首先, 像 JFrame 这样的顶层窗口是 Container 的子类, 所以也是 Component 的子类, 但却不能放在其他容器内。 另外,JComponent 是 Container 的子类, 但不直接继承 Component, 因此, 可以将其他组件添置到 JButton 中。(但无论如何, 这些组件无法显示出来)。
每个容器都有一个默认的布局管理器,但可以重新进行设置。 例如, 使用下列语句:
panel.setLayout(new GridLayout(4, 4));
这个面板将用 GridLayout 类布局组件。可以往容器中添加组件。容器的 add 方法将把组件和放置的方位传递给布局管理器
【API】java.awt.Container 1.0
Void SetLayout(LayoutManager m)
Component add(Component c)
Component add(Component c, Object constraints)
1.1【API】 java.awt.FlowLayout 1.0
FIowLayout ()
FIowLayout(int align)
FIowLayout (int align, int hgap , int vgap)
构造一个新的 FlowLayout 对象。
参数: align LEFT、 CENTER 或者 RIGHT
hgap 以像素为单位的水平间距(如果为负值, 则强行重叠)
vgap 以像素为单位的垂直间距(如果为负值,则强行重叠)
边框布局管理器(border layout manager) 是每个 JFrame的内容窗格的默认布局管理器。 流布局管理器完全控制每个组件的放置位置,边框布局管理器则不然,它允许为每个组件选择一个放置位置。
先放置边缘组件,剩余的可用空间由中间组件占据。当容器被缩放时,边缘组件的尺寸不会改变, 而中部组件的大小会发生变化。 在添加组件时可以指定 BorderLayout 类中的 CENTER、 NORTH、 SOUTH、 EAST 和 WEST 常量。并非需要占用所有的位置,如果没有提供任何值,系统默认为 CENTER。
注释: BorderLayout 常量定义为字符串。 例如: BorderLayout.SOUTH 定义为 字符串“ SOUTH”。很多程序员喜欢直接使用字符串, 因为这些字符串比较简短, 例如, frame.add (component,“ SOUTH” )。 然而, 如果字符串拼写有误, 编译器不会捕获错误。
与流布局不同,边框布局会扩展所有组件的尺寸以便填满可用空间(流布局将维持每个组件的最佳尺寸。) 当将一个按钮添加到容器中时会出现问题 .按钮扩展至填满框架的整个南部区域。而且,如果再将另外一个按钮添加到南部区域, 就会取代第一个按钮。
解决这个问题的常见方法是使用另外一个面板(panel )。屏幕底部的三个按钮全部包含在一个面板中。这个面板被放置在内容窗格的南部。
要想得到这种配置效果, 首先需要创建一个新的 JPanel 对象,然后逐一将按钮添加到面板中。面板的默认布局管理器是 HowLayout,这恰好符合我们的需求。随后使用在前面已经看到的 add 方法将每个按钮添加到面板中。每个按钮的放置位置和尺寸完全处于 FlowLayout 布局管理器的控制之下。这意味着这些按钮将置于面板的中央,并且不会扩展至填满整个面板区域。最后, 将这个面板添加到框架的内容窗格中。
【API】java.awt.BorderLayout 1.0:
BorderLayout()
BorderLayout( int hgap, int vgap)
网格布局像电子数据表一样, 按行列排列所有的组件。不过,它的每个单元大小都是一样的。缩放窗口时, 示例中的计算器按钮将随之变大或变小,但所有的按钮尺寸始终保持一致。
在网格布局对象的构造器中,需要指定行数和列数 。
当然, 极少有像计算器这样整齐的布局。实际上, 在组织窗口的布局时小网格(通常只有一行或者一列)比较有用。 例如, 如果想放置一行尺寸都一样的按钮,就可以将这些按钮放置在一个面板里, 这个面板使用只有一行的网格布局进行管理。
【API】java.awt.GridLayout 1.0
GridLayout(int rows, int columns)
GridLayout(int rows, int columns, int hgap, int vgap)
首先, 介绍具有用户输入和编辑文本功能的组件。文本域( JTextField) 和文本区 ( JTextArea) 组件用于获取文本输人。文本域只能接收单行文本的输人, 而文本区能够接收多行文本的输人。JPassword 也只能接收单行文本的输人,但不会将输入的内容显示出来。
这三个类都继承于 JTextComponent 类。由于 JTextComponent 是一个抽象类,所以不能够构造这个类的对象。另外,在 Java 中常会看到这种情况。在査看 API 文档时,发现自己正在寻找的方法实际上来自父类 JTextComponent, 而不是来自派生类自身。例如,在一个文本域和文本区内获取(get)、 设置(set) 文本的方法实际上都是 JTextComponent 类中的方法。
【API】javax.swing.text.JTextComponent 1.2:
String getText()
void setText(String text)
boolean isEditable()
void setEditable( boolean b)
把文本域添加到窗口的常用办法是将它添加到面板或者其他容器中,这与添加按钮完全一样。
但是,这里所说的列不是一个精确的测量单位。一列就是在当前使用的字体下一个字符的宽度。 如果希望文本域最多能够输人 n 个字符, 就应该把宽度设置为 n 列。在实际中, 这样做效果并不理想,应该将最大输人长度再多设 1 ~ 2 个字符。列数只是给 AWT 设定首选( preferred) 大小的一个提示。如果布局管理器需要缩放这个文本域,它会调整文本域的大小。在 JTextField 的构造器中设定的宽度并不是用户能输人的字符个数的上限。用户可以输入一个更长的字符串,但是当文本长度超过文本域长度时输人就会滚动。用户通常不喜欢滚动文本域, 因此应该尽量把文本域设置的宽一些。如果需要在运行时重新设置列数,可以使用 setColumns 方法。
提示:使用 setColumns 方法改变了一个文本域的大小之后, 需要调用包含这个文本框的容器的 revalidate 方法。
revalidate 方法会重新计算容器内所有组件的大小, 并且对它们重新进行布局。调用revalidate 方法以后, 布局管理器会重新设置容器的大小, 然后就可以看到改变尺寸后的文本域了。
revalidate 方法是 JComponent 类中的方法。它并不是马上就改变组件大小, 而是给这个组件加一个需要改变大小的标记。这样就避免了多个组件改变大小时带来的重复计算。但是, 如果想重新计算一个 JFrame 中的所有组件, 就必须调用 validate 方法JFrame 没有扩展 JComponent。
通常情况下,希望用户在文本域中键入文本(或者编辑已经存在的文本)。文本域一般初始为空白。只要不为 JTextField 构造器提供字符串参数,就可以构造一个空白文本域
可以在任何时候调用 setText 方法改变文本域中的内容。 这个方法是从前面提到的
JTextComponent 中继承而来的。
并且, 在前面已经提到, 可以调用 getText 方法来获取用户键人的文本。这个方法返回用户输人的文本。 如果想要将 getText 方法返回的文本域中的内容的前后空格去掉, 就应该调用 trim 方法
如果想要改变显示文本的字体,就调用 setFont 方法
【API】javax.swing.JTextField 1.2:
JTextField(Int cols )
JTextField(String text , int cols )
int getColumns( )
void setColumns( int cols )
【API】 javax.swing.JComponent 1.2:
void revalidate( )
void setFont( Font f )
【API】 java.awt.Component 1.0:
void validate( )
Font getFont( )
标签是容纳文本的组件,它们没有任何的修饰(例如没有边缘,) 也不能响应用户输入。可以利用标签标识组件。 例如: 与按钮不同,文本域没有标识它们的标签 要想用标识符标识这种不带标签的组件, 应该
1 ) 用相应的文本构造一个 JLabel 组件。
2 ) 将标签组件放置在距离需要标识的组件足够近的地方, 以便用户可以知道标签所标识的组件。
JLabel 的构造器允许指定初始文本和图标, 也可以选择内容的排列方式。可以用 Swing Constants 接口中的常量来指定排列方式。在这个接口中定义了几个很有用的常量, 如 LEFT、RIGHT、 CENTER、 NORTH、 EAST 等。JLabel 是实现这个接口的一个 Swing 类。
利用 setText 和 setlcon 方法可以在运行期间设置标签的文本和图标 。
提示: 可以在按钮、 标签和菜单项上使用无格式文本或 HTML 文本。 我们不推荐在按钮上使用 HTML 文本—这样会影响观感。 但是 HTML 文本在标签中是非常有效的。 只要简单地将标签字符串放置在 ...
中即可
需要说明的是包含 HTML 标签的第一个组件需要延迟一段时间才能显示出来, 这是因为需要加载相当复杂的 HTML 显示代码。
与其他组件一样,标签也可以放置在容器中。这就是说,可以利用前面介绍的技巧将标签放置在任何需要的地方。
【API】javax.swing.JLabel 1.2:
JLabel (String text )
JLabel (Icon icon)
JLabel (String text, int align)
JLabel (String text, Icon Icon, int align)
String getText( )
void setText(String text )
Icon getIcon( )
void setIcon(Icon Icon)
密码域是一种特殊类型的文本域。为了避免有不良企图的人看到密码, 用户输入的字符不显示出来。 每个输人的字符都用回显字符 ( echo character) 表示, 典型的回显字符是星号(*)。 Swing 提供了 JPasswordField 类来实现这样的文本域。
密码域是另一个应用模型 - 视图 - 控制器体系模式的例子。密码域采用与常规的文本域相同的模型来存储数据, 但是,它的视图却改为显示回显字符,而不是实际的字符。
【API】 javax.swing.JPasswordField 1.2:
JPasswordField(String text, int columns)
void setEchoChar(char echo)
char[ ] getPassworci()
有时, 用户的输人超过一行。 正像前面提到的, 需要使用 JTextArea 组件来接收这样的输入。当在程序中放置一个文本区组件时, 用户就可以输
人多行文本,并用 ENTER 键换行。每行都以一个“ \n” 结尾。
在 JTextArea 组件的构造器中,可以指定文本区的行数和列数。
与文本域一样。出于稳妥的考虑, 参数 columns 应该设置得大一些。另外,用户并不受限于输人指定的行数和列数。当输人过长时,文本会滚动。还可以用 setColumns 方法改变列数, 用 setRows 方法改变行数。这些数值只是首选大
小一布局管理器可能会对文本区进行缩放。
如果文本区的文本超出显示的范围, 那么剩下的文本就会被剪裁掉。可以通过开启换行特性来避免裁剪过长的行
换行只是视觉效果;文档中的文本没有改变,在文本中并没有插入“ \n” 字符。
在 Swing 中, 文本区没有滚动条。 如果需要滚动条, 可以将文本区插人到滚动窗格(scroll pane) 中。
现在滚动窗格管理文本区的视图。 如果文本超出了文本区可以显示的范围, 滚动条就会自动地出现, 并且在删除部分文本后, 当文本能够显示在文本区范围内时, 滚动条会再次自动地消失。滚动是由滚动窗格内部处理的, 编写程序时无需处理滚动事件。
这是一种为任意组件添加滚动功能的通用机制, 而不是文本区特有的。也就是说, 要想为组件添加滚动条, 只需将它们放人一个滚动窗格中即可。
注释: JTextArea 组件只显示无格式的文本, 没有特殊字体或者格式设置。如果想要显示格式化文本(如 HTML ), 就需要使用 JEditorPane 类。在卷 II 将详细讨论。
【API】javax.swing.JTextArea 1.2:
JTextArea( )
JTextArea( int rows, int cols )
JTextArea( String text, int rows, int cols )
void setColumns( int cols )
void setRows ( int rows )
void append( String newText )
void setLineWrap(boolean wrap )
void setWrapStyleWord(boolean word)
void setTabSize(int c )
【API】 javax.swing.JScrollPane 1.2:
JScrollPane( Component c )
如果想要接收的输人只是“ 是” 或“ 非”, 就可以使用复选框组件。复选框自动地带有标识标签。用户通过点击某个复选框来选择相应的选项, 再点击则取消选取。当复选框获得焦点时, 用户也可以通过按空格键来切换选择。
复选框需要一个紧邻它的标签来说明其用途。在构造器中指定标签文本。
可以使用 setSelected 方法来选定或取消选定复选框。
isSelected方法将返回每个复选框的当前状态。
当用户点击复选框时将触发一个动作事件。通常, 可以为复选框设置一个动作监听器。
【API】javax.swing.JCheckBox 1.2:
JCheckBox(String label )
JCheckBox( String label , Icon icon )
JCheckBox(String label , boolean state )
boolean isSelected ( )
void setSelected(boolean state )
在前一个例子中, 对于两个复选框, 用户既可以选择一个、 两个,也可以两个都不选。在很多情况下,我们需要用户只选择几个选项当中的一个。当用户选择另一项的时候, 前一项就自动地取消选择。这样一组选框通常称为单选钮组(Radio Button Group), 这是因为这些按钮的工作很像收音机上的电台选择按钮。当按下一个按钮时,前一个按下的按钮就会自动弹起。
在 Swing 中,实现单选钮组非常简单。为单选钮组构造一个 ButtonGroup 的对象。然后,再将 JRadioButton 类型的对象添加到按钮组中。按钮组负责在新按钮被按下时,取消前一个被按下的按钮的选择状态。
构造器的第二个参数为 true 表明这个按钮初始状态是被选择,其他按钮构造器的这个参数为 false。注意,按钮组仅仅控制按钮的行为, 如果想把这些按钮组织在一起布局, 需要把它们添加到容器中, 如 JPanel。
注释: 如果有一组单选钮, 并知道它们之中只选择了一个。要是能够不查询组内所有的按钮就可以很快地知道哪个按钮被选择的话就好了。 由 于 ButtonGroup 对象控制着所有的按钮, 所以如果这个对象能够给出被选择的按钮的引用就方便多了。 事实上,ButtonGroup 类中有一个 getSelection 方法, 但是这个方法并不返回被选择的单选钮, 而是返回附加在那个按钮上的模型 ButtonModel 的引用。 对于我们来说, ButtonModel 中的方法没有什么实际的应用价值。 ButtonModel 接 口 从 ItemSelectable 接口继承了一个getSelectedObject 方法, 但是这个方法没有用, 它返回 null。 getActionCommand 方法看起来似乎可用, 这是因为一个单选钮的“ 动作命令” 是它的文本标签, 但是它的模型的动作命令是 null。只有在通过 setActionCommand 命令明确地为所有单选钮设定动作命令后
, 才能够通过调用方法 buttonGroup.getSelection().getActionCommand() 获得当前选择的按钮的动作命令。
【API】javax.swing.JRadioButton 1.2:
JRadioButton(String label , Icon icon)
JRadioButton(String label , boolean state)
【API】 javax.swing.ButtonGroup 1.2:
void add(AbstractButton b)
ButtonModel getSelection()
【API】javax.swing.ButtonModel 1.2:
String getActionCommand()
【API】 javax.swing.AbstractButton 1.2:
void setActionCommand(String s)
如果在一个窗口中有多组单选按钮, 就需要用可视化的形式指明哪些按钮属于同一组。Swing 提供了一组很有用的边框( borders ) 来解决这个问题。可以在任何继承了 JComponent的组件上应用边框。 最常用的用途是在一个面板周围放置一个边框,然后用其他用户界面元素 (如单选钮)填充面板。
1 ) 调用 BorderFactory 的静态方法创建边框。下面是几种可选的风格:
2 ) 如果愿意的话, 可以给边框添加标题, 具体的实现方法是将边框传递给
BroderFactory.createTitledBorder。
3 ) 如果确实想把一切凸显出来, 可以调用下列方法将几种边框组合起来使用:
BorderFactory.createCompoundBorder 。
4 ) 调用 JComponent 类中 setBorder方法将结果边框添加到组件中。
不同的边框有不同的用于设置边框的宽度和颜色的选项。 详情请参看 API 注释。 偏爱使用边框的人都很欣赏这一点, SoftBevelBorder 类用于构造具有柔和拐角的斜面边框, LineBorder 类也能够构造圆拐角。 这些边框只能通过类中的某个构造器构造, 而没有BorderFactory 方法。
【API】javax.swing.BorderFactory 1.2 :
static Border createLineBorder(Color color)
static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Color color)
static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Icon tileIcon)
static Border createEmptyBorder()
static Border createEmptyBorder(int top, int left, int bottom, int right)
static Border createEtchedBorder()
static Border createEtchedBorder(Color highlight, Color shadow)
static Border createEtchedBorder(int type)
static Border createEtchedBorder(int type, Color highlight, Color shadow)
static Border createBevelBorder(int type)
static Border createBevelBorder(int type, Color highlight, Color shadow)
static Border createLoweredBevelBorder()
static Border createRaisedBevelBorder()
static TitledBorder createTitledBorder(String title)
static TitledBorder createTitledBorder(Border border)
static TitledBorder createTitledBorder(Border border, String title)
static TitledBorder createTitledBorder(Border border, String title, int justification, int position)
static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font)
static TitledBorder createTitledBorder(Border border, String title, int justification, int position), Font font, Color color
static CompoundBorder createCompoundBorder(Border outsideBorder, Border insideBorder)
【API】javax.swing.border.SoftBevelBorder 1.2 :
SoftBevelBorder(int type)
SoftBevelBorder(int type, Color highlight, Color shadow)
【API】javax.swing.border.LineBorder 1.2 :
public LineBorder(Color color, int thickness, boolean roundedCorners)
【API】javax.swing.JComponent 1.2 :
void setBorder(Border border)
如果有多个选择项, 使用单选按钮就不太适宜了, 其原因是占据的屏幕空间太大。这时就可以选择组合框。 当用户点击这个组件时, 选择列表就会下拉出来, 用户可以从中选择一项
如果下拉列表框被设置成可编辑 ( editable ), 就可以像编辑文本一样编辑当前的选项内容。鉴于这个原因, 这种组件被称为组合框(combo box ),它将文本域的灵活性与一组预定义的选项组合起来。 JComboBox 类提供了组合框的组件。
在 Java SE 7中, JComboBox 类是一个泛型类。 例如, JComboBox
调用 setEditable 方法可以让组合框可编辑。 注意, 编辑只会影响当前项, 而不会改变列表内容。
可以调用 getSelectedltem 方法获取当前的选项, 如果组合框是可编辑的, 当前选项则是可以编辑的。不过,对于可编辑组合框, 其中的选项可以是任何类型,这取决于编辑器(即由编辑器获取用户输人并将结果转换为一个对象。) 关于编辑器的讨论请参见卷 II 中的第 6 章。 如果你的组合框不是可编辑的, 最好调用
combo.getltemAt(combo.getSelectedlndex ())
这会为所选选项提供正确的类型。
可以调用 addltem 方法增加选项。在示例程序中, 只在构造器中调用了 addltem 方法,实际上,可以在任何地方调用它。
这个方法将字符串添加到列表的尾部。可以利用 insertltemAt 方法在列表的任何位置插人一个新选项 。
可以增加任何类型的选项,组合框可以调用每个选项的 toString 方法显示其内容。
如果需要在运行时删除某些选项,可以使用 removeltem 或者 removeltemAt 方法,使用哪个方法将取决于参数提供的是想要删除的选项内容, 还是选项位置。
调用 removeAllltems 方法将立即移除所有的选项。
提示: 如果需要往组合框中添加大量的选项,addltem 方法的性能就显得很差了。 取而代之的是构造一个 DefaultComboBoxModel, 并调用 addElement 方法进行加载, 然后再调用 JComboBox 中的 setModel 方法。
当用户从组合框中选择一个选项时,组合框就将产生一个动作事件。为了判断哪个选项被选择, 可以通过事件参数调用 getSource 方法来得到发送事件的组合框引用, 接着调用getSelectedltem 方法获取当前选择的选项。需要把这个方法的返回值转化为相应的类型,通常是 String 型。
注释: 如果希望持久地显示列表, 而不是下拉列表, 就应该使用 JList 组件。在卷 II 的第6 章中将介绍 JList。
【API】javax.swing.JComboBox 1.2:
boolean isEditable( )
void setEditable( boolean b )
void addItem( Object item)
void insertltemAt(Object item, int index )
void removeItem( Object item)
void removeItemAt( int index )
void removeAllItems( )
Object getSelectedltem( )
通常, 可以使用下列方式构造滑动条:
JSlider slider = new ]Slide「(min, max, initialValue) ;
如果省略最小值、 最大值和初始值, 其默认值分别为 0、100 和 50。
或者如果需要垂直滑动条, 可以按照下列方式调用构造器:
JSlider slider = new JSIider(SwingConstants.VERTICAL, min, max, initialValue);
这些构造器构造了一个无格式的滑动条 。
可以通过显示标尺( tick ) 对滑动条进行修饰。
slider.setMajorTickSpacing(20);
slider.setMinorTi ckSpacing(5);
上述滑动条在每 20 个单位的位置显示一个大标尺标记, 每 5 个单位的位置显示一个小标尺标记。所谓单位是指滑动条值, 而不是像素。
这些代码只设置了标尺标记, 要想将它们显示出来, 还需要调用:
slider.setPaintTicks(true);
大标尺和小标尺标记是相互独立的。 例如, 可以每 20 个单位设置一个大标尺标记, 同时每 7 个单位设置一个小标尺标记, 但是这样设置, 滑动条看起来会显得非常凌乱。
可以强制滑动条对齐标尺。这样一来, 只要用户完成拖放滑动条的操作, 滑动条就会立即自动地移到最接近的标尺处。激活这种操作方式需要调用:
slider.setSnapToTicks(true);
注释:“ 对齐标尺” 的行为与想象的工作过程并不太一样。在滑动条真正对齐之前, 改变监听器报告的滑动条值并不是对应的标尺值。 如果点击了滑动条附近, 滑动条将会向点击的方向移动一小段距离,“ 对夺标尺” 的滑块并不移动到下一个标尺处。
可以调用下列方法为大标尺添加标尺标记标签(tick mark labels ):
slider.setPaintLabels(true);
还可以提供其他形式的标尺标记, 如字符串或者图标(见图 12-18 )。 这样做有些烦琐。 首先需要填充一个键为 Integer 类型且值为 Component 类型的散列表。 然后再调用setLabelTable 方法,组件就会放置在标尺标记处。通常组件使用的是 JLabel 对象。
提示: 如果标尺的标记或者标签不显示, 请检查一下是否调用了 setPaintTicks(true) 和 setPaintLabels(true)。
要想隐藏滑动条移动的轨迹, 可以调用:
slider.setPaintTrack(false);
滑动条是逆向的, 调用下列方法可以实现这个效果:
slider.setlnverted(true);
【API】javax.swing.JSlider 1.2:
JSlider()
JSlider(int direction)
JSlider ( int min, int max )
JSlider ( int min, int max, int initialValue)
JSlider( int direction, int min, int max, int initialValue)
void setPaintTicks( boolean b)
void setMajorTickSpacing(int units )
void setMinorTickSpacing(int units )
void setPaintLabels(boolean b)
void setLabelTable( Dictionary table )
void setSnapToTicks ( boolean b)
void setPaintTrack( boolean b)
位于窗口顶部的菜单栏 (menubar ) 包括了下拉菜单的名字。点击一个名字就可以打开包含菜单项 ( menu items ) 和子菜单 ( submenus) 的菜单。 当用户点击菜单项时, 所有的菜单都会被关闭并且将一条消息发送给程序。
创建菜单是一件非常容易的事情。首先要创建一个菜单栏:
JMenuBar menuBar = new JMenuBar();
菜单栏是一个可以添加到任何位置的组件。 通常放置在框架的顶部。可以调用 setJMenuBar 方法将菜单栏添加到框架上:
frame.setJMenuBar(menuBar);
需要为每个菜单建立一个菜单对象:
JMenu editMenu = new JMenu("Edit");
然后将顶层菜单添加到菜单栏中:
menuBar.add(editMenu);
向菜单对象中添加菜单项、分隔符和子菜单:
JMenuItem pasteItem = new JMenuItem("Paste");
editMenu.add(pasteItem) ;
editMenu.addSeparator() ;
JMenu optionsMenu = . . .; // a submenu
editMenu.add(optionsMenu) ;
当用户选择菜单时,将触发一个动作事件。这里需要为每个菜单项安装一个动作监听器。
ActionListener listener = ... ;
pasteItem.addActionListener(listener) ;
可以使用 JMenu.add(String s) 方法将菜单项插入到菜单的尾部, 例如:
editMenu.add("Paste");
Add 方法返回创建的子菜单项。可以采用下列方式获取它,并添加监听器:
JMenuItem pasteItem = editMenu.add("Paste");
pasteItem.addActionListener(listener);
在通常情况下, 菜单项触发的命令也可以通过其他用户界面元素(如工具栏上的按钮 )激活。在第 11 章中, 已经看到了如何通过 Action对象来指定命令。通常, 采用扩展抽象类AbstractAction 来定义一个实现 Action 接口的类。这里需要在 AbstractAction 对象的构造器中指定菜单项标签并且覆盖 actionPerformed 方法来获得菜单动作处理器。
【API】javax.swing.JMenu 1.2:
JMenu(String label)
JMenuItem add(JMenuItem item)
JMenuItem add(String label)
JMenuItem add(Action a)
void addSeparator()
JMenuItem insert(JMenuItem menu, int index)
JMenuItem insert(Action a, int index)
void insertSeparator(int index)
void remove(int index)
void remove(JMenuItem item)
【API】 javax.swing.JMenuItem 1.2:
JMenuItem(String label)
JMenuItemCAction a)
1.3【API】 javax.swing.AbstractButton 1.2:
void setAction(Action a)
1.3【API】javax.swing.JFrame 1.2:
void setJMenuBar(JMenuBar menubar)
菜单项与按钮很相似。 实际上, JMenuItem 类扩展了 AbstractButton 类。与按钮一样,菜单可以包含文本标签、 图标, 也可以两者都包含。既可以利用 JMenuItem(Striiig,Icon) 或者 JMenuItem(Icon) 构造器为菜单指定一个图标,也可以利用 JMenuItem 类中的 setlcon 方法(继承自 AbstractButton 类)指定一个图标。
正如所看到的,在默认情况下,菜单项的文本被放置在图标的右侧。如果喜欢将文本放置在左侧, 可以调用 JMenuItem类中的 setHorizontalTextPosition 方法(继承自 AbstractButton 类)设置。
也可以将一个图标添加到一个动作上:
cutAction.putValue(Action.SMALL_ICON, new ImageIcon("cut.gif"));
当使用动作构造菜单项时,Action.NAME 值将会作为菜单项的文本,而 Action.SMALL_ICON 将会作为图标。
另外, 可以利用 AbstractAction 构造器设置图标
【API】javax.swing.JMenuItem 1.2:
JMenuItem(String label , Icon icon)
【API】 javax.swing.AbstractButton 1.2:
void setHorizontalTextPosition(int pos)
【API】 javax.swing.AbstractAction 1.2:
AbstractAction(String name, Icon smallIcon)
下面是创建复选框菜单项的代码:
JCheckBoxHenuItem readonlyltem = new]CheckBoxMenuItem("Read-only");
optionsMenu.add(readonlyltem);
单选钮菜单项与普通单选钮的工作方式一样, 必须将它们加人到按钮组中。当按钮组中的一个按钮被选中时, 其他按钮都自动地变为未选择项。
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem = new JRadioButtonMenuItem("Insert");
insertItem.setSelected(true);
]RadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Overtype");
group.add(insertItem);
group.add(overtypeItem);
optionsMenu.add(insertItem);
optionsMenu.add(overtypeltem);
使用这些菜单项,不需要立刻得到用户选择菜单项的通知。而是使用 isSelected方法来测试菜单项的当前状态(当然, 这意味着应该保留一个实例域保存这个菜单项的引用)。使用setSelected 方法设置状态。
【API】javax.swing.JCheckBoxMenultem 1.2:
JCheckBoxMenuItem(String label)
JCheckBoxMenuItem(String label , boolean state)
【API】 javax.swing.JRadioButtonMenultem 1.2:
JRadioButtonMenuItem(String label)
JRadioButtonMenuItemCString label , boolean state)
【API】javax.swing.AbstractButton 1.2:
boolean isSelected()
void setSelected(boo1ean state)
弹出菜单(pop-up menu) 是不固定在菜单栏中随处浮动的菜单。
创建一个弹出菜单与创建一个常规菜单的方法类似,但是弹出菜单没有标题。
JPopupMenu popup = new JPopupMenu();
然后用常规的方法添加菜单项:
JMenuItem item = new JMenuItem("Cut");
item.addActionListener(listener);
popup.add(item);
弹出菜单并不像常规菜单栏那样总是显示在框架的顶部,必须调用 show 方法菜单才能显示出来。 调用时需要给出父组件以及相对父组件坐标的显示位置。例如:
popup.show(panel , x, y);
通常, 当用户点击某个鼠标键时弹出菜单。 这就是所谓的弹出式触发器(pop-up trigger) 。 在 Windows 或者 Linux 中, 弹出式触发器是鼠标右键。要想在用户点击某一个组件时弹出菜单, 需要按照下列方式调用方法:
component.setComponentPopupMenu(popup) ;
偶尔会遇到在一个含有弹出菜单的组件中放置一个组件的情况。这个子组件可以调用下列方法继承父组件的弹出菜单。调用:
child.setInheritsPopupMenu(true);
【API】javax.swing.JPopupMenu 1.2:
void show( Component c, int x, int y)
boolean isPopupTrigger( MouseEvent event )
1.3【API】java.awt.event.MouseEvent 1.1:
boolean isPopupTrigger( )
【API】javax.swing.JComponent 1.2:
JPopupMenu getComponentPopupMenu( )
5.0void setComponentPopupMenu( JPopupMenu popup)
5.0boolean getlnheritsPopupMenu( )
5.0void setInheritsPopupMenu( boolean b)
5.0对于有经验的用户来说, 通过快捷键来选择菜单项会感觉更加便捷。可以通过在菜单项的构造器中指定一个快捷字母来为菜单项设置快捷键:
JMenuItem aboutltem = new JMenuItem("About" , ' A');
快捷键会自动地显示在菜单项中, 并带有一条下划线(如图 12-21 所示)。例如,在上面的例子中, 菜单项中的标
签为“ About”, 字母 A 带有一个下划线。当显示菜单时,用户只需要按下“ A” 键就可以这个选择菜单项(如果快捷
字母没有出现在菜单项标签字符串中, 同样可以按下快捷键选择菜单项, 只是快捷键没有显示出来。很自然,这种
不可见的快捷键没有提示效果)。
有时候不希望在菜单项的第一个快捷键字母下面加下划线。例如, 如果在菜单项“ Save As” 中使用快捷键“ A”, 则在第二个“ A” ( Save As) 下面加下划线更为合理。可以调用 setDisplayedMnemonicIndex 方法指定希望加下划线的字符。
【API】javax.swing.JMenultem 1.2:
JMenuItem(String label, int mnemonic )
void setAccelerator(Keystroke k )
【API】javax.swing.AbstractButton 1.2:
void setMnemonic(int mnemonic )
void setDisplayedMnemoniclndex( int index )
1.4【API】javax.swing.JMenultem 1.2:
void setEnabled( boolean b )
【API】 javax.swing.event.MenuListener 1.2:
void menuSelected(MenuEvent e)
void menuDeselected(MenuEvent e )
void menuCanceled(MenuEvent e )
【API】javax.swing.JToolBar 1.2:
JToolBar( )
JToolBar ( String titlestring )
JToolBar( int orientation )
JToolBar( String titleString , int orientation )
JButton add( Action a )
void addSeparator( )
【API】 javax.swing.JComponent 1.2:
void setToolTipText( String text )
【API】java.awt.GridBagConstraints 1.0:
int gridx, gridy
int grid width , gridheight
double weightx, weighty
int anchor
int fill
int ipadx, ipady
Insets insets
GridBagConstraints ( int gridx , int gridy, int gridwidth , int gridheight , double weightx, double weighty, int anchor , int fill ,Insets insets , int ipadx, int ipady)
1.2