第 6章 GUI编程
软件工程一系
Java程序设计与应用开发
教学内容
GUI实例
GUI组件
现代的用户倾向于使用可以由鼠标方便操作的图形用户界面 (GUI-
Graphical User Interface)程序 。 本章的内容就是介绍如何使用
JFC(Java Foundation Class)中的 Swing组件 (Component)来构建
GUI应用程序 。
事件类
字体类
Graphics
Component
颜色类
布局管理类
java.lang.Object
Checkbox ContainerButton …
Window Panel
AppletDialogFrame
List
图象类 菜单类
6.1 Swing
6.1 Swing起步 -Swing概述
? 在 Java1.0中,已经有一个用于 GUI编程的类库 AWT(Abstract Window Toolkit),称之
为抽象窗口工具箱。遗憾的是,AWT中的组件 (例如按钮,类名为 Button)在实现中使
用了本地代码 (Native Code),这些组件的创建和行为是由应用程序所在平台上的本地
GUI工具来处理的。因此,AWT组件要在不同的平台上提供给用户一致的行为就受到
了很大的限制。同时,AWT组件中还存在很多 bug,这就使得使用 AWT来开发跨平台
的 GUI应用程序困难重重。
? 1996年,SUN公司和 Netsacpe公司在一个称为 Swing的项目中合作完善了 Netsacpe公司
原来开发的一套 GUI库,也就是现在所谓的 Swing组件。 Swing组件和原来的 AWT组件
完全不同,最大的区别就是 Swing组件的实现中没有使用本地代码,这样对底层平台的
依赖型就大为降低,并且可以给不同平台的用户一致的感觉。此外,和原来的 AWT相
比,Swing中提供了内容更多、使用更为方便的组件。
? 读者在阅读一些书籍时,常会遇到名词 JFC(Java Foundation Class)。 JFC的概念是在
1997年的 JavaOne开发者大会上首次提出的,是指用于构建 GUI的一组 API。 实际上,
Swing只是 JFC的一部分,其它的还有二维图形 (Java 2D)API以及拖放 (Drag and
Drop)API等等。
Swing概述 -续
? √在 GUI编程中, 使用什么样的 GUI组件固然很重要, 但是采用什么事件处理模
型同样也很重要 。 Java 1.0中, AWT的事件处理模型是很不完善的 。 Java 1.1
中使用新的 AWT事件处理模型, 在此之后, 未作变动 。 在编写本书时, 使用的
仍旧是 1.1的事件处理模型 。
? √Swing并不是完全取代了 AWT,Swing只是使用更好的 GUI组件 (如 JButton)代
替 AWT中相应的 GUI组件 (如 Button),并且增加了一些 AWT中原来所没有的 GUI组
件 。 并且, Swing仍使用 AWT 1.1的事件处理模型 。
? √虽然现在 AWT组件仍得到支持, 但是建议在你的应用程序中尽量使用 Swing组
件和 1.1的事件模型 。
6.1 组件的分类
? Jcomponent是一个抽象类,用于定义所有子类组件的一般
方法,其类层次结构如下所示:
java.lang.Object
|
+--java.awt.Component
|
+--java.awt.Container
|
+--javax.swing.JComponent
并不是所有的 Swing组件都继承于 JComponent类,
JComponent类继承于 Container类,所以凡是此类的组件
都可作为容器使用。
6.1.2 一个 GUI实例
下面的这个应用程序很简单,只是在屏幕上显示一个框架组
件 (JFrame):
import javax.swing.*;
public class FirstGUI{
public static void main(String []args){
JFrame f=new JFrame(); //创建一个框架对象 f
f.setTitle("FirstFrame"); //设定框架的标题
f.setSize(250,100); //设定框架的大小
f.show() ; //显示框架
}
}
GUI实例-续
? Swing中的组件是, 轻量级, (lightweight)组件,并且每个
组件都可以是一个容器。可以向任何一个组件中添加其它
的组件,但是顶层容器类型的组件不能添加到任何其它组
件中。此外,任何一个 Swing组件要想在屏幕上显示出来,
最终都必须由一个顶层容器来容纳。
? Swing中组件的类名通常以 'J'开头 (如 JFrame),以区别于
AWT中相应的组件 (如 Frame)。
? Swing位于包 javax.swing中,javax是 java extension的缩写
形式,表示 Swing包是 java的一个扩展包。
6.1.3 面板
? JPanel 是常用的 Swing组件之一 。 JPanel本身也是一个容
器,可以向其中添加其它 GUI组件 (如按钮 JButton); 但是
JPanel不是顶层容器,因此,要在屏幕上显示 JPanel,必
须将它添加到一个顶层容器 (如 JFrame)中。 JPanel还具备
在自身表面绘制图形的功能,可以通过定制的方式在面板
表面绘制各种图形。
? Swing中允许组件嵌套添加, 例如:可以将一个 JButton添
加到一个 JPanel中, 再将 JPanel添加到 JFrame中 。 在构建
复杂的用户界面时, 常常需要使用这种嵌套添加的方式 。
? Swing中还允许将一个组件添加到同类型的组件中, 例如:
可以将一个 JPanel添加到另一个 JPanel中去 。
6.1.3.1作为容器
import javax.swing.*;
import java.awt.*;
public class FirstPanel{
public static void main(String []args){
JLabel labOne=new JLabel("这是标签 ");
JTextField txtOne=new JTextField("这是文本框 ");
JPanel p=new JPanel(); //生成面板对象
//将标签和文本框添加到面板容器中
p.add(labOne);
p.add(txtOne);
//给面板增加一个边框
//Border border=BorderFactory.createEtchedBorder();
//p.setBorder(border);
JFrame f=new JFrame(); //创建一个框架对象 f
f.setSize(300,300); //设定框架的大小
//取得框架的内容窗格
Container contentPane=f.getContentPane();
//将面板添加到框架的内容窗格中
contentPane.add(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.show(); //显示框架
}
}
分为两步:
(1) 将一个标签和一个文本框添加到面板中。
(2) 再将面板添加到框架中,然后显示框架。
6.1.3.2表面重绘
? paintComponent(Graphics g)方法 。
? repaint()方法 。
? 在覆盖组件的 paintComponent(Graphics g)方法时,记得
首先调用 super.paintComponent(g)。
6.1.4 改变应用程序的观感
? UIManager.setLookAndFeel(String lnfName )方法。
? SwingUtilities.updateComponentTreeUI() 方法。
例如,要设定 Windows的观感:
try{
String
lnfName="com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(lnfName);
}catch(Exception e){
e.printStackTrace();
}
6.2 AWT事件处理
6.2.1事件处理简介
? 用户对应用程序进行操作时会产生事件 (Event), 事件源的事件发
生后,可以被传递给任何对象,前提是该对象实现 (implements)了
适当的接口并且注册到该事件源。
? 例如,可以给按钮的动作事件 (ActionEvent)注册 (Register)一个动作
事件侦听器 (ActionListener) ; 任何一个实现了 ActionListener接口的
类所生成的对象都可以注册成为按钮的动作事件侦听器。这样,当
按钮的动作事件发生时,动作事件就会传递给已注册的所有侦听器。
? 给事件源注册事件侦听器,使用该事件源中的 addXXXListener
(aListener)方法。依据事件类型的不同,注册的方法名也不同。
? 例如给按钮注册一个动作事件侦听器:
aButton.addActionListener(aActionListener);
而给框架注册一个窗口事件侦听器:
aFrame.addWindowListener(aWindowListener);
一个事件源可以注册多个侦听器,一个侦听器也可以被注册
到多个事件源。
6.2.1 AWT事件处理
事件处理机制,(先理解三个重要的概念 )
? 事件,通常是指因用户的界面操作 (如单击按钮、选择菜单、
往文本域输入内容等 )而引起的组件状态或数据的改变。在
处理事件时,这些受作用的组件被称为事件源。 GUI只有支
持事件处理才能实现程序与用户之间的交互。
? 事件源,发生事件的组件就是事件源
? 事件处理器,负责处理事件的方法。
Java程序对事件进行处理的方法是放在一个类对象中的,这个
类对象就是事件监听器 (事件处理器 )。
三者之间的关系如下,
6.2.2事件处理实例
? 该程序提供了一个按钮, 点击该按钮可以使得应用程序在 Windows
和 Metal观感之间进行切换, 源代码见例 6.2.1,图 6.2.1显示了
运行过程中两种不同的观感 。
? 在例 6.2.1中, 内部类 LookAndFeelListener实现了动作事件侦听
接口 ActionListener。 接口 ActionListener只有一个需要实现的
方法 public void actionPerformed(ActionEvent e)。
? 在类 ActionFrame中, 使用语句:
? ActionListener al=new LookAndFeelListener();
? 创建了对象 al,由于内部类 LookAndFeelListener实现了接口
ActionListener,因 此 对 象 al 可 以 被 注 册 到 动 作 事 件 源
btnLookAndFeel:
? btnLookAndFeel.addActionListener(al);
? 这样,当事件源 btnLookAndFeel发生动作事件时,就会调用侦听
器 al对象中的 actionPerformed(ActionEvent e)方法,并且所发生
的动作事件以一个 ActionEvent类型的对象传递进来。
事件处理实例-续
? 不仅是 JButton类型的事件源能产生动作事件
(ActionEvent),也有其它类型的事件源可以产生动作事件。
例如点击 菜单 (JMenuItem)选项、双击 列表框 中 (JList)的
选项以及在 文本输入框 (JTextField,JPasswordField)中按
下回车键等,也会产生动作事件。
? 本小节介绍了三种 (独立的类、内部类以及匿名内部类,具
体内容参见教材 )方式来注册事件侦听器,希望读者都能
够掌握。因为,这三种方式都可能在别的程序员编写的程
序中出现,而你很可能需要阅读这样的程序。
6.2.3使用事件适配器
? 前面我们已经知道, 在缺省情形下, 关闭框架 (JFrame)只
是使之隐藏 。 可以通过
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
使得在关闭框架时应用程序退出 。 由于在关闭框架时, 会
发生一个窗口事件 (WindowEvent),因此也可以通过捕获
该事件, 然后让应用程序退出 。
? 任何实现了 WindowListener接口的类所生成的对象均可以
注册到窗口事件源 。
? WindowListener接口中包含了如下的七个方法:
public interface WindowListener{
void windowActivated(WindowEvent e);
void windowClosed(WindowEvent e);
void windowClosing(WindowEvent e);
void windowDeactivated(WindowEvent e);
void windowDeiconified(WindowEvent e);
void windowIconified(WindowEvent e);
}
? 一个类要实现 WindowListener接口, 就必须实现该接口
中的所有七个方法 。
? 例如, 可以按如下方式定义一个 WindowExit类, 并且实现
WindowListener接口:
class WindowExitimplements WindowListener{
public void windowActivated(WindowEvent e){}
public void windowClosed(WindowEvent e){}
public void windowClosing(WindowEvent e){
System.exit(0); //强制应用程序退出
}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){};
public void windowOpened(WindowEvent e)
}
然后就可以将 WindowExit生成的对象注册为窗口事件侦听器:
JFrame f=new JFrame();
WindowListener wl= new WindowExit();
f.addWindowListener(wl);
使用事件适配器-续
? 然而, 在很多时候我们只对其中某个或是某几个方法感兴趣,
例如在本例中, 就只对其中的 windowClosing() 方法感兴趣, 如
果像上面一样直接定义一个类去实现 WindowListener接口, 对
那些不感兴趣的方法也必须实现 。
? AWT事件模型中使用 适配器 (Adapter)类来帮助程序员做这件事
情,对于那些 具有不止一 个方法的 事件侦听 接口 (例如
WindowListener),都提供了一个用空方法实现接口中全部方法
的适配器类 (例如 WindowAdapter)。
? 当程序员只需要继承适配器类, 然后覆盖自己感兴趣的方法即
可, 而不用再去实现接口中所有方法 。
? 例如, 上面的 WindowExit类可以按如下方式来定义:
class WindowExitextends WindowAdapter{
public void windowClosing(WindowEvente){
System.exit(0);//强制应用程序退出
}
}
然后使用和前面类似的方式,将 WindowExit生成的对象注册
为窗口事件侦听器:
JFrame f=new JFrame();
WindowListener wl= new WindowExit();
f.addWindowListener(wl);
这样,就大大减少了程序员的编码工作量。
6.2.4 AWT事件继承关系
AWT事件模型中事件类的继承关系
侦听器接口及对应的适配器类
侦听器接口 适配器类
ActionListener 无
AdjustmentListener 无
ComponentListener ComponentAdapter
ContainerListener ContainerAdapter
FocusListener FocusAdapter
ItemListener 无
KeyListener KeyAdapter
MouseListener MouseAdapter
MouseMotionListener MouseMotionAdapter
MouseWheelListener 无
TextListener 无
WindowListener WindowAdapter
WindowFocusListener 无
WindowStateListener 无
6,3布局管理
? 在 Java中,GUI组件在容器中的布局是由容器的布局管理
器 (Layout Manager)来决定的。每个容器都具有一个缺省
的布局管理器。程序设计人员可以方便地改变容器的布局
管理器。例如 面板的缺省布局管理器是流布局管理器
(FlowLayout),内容窗格的缺省布局管理器是边框布局管
理器 (BorderLayout)。 如果面板或是内容窗格的缺省布局
管理器不能满足要求,可以调用这两种容器的
setLayout(aNewLayout)方法来改变其布局管理器,方法
setLayout()的参数是一个布局管理器对象。
6.3.1 流式布局
? 例 6.3.1使用类似方法向一个面板中添加三个按钮,图 6.3.1是程序的运
行结果。可以发现,这三个按钮是按照被添加到面板中的顺序排列在
一行上的。如果将框架的宽度缩小,可以发现三个按钮将排列成两行,
如图 6.3.2所示。由此可知:使用 FlowLayout布局时,GUI组件将按照
添加入容器的顺序自左而右排列在一行上,如果一行排不下,另起一
行;也就是说组件是按照自左而右、自上而下的顺序进行排列的。
? FlowLayout 类提供了三种构建器
? public FlowLayout()、
? public FlowLayout(int alignment)
? public FlowLayout(int alignment,int horizontalGap,int verticalGap)
? alignment参数可以取值 FlowLayout.LEFT,FlowLayout.CENTER或是
FlowLayout.RIGHT,用于指定组件在一行上的对齐方式。
horizontalGap和 verticalGap分别表示组件在水平和垂直方向上的间距
(以像素为单位 )。 alignment缺省取值为 FlowLayout.CENTER,
horizontalGap和 verticalGap均取值为 5。
6.3.2 边框布局
? 使用了 边框 (BorderLayout)布局的容器均提供 5个位置用于存放组件,
分别是 North,South,East,West以及 Center,如图所示:
下面的语句将一个按钮添加到内容窗格中:
contentPane.add(new JButton("North"),BorderLayout.NORTH);
N ort h
S out h
W e s t E a s tCe nt e r
实例:
import javax.swing.*;
import java.awt.*;
public class BorderLayoutExample{
public static void main(String []args){
JFrame f=new JFrame();
Container contentPane=f.getContentPane();
contentPane.add(new JButton("North"),BorderLayout.NORTH);
contentPane.add(newJButton("South"),BorderLayout.SOUTH);
contentPane.add(newJButton("West"),BorderLayout.WEST);
contentPane.add(newJButton("East"),BorderLayout.EAST);
contentPane.add(newJButton("Center"),BorderLayout.CENTER);
f.setSize(300,200);
f.show();
}
}
运行结果
? 观察运行结果,如果对框架进行缩放,可以发现 BorderLayout
的特点是,North和 South位置的组件在水平方向上会扩张,以
填满水平方向的空间,垂直方向不变; West和 East位置的组件
在垂直方向上会扩张,以填满垂直方向的空间,水平方向不变;
而 Center位置的组件会在水平和垂直方向均会尽量扩张,填满中
部的空间。
? 此外,如果向同一个位置 (比如 West)添加多个组件,那么只有最
后一个被添加的才是有效的,也就是说 BorderLayout布局中的
每个位置只能放一个组件。
6.3.3 网格布局
? 网格布局 (GridLayout)将容器划分为大小相同的
网格,把 GUI组件向使用了网格布局的容器中添
加时,是按照自左向右,自上而下的位置存放的。
? GridLayout类提供了两个构建器:
? public GridLayout(int rows,int columns)
? public GridLayout(int rows,int columns,int
horizontalGap,int verticalGap)
? rows和 columns分别指定划分网格的行数及列数。
horizontalGap和 verticalGap用于指定组件在水平
和垂直方向上的间隔,缺省情况下均为零。
例 6.3.3运行结果
? 阅读例 6.3.3 GridWindow.java 的源代码。 可以发现,添加
到使用网格布局容器中的 GUI组件,均具有相同的大小。
对容器进行缩放后,其中的 GUI组件也进行同步的缩放。
也就是说,网格布局适合于规则的布局。
6.3.4 网格袋布局
? 在构建复杂的用户界面时,仅仅使用前面所讲的三种布局往往不能达
到理想的效果。网格袋布局 GridBagLayout是最灵活 (也是最复杂的 )
一种布局管理器,该布局管理器具有强大的功能,非常适合复杂界面
的布局。与网格布局类似,网格袋布局也是将用户界面划分为若干个
网格 (Grid),不同之处在于:
? 网格袋布局中的每个网格的宽度和高度都是可以不一样的。
? 每个组件可以占据一个或是多个网格。
? 可以指定组件在网格中的停靠位置。
? 当将一个 GUI组件添加到使用了网格袋布局的容器中时,需要指定该
组件的位置、大小以及缩放等一系列约束条件。可以使用一个
GridBagConstraints类型的对象来存储这些约束条件。
代码框架:
? 向使用网格袋布局的容器中添加组件的代码框架如下:
JPanel p=new JPanel();
JTextField txtField=new JTextField();
//容器 p设置为网格袋布局
p.setLayout(new GridBagLayout());
//创建约束条件对象
GridBagConstraints constraints=new GridBagConstraints();
//设置具体的约束条件
..,
//按照约束条件 constraints将 txtField添加到 p中
p.add(txtField,constraints);
如何设置约束条件,是使用网格袋布局中最重要、也是最困难
的步骤。下面,我们通过一个实例来仔细说明约束条件中有哪
些参数需要设置以及如何设置。
实例:
? 目标:们想在一个面板上按照图 6.3.7所示排列组件,
此外,我们还希望面板上的标签在面板缩放时,大小
不改变;文本框在面板缩放时,在水平方向上能够相
应地缩放,而在垂直方向上大小不改变。
0 1 2 3
0
1
2
参数设置
? 对于要添加到该面板中的组件,需要有一个 GridBagConstraints 类型
的约束条件对象 constraints。 constraints中有如下字段需要设置:
? constraints.gridx和 constraints.gridy
这两个字段用于指定组件的起始网格坐标 。 例如, 对于出生日期标签:
constraints.gridx=2; constraints.gridy=1;
对于备注文本框,constraints.gridx=1;constraints.gridy=2;
? constraints.gridwidth和 constraints.gridheight
这两个字段用于指定组件所占网格的列数和行数。对于备注文本框:
constraints.gridwidth=2; constraints.gridheight=1;
对于出生日期标签,constraints.gridwidth=1; constraints.gridheight=1;
? constraints.fill
组件在网格中的填充方式。 GridBagConstraints 中定义了
一些常量用于 确定组件在网格中的填充方式:
GridBagConstraints,HORIZONTAL //水平方向尽量扩张
GridBagConstraints,VERTICAL //垂直方向尽量扩张
GridBagConstraints,BOTH //水平、垂直方向均扩张
GridBagConstraints,NONE //水平、垂直方向均不扩张
? constraints.insets
该字段是一个 Insets类型的对象。 该对象用作所添加组件的
外部填塞,其大小由该对象中的 left,top,right及 buttom字
段决定。例如:
constraints.insets= new Insets(2,2,2,2);
参数设置
? constraints.ipadx和 constraints.ipady
与 constraints.insets字段相对应,这两个字段称为组件
的内部填塞。这两个值被加到组件的最小宽度和最小高
度上,从而保证组件不会收缩到它的最小尺寸之下。
? constraints.anchor
当组件比所在网格小的时候,可以使用该字段来确定
组件在网格中的停靠位置。 GridBagConstraints中定义
了如下的常量来表示组件的停靠位置:
CENTER NORTH NORTHEAST
EAST SOUTHEAST SOUTH
SOUTHWEST WEST NORTHWEST
参数设置
? constraints.weightx和 constraints.weighty
这两个字段是组件在水平方向和垂直方向的扩张权重。如果在某个方
向上不希望组件扩张,则将该方向上的权重置为 0。例如对于出生日
期标签,水平、垂直方向均不希望扩张:
constraints.weightx=0; constraints.weighty=0;
对于备注文本框,水平方向扩张,而垂直方向不扩张:
constraints.weightx=1; constraints.weighty=0;
理论上权重可以取任意非负值,用以表示扩张的程度。但是实践中我
们发现,通过调整权值来设定各个组件扩张意义并不大。一个简单、
适用的规律就是:将需要扩张方向上的权重设定为 1;不需要扩张方
向上的权重设定为 0。
? 特别要小心,权重字段的取值要和 fill字段相适应。
? 源代码见 例 6.3.4 。
6.4 常用 GUI组件
标签 (JLabel)
? 标签 (JLabel)通常是用来标识另外一个组件的含义 。以在标签上显示
文字、图象或是文字图象的组合。
JLabel labText=newJLabel(“文本标签, );
labText.setText("文本标签 ");
ImageIcon icon = new ImageIcon("image/greenflag20.gif");
JLabel labImage=new JLabel(icon);
labImage.setIcon(aIcon);
? 如果要在标签上同时显示文本和图象, 可以使用 JLabel提供的一个构
建器 JLabel(String text,Icon icon,int horizontalAlignment)。 该构建器
中, 第一个参数是欲显示的文本, 第二个参数是欲显示的图象, 第三
个参数为水平方向上的对齐方式, 取值为,SwingConstants.LEFT、
SwingConstants.RIGHT或 SwingConstants.CENTER。 如:
? JLabel labTextImage=new JLabel(,文 本 图 象 标 签,,icon,
SwingConstants.LEFT);
标签
标签上同时显示文本和图象时,在缺省情况下,文字是显示在
图象的右侧的。如果希望文字显示在图象的左侧,可以使用如
下方法:
labTextImage.setHorizontalTextPosition(SwingConstants.LEFT);
类似地,可以设置文本与图象在垂直方向上的相对位置。
标签还支持使用 HTML类型的文本参数,使用 HTML可以方便
地在标签上显示丰富多彩的文本,例如:
String htmlText="<html> a <font color=red> Red</font>
Label</html>";
JLabel labHTML=new JLabel(htmlText);
文本框
用户可以在文本框中输入单行文本并且进行编辑。下面的代码生成了一个文
本框对象:
JTextField txtA=new JTextField();
在生成一个文本框对象时初始化文本框中的文本内容:
JTextField txtA=new JTextField("abc");
指定文本框的列宽度,
JTextField txtA=new JTextField(20);
同时指定初始文本内容与列宽度:
JTextField txtA=new JTextField("abc",20);
不要依赖列宽度参数来设定文本框的宽度大小。列宽度参数只是影响到文本
框初始大小的设定,最终还是由文本框所在容器的布局管理器来决定的。
使用文本框对象的 getText()方法,可以取得文本框中的文本内容。
setEditable(boolean aValue)方法来设置文本框是否可编辑。
密码框
密码框实际上是一种特殊类型的文本框,用户可以向其中输入文本并加以编辑。
和文本框不同的是,向密码框中输入文本时,显示的不是实际输入的文本,
而是特殊的回显字符 (通常是 '*')。可以使用 setEchoChar(char c)方法来改变缺
省的回显字符。
需要注意的是,取得文本框中的文本时,使用方法 getText(),该方法返回的是
一个 String类型的对象;而要取得密码框中的文本,使用方法 getPassword(),
该方法返回的是一个 char数组。
例如,创建了一个密码框:
JPasswordField txtPwd=new JPasswrodField(20);
设定该密码框的回显字符为 '#':
txtPwd.setEchoChar('#');
取得密码框中的内容:
char []pwd=txtPwd.getPassword();
也可以方便地将 char数组转化为 String类型的对象:
String pwdStr=new String(txtP.getPassword());
文本域
? 文本域允许用户在其中输入多行文本并进行编辑。创建一个文本域对象:
JTextArea txtArea=new JTextArea();
? 还可以在创建时指定文本域的行数和列数:
JTextArea txtArea=new JTextArea(10,30);
? 需要注意的是,和文本框一样,不要依赖列这两个参数来设定文本域的大小。
? 文本域对象的 setLineWrap(true)方法来将文本域设置为自动换行 。
? 给文本域加上滚动条非常简单,只需要将文本域作为参数创建一个滚动窗格 (JScrollPane)即可 (不仅
仅是文本域,很多其它组件需要增加滚动条时,也是将组件添加到滚动窗格中 ):
JFrame f=new JFrame();
JTextArea t=new JTextArea();
JScrollPane scroll=new JScrollPane(t);
f.getContentPane().add(scroll);
? 滚动窗格提供了方法用来设定水平或是垂直滚动条的显示策略:
setHorizontalScrollBarPolicy(int policy)
setVerticalScrollBarPolicy(int policy)
参数 policy可以取值为 (三种策略 ):
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED //根据需要显示
JScrollPane.VERTICAL_SCROLLBAR_NEVER //从不显示
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS //一直显示
组合框
? 使用组合框 JComboBox 。这样,一方面可以减少用户的输入工作量,另一方面还可以
减少用户输入出错的机会。
? 组合框可以以两种模式工作:可编辑模式与不可编辑模式。在缺省情形下,组合框处
于不可编辑的模式。
在不可编辑编辑模式下,用户点击组合框后,组合框会提供一个选项列表供用户选择,
并且用户只能从该选项列表中选择一项作为组合框的输入。在可编辑模式下,一方面
用户可以从选项列表中选择,另一方面还可以直接在组合框中输入并进行编辑。
? 使用 addItem(Object anObject)方法向组合框中添加选项,例如:
JComboBox comFont=new JComboBox();
comFont.addItem(“宋体, );
? 如果想在选项列表中插入选项,使用 insertItemAt(Object anObject,int index)方法,第
一个参数是待插入的选项,第二个参数是插入的位置索引,例如:
comFont.insertItemAt(“隶书,,2);
? 组合框还提供了两种方法用以删除选项列表中的选项,removeItem(Object anObject)和
removeItemAt(int anIndex)。 例如:
comFont.removeItem("宋体 ");
comFont.removeItemAt(0)
组合框事件
? 当用户选中了组合框选项列表中的一项,组合框会生成一个动作事件 (ActionEvent)。
监听该事件,就可以取得当前被选中的选项。在事件侦听器中使用 getSelectedItem()方
法:
Object selectedItem=comFont.getSelectedItem();
也可以先使用 getSelectedIndex()得到所选中选项的位置索引,然后使用 getItemAt(int
index)方法来取得选项值:
int index=comFont.getSelectedIndex();
Object selectedItem=comFont.getItemAt(index);
? 在可编辑的组合框中,键入回车后,组合框同样发生动作事件。这里需要注意的是:
如果用户在组合框中输入的值不是选项列表中的任何一项 (以图 6.4.8示例 ),在监听器中
使用 getSelectedItem()方法,得到的结果是 "方正姚体 ":
Object selectedItem=comFont.getSelectedItem();
//selectedItem为 "方正姚体 "
而如果使用如下方式,则不能正确得到用户的实际输入:
int index=comFont.getSelectedIndex(); //index的值为 -1
Object selectedItem=comFont.getItemAt(index);
//selectedItem为 null
6.4.3.1单选按钮
? 单选按钮 (JRadioButton)通常成组 (Group)使用,即若干个单选按钮构成一组,
并且每次只能有一个按钮被选中,适用于从多个备选选项中选择一项的场合
(如图 6.4.9所示 )。从完成的功能来看,类似于不可编辑的组合框。
? 当备选选项内容较少时,既可以使用单选按钮,也可以使用组合框。但是当
备选选项内容较多时,使用单选按钮就不合适了,因为要占据太多的画面显
示空间。
? 在实际使用时,先生成一组单选按钮,例如:
JRadioButton radMSSQL=new JRadioButton("MS SQL Server");
JRadioButton radOracle=new JRadioButton("ORACLE Server");
JRadioButton radMysql=new JRadioButton("MySQL Server");
然后生成一个按钮组 (ButtonGroup)对象,并将这些单选按钮添加到其中,
ButtonGroup group=new ButtonGroup();
group.add(radMSSQL);
group.add(radOracle);
group.add(radMysql);
单选按钮-续
? 当对一组单选按钮进行布局时,是对该组中的每个按钮进行布局,而
不是对按钮组对象布局。按钮组对象只是用来控制这一组按钮的行为
的,即每次仅有一个按钮能被选中。
? 和其它类型的按钮一样,点击一个单选按钮时,同样生成动作事件
(ActionEvent)。 例 6.4.4中,使用了三个单选按钮构成一个按钮组。这
三个单选按钮分别指示不同的数据库服务器类型。当用户点击单选按
钮时,会在一个标签上显示出当前所选定的数据库服务器类型。
? 阅读 例 6.4.4 源代码。
6.4.3.2复选框
? 前面所讲的组合框和单选按钮均只能从备选选项中选择一项,即各个选项之
间是互斥的。当需要从备选选项中选择不止一项时,可以使用复选框
(JCheckBox)。 复选框是一种二状态的 GUI组件:重复点击同一个复选框,会
在选中和未选中这两种状态之间进行切换。一组复选框中可以同时有多个复
选框被选中,如图 6.4.10所示。
? 下面的这条语句生成一个复选框对象,其中的字符串参数用以表示该复选框
的含义:
JCheckBox chkOperation=new JCheckBox("清空操作记录 ");
? 要判断一个复选框是否被选中,使用方法 isSelected()。 如果返回值是 true,表
示选中;若返回值为 false,表示未选中。还可以使用 setSelected(boolean
aValue)方法设定复选框是否被选中,参数 aValue为 true(false)时设定为选中
(未选中 )。
? 同样,点击一个复选框,会生成一个动作事件。
? 点击复选框也会生成一个 ItemEvent事件,任何实现了 ItemListener接口的类
所生成的对象均可以作为 ItemEvent事件的监听器,ItemListener接口中唯一
的方法是 public void itemStateChanged(ItemEvent e)。
6.4.3.4列表框
? 列表框 (JList)将一组选项以列表的方式提供给用户选择 (参见表 6.4.1)。根据列表框所设
定的性质不同,用户可以同时选择一个或是多个选项。下面的两行代码生成了一个列
表框对象:
Object []employee= {"Tom Hanks","Bob","Jack London","Sindy","Mike","Lizz","Jerrey"};
JList lstEmployee=new JList(employee);
数组 employee中的元素将作为列表框 lstEmployee的选项供用户选择。需要注意的是,使
用这种方法创建的列表框,不能再向其中添加、插入或是删除选项。例如,要向其中
添加一个名叫,Jerry”的雇员是不行的,也即这种列表框中的选项是不可改变
(immutable)的。
? 如果希望创建一个可改变选项的列表框,则需要使用一个可改变的数据模型,例如使
用一个 DefaultListModel类型的模型:
DefaultListModel lstModel=new DefaultListModel();
JList lstEmployee=new JList(lstModel);
DefaultListModel类型的模型中存储的数据是可变的。使用 addElement(Object aObject)
向模型中添加数据:
lstModel.addElement("Tom Hanks");
列表框-续
? 如果需要在指定位置插入一个选项,使用 insertElementAt(Object aObject,int
index),第一个参数为欲插入的数据,第二个参数为指定的插入位置索引:
lstModel.insertElementAt("Bob",0);//将 "Bob"置为列表框的第一个选项值
? 有三种方法可以删除模型中的数据:
void removeAllElements(); // 删除模型中所有数据
boolean removeElement(Object aObject); //删除模型中指定的数据
void removeElementAt(int index); //删除模型中指定位置索引的数据
? 当列表框中的选项较多时,就需要给列表框加上滚动条。与给文本域添加滚
动条类似,也是将列表框作为参数创建一个滚动窗格 (JScrollPane)对象:
JScrollPane scroll=new JScrollPane(lstEmployee);
列表框-续
? 使用 setSelectionMode(int mode)方法可设置列表框的选中模式,列表
框共有三种选中模式,ListSelectionModel.SINGLE_SELECTION
ListSelectionModel.SINGLE_INTERVAL_SELECTION)
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
? 不管列表框使用了何种选中模式, 只要改变了选中的内容, 列表框就
会生成一个列表选择事件 ( ListSelectionEvent)。 实现了
ListSelectionListener接口的类所生成的对象可以作为列表选择事件的
侦听器 。 ListSelectionListener接口中唯一的方法是:
public void valueChanged(ListSelectionEvent e)。
6.4.4.1菜单
? 菜单也是一种常用的 GUI组件,菜单采用的是一种层次结构,最顶层是菜单栏
(JMenuBar); 在菜单栏中可以添加若干个菜单 (JMenu),每个菜单中又可以
添加若干个菜单选项 (JMenuItem),分隔线 (Separator)或是菜单 (称之为子菜
单 )。
J M e nuI t e m
J M e nuB a r J M e nu
J M e nu
S e pa r a t or
J M e nuI t e m
菜单-续
构建应用程序的菜单时,先创建一个菜单栏:
JMemuBar menuBar=new JMenuBar();
通常使用框架的 setJMenuBar(JMenuBar aMenuBar)方法将菜单栏置于框架中:
frame.setJMenuBar(menuBar);
随后,创建所需要的各菜单并逐个添加到菜单栏中,例如:
JMenu menuDBAccount=new JMenu("电表出帐 (C)");
...
JMenu menuSysConfig=new JMenu("系统设置 (X)");
menuBar.add(menuDBAccount);
...
menuBar.add(menuSysConfig);
菜单-续
最后, 向各个菜单中添加菜单选项, 分隔线或是子菜单, 这里以图 6.4.11中所示的系统设置菜单为例:
//创建菜单选项或是子菜单
JMenuItem sysConfigItem=new JMenuItem("参数设置 (S)...");
JMenu viewMenu=new JMenu("外观设置 ");
JRadioButtonMenuItem metalItem=new JRadioButtonMenuItem("金属外观 ");
JRadioButtonMenuItem classicItem=new JRadioButtonMenuItem("传统外观 ");
JRadioButtonMenuItem modernItem=new JRadioButtonMenuItem("现代外观 ");
JMenuItem cascadeItem=new JMenuItem("层叠窗口 (C)");
JMenuItem iconifyItem=new JMenuItem("排列图标 (I)");
//将三个单选按钮添加到一个按钮组
ButtonGroup group=new ButtonGroup();
group.add(metalItem);
group.add(classicItem);
group.add(modernItem);
//构建子菜单
viewMenu.add(metalItem);
viewMenu.add(classicItem);
viewMenu.add(modernItem);
//添加到系统设置菜单
menuSysConfig.add(sysConfigItem); //添加菜单选项
menuSysConfig.add(viewMenu); //添加子菜单
menuSysConfig.add(cascadeItem); //添加菜单选项
menuSysConfig.addSeaparator(); //添加分隔线
menuSysConfig.add(iconifyItem); //添加菜单选项
菜单-续
? 通常的菜单选项是 JMenuItem,也可以使用复选框或是单选按钮类型的菜单选
项,分别是 JCheckBoxMenuItem和 JRadioButtonMenuItem。 和 JRadioButton一
样,使用 JRadioButtonMenuItem时,需要将它们添加到同一个按钮组中。
? 当点击一个菜单选项时,会生成一个动作事件 (ActionEvent) 。 为菜单选项添
加事件侦听器就可以侦听其动作事件,例如:
sysConfigItem.addActionListener(aListener);
? 为一个菜单或是菜单选项设置快捷键:
menuSysConfig.setMnemonic('X');
sysConfigItem.setMnemonic('S');
? 如果需要快速选择未打开的菜单中的菜单选项或是子菜单,可以使用加速键。
例如,当希望按下 CTRL+L时就立刻选中 lockItem菜单选项,而不管 lockItem所
在的菜单是否已经打开,就可以使用下面的方法为 lockItem设置加速键:
KeyStroke ks= KeyStroke.getKeyStroke(
KeyEvent.VK_L,InputEvent.CTRL_MASK);
lockItem.setAccelerator(ks);
6.4.4.2弹出式菜单
? 弹出式菜单 (JPopupMenu)是一种特殊的菜单,和普通菜单的区别在于其不固定在菜单栏中,而是可
以四处浮动的。
? 下面的语句创建了一个弹出式菜单:
JPopupMenu popup=new JPopupMenu();
与向菜单中添加菜单选项、分隔线或是子菜单一样,使用同样的方法向弹出式菜单中添加内容。
用户可以通过点击某个特殊的鼠标键 (称为触发器 )来触发弹出式菜单。例如,在 Windows操作系统上,
一般是鼠标右键。
为此, 应用程序中应该监听弹出式菜单的父组件的鼠标事件:当有鼠标事件发生时, 使用
isPopupTrigger()方法来判断是否为弹出式菜单的触发器;如果是, 则在该父组件上显示出弹出式
菜单 。 同样以图 6.4.13所示意的弹出式菜单为例, 该菜单的父组件是一个显示公司徽标的标签
labLogo,当用户在该标签上右击鼠标时, 弹出式菜单出现 。 下面的代码实现了上述功能:
labLogo.addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent e){
if (e.isPopupTrigger()){
popup.show(labLogo,e.getX(),e.getY());
}
}
});
6.4.4.3工具条
? 有些菜单选项的使用频率较高,每次使用都要打开菜单,效率较低。
为此,可以在工具条 (JToolBar)中提供与这些菜单选项相对应快捷按
钮,以提高用户的效率。工具条中通常是一些带有图标的按钮 (如图
6.4.14所示 ),当然也可以是其它类型的 GUI组件,例如组合框等等。
? 通常,工具条所提供的操作是菜单所能提供操作的一个子集,目的是
能够快速访问那些使用频率高的操作。
? 工具条通常被置于布局为 BorderLayout的容器中。在例 6.4.8中,工具
条被置于布局为 BorderLayout的一个面板中。工具条还有一个特点:
可以被拖动到所在容器其它边界 (图 6.4.15),甚至脱离其所在的容器
(图 6.4.16)。
工具条的使用
? 生成一个工具条对象,可以使用语句:
JToolBar toolbar=new JToolBar();
? 还可以在生成工具条时,指明工具条的标题。这样,当工具条脱离所在容器
时,可以在自己的标题栏显示标题 (图 6.4.16):
JToolBar toolbar=new JToolBar("颜色工具条 ");
? 向工具条中添加组件,使用 add方法:
toolbar.add(btnBlue);
toolbar.add(btnYellow);
? 缺省情况下,组件是按照水平方式逐个添加入工具条的,也可以在生成工具
条时指明以垂直方式来添加组件:
JToolBar toolbar=new JToolBar(SwingConstants.VERTICAL);
? 如果希望工具条不能移动,可以使用 setFloatable方法来设置:
toolbar.setFloatable(false);
? 当工具条中的按钮上只显示图标时,用户可能不能确切知道各个按钮的具体
含义。可以使用工具提示解决这个问题:
btnYellow.setToolTipText("面板置为黄色 ");
6.4.5对话框
对话框是用户和应用程序进行交互 (对话 )的一个桥梁:对话框可
以用于收集用户的输入数据传递给应用程序,或是显示应用程
序的运行信息给用户。
对话框分为模式 (modal)和非模式两种。模式对话框处于可见状
态时,用户将不能与应用程序的其它窗口进行交互,而非模式
对话框则没有此限制。
Java中提供了一个类 JOptionPane用于创建简单的模式对话框,
如果希望创建非模式对话框或是自定义对话框可以使用 JDialog。
6.4.5.1 选项对话框
? JOptionPane类中提供了 4种静态方法,用以显示 4种常用的对话框:
showMessageDialog 消息对话框
showInputDialog 输入对话框
showConfirmDialog 确认对话框
showOptionDialog 选项对话框
? 通过观察右侧的对话框可以发现,该对话框主要由如下几个部分构成:
图标、消息以及按钮。
JOptionPane
? 先来看一下图标,系统本身提供了如下四种图标:
? 类 JOptionPane中定义了如下五个常量,
JOptionPane.QUESTION_MESSAGE
JOptionPane.INFORMATION_MESSAGE
JOptionPane.WARNING_MESSAGE
JOptionPane.ERROR_MESSAGE
JOptionPane.PLAIN_MESSAGE //不使用图标
前四个常量对应着四个图标, 第五个常量表示不使用图标 。 开发人员可
以使用这些常量来指定对话框中显示的图标 。 当然, 对话框也提供了
方法使得开发人员可以使用自己的图标 。
Q U E S T I O N I N F O R M A T I O N W A R N I N G E R R O R
JOptionPane
? 图 6.4.18所示的确认对话框中只包含了一条字符串类型的消息 (Really
Quit?)。 对话框不仅仅可以显示字符串类型的消息,还可以显示其它
类型的消息。例如,可以是一副图片、还可以是一个 GUI组件。更广
泛地说,这里的消息可以是任何类型的对象或是对象数组。在书上的
例子中,你可以看到不同类型消息的应用。
? 对话框底部的按钮取决于对话框类型和选项类型。例如,对于确认对
话框,可以使用如下的四种选项类型之一:
DEFAULT_ OPTION
YES_NO_OPTION
YES_NO_CANCEL_OPTION
OK_CANCEL_OPTION
6.4.5.2 自定义对话框
? 目标:例如,现在要设计一个如图 6.4.24所示的登录对话框。该对话
框中包括两个标签、一个文本输入框、一个密码输入框和两个按钮,
这些 GUI组件被添加到一个使用了网格袋布局的面板 panel中,面板
panel又被添加到 LoginDialog的内容窗格。类 LoginDialog继承了
JDialog,先来看一下 LoginDialog的构建器:
public LoginDialog(Frame f,String s,boolean b)
? 该构建器中包含了 3个参数,f和 s分别是对话框的父窗口和标题,布尔
类型的参数 b用来确定对话框的类型,当取值为 true时,表示是模式对
话框;取值为 false时,表示是非模式对话框。
? 阅读源代码。
6.4.5.3 文件对话框
? Java中提供了文件对话框 JFileChooser用于定位文件。
创建一个文件对话框,可以使用语句:
JFileChooser chooser=new JFileChooser();
? 使用上述语句所创建的文件对话框的当前目录为用户 home目录。还可以在创建时指定文件
对话框的当前目录:
//用户当前目录作为对话框当前目录
JFileChooser chooser=new JFileChooser(".");
//c:根目录作为对话框当前目录
FileChooser chooser=new JFileChooser("c:/");
或是使用 setCurrentDirectory方法来设定对话框的当前目录:
chooser.setCurrentDirectory(new File("c:/"));
? 依据调用 chooser中的不同方法,chooser可以显示为打开或是保存对话框,如图 6.4.25所示:
int showOpenDialog(Component parent) //显示为打开对话框
int showSaveDialog(Component parent) //显示为保存对话框
? 上面两个方法中,参数 parent是对话框的父组件,可以为 null。 两个方法返回一个整型值,
用以确定用户是否通过对话框选定了文件,例如:
int result=chooser.showOpenDialog(this);//显示打开对话框
if(result==JFileChooser.APPROVE_OPTION){//用户选定
..,
}else{ //用户取消
...
}
打开 /保存对话框
6.4.6表格
? 表格 (JTable)也是一种常用的 GUI组件,常用来显示大量的数据,如
图 6.4.27所示:
? 表格是模型-视图-控制器设计模式的一个典型应用 。 表格本身并不
存储所显示的数据, 数据实际上是存储在表模型中的, 表格只是表模
型的一种视图, 如图 6.4.28所示:
6.4.6.1 简单表格
? JTable提供了如下两种构建器,可以方便地创建简单表格:
JTable(Object[][] data,Object[] columnNames)
JTable(Vector data,Vector columnNames)
? 观察例 6.4.11 的运行结果可以发现存在不少缺点,例如,每一列的宽
度都是一样的;未能正确显示日期;数值未能按照我们的希望保留到
小数点后面两位,并靠右显示;表格中的数据必须预先存放在一个数
组中或是向量 (Vector)中等等。在一个真正的应用程序中,使用这样
的表格是不能满足实际应用的要求的。
调整列宽度
? 在缺省情况下,表格中每列是等宽的,并且调整某列的宽度时,其它
列的宽度也会相应自动调整。可以使用下面的语句关闭列宽自动调整
特性:
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
之后,要设定某列的宽度,首先依据该列的列名取得列对象,以设定
第一列宽度为例:
TableColumn col=table.getColumn(columnNames[0]);
然后调用 setPreferredWidth方法设定该列宽度:
col.setPreferredWidth(200);
6.4.6.2 定制表模型
前面已经提到, 表格有一个对应的表模型, 数据是存储在表模型中, 表格是表模型的视图 。 表格
在建立视图时总需要自动调用表模型中的一些方法, 这些方法的返回值决定了最终的视图 。
部分常需要用到的方法的名称和含义如下:
public int getRowCount();//取得行数
public int getColumnCount();//取得列数
public Object getValueAt(int row,int column); //取得指定单元格的数据
//指定单元格是否允许编辑
public boolean isCellEditable(int row,int column);
public String getColumnName(int column); //取得指定列的列名
/* 取得指定列的数据类型
视图依据不同的数据类型选择不同的渲染方式 (显示方式 )
在缺省情况下, 视图将按照如下方式进行渲染,
如果 getColumnClass方法返回
1) Boolean.class类型, 使用 JCheckBox显示
2) Number.class,靠右显示该数值
3) ImageIcon.class,居中显示图象
4) 其它 Oject.class,转换为字符串, 靠左显示
*/
public Class getColumnClass(int column);
缺省表模型 DefaultTableModel提供了上述方法的缺省实现 。 例如, DefaultTableModel中的
isCellEditable方法总是返回 true,表示所有的单元格都允许编辑; getColumnClass方法
总是返回 Object.class,因此所有单元格的数据总是作为一个字符串来显示 。
定制表模型-续
? 可以使用 DefaultTableModel创建一个表模型对象,然后再使用表模型创建表格,例如:
DefaultTableModel model=new DefaultTableModel(0,5);//0行 5列的表模型
JTable table=new JTable(model);
然后,可以使用 model的 addRow,removeRow方法向表模型中添加或是删除数据,对表
模型增删数据的结果会自动反映到表格视图上来。
但是,通常情况下,我们并不直接使用 DefaultTableModel。 更多的情形是继承
DefaultTableModel类,并覆盖其中部分方法以达到特殊的要求,详见例 例 6.4.12
CustomTableModel.java 。
定制表模型-续
? 类 CustomTableModel的构建器接收 4个参数:
? int r - 表模型的行数
? int c - 表模型
? String []cn - 列名数组
? Class []dataType - 每列的数据类型
? 首先调用父类的构建器 super(r,c)设定表模型的行数和列数。
? 类 CustomTableModel中覆盖了 isCellEditable方法,该方法始终返回 false,使得所有的
单元格不可编辑。读者还可以根据需要设定哪些单于格允许编辑,例如:
? //偶数列的单元格允许编辑,奇数列的单元格不允许编辑
? public boolean isCellEditable(int row,int col){
? if(col%2==0) return true;
? else return false;
? }
? 类 CustomTableModel中还覆盖了 getColumnClass方法,这样,在使用
CustomTableModel创建表模型时,可以方便地指定每列的数据类型,从而使得表格视
图能够合理地渲染数据。阅读例 例 6.4.13 。
定制表模型-续
我们再提出一个要求:希望数据总是保留小数点后面两位 。 这时候, 使用表格视图对数值类型的缺省渲
染器就不行了, 可以对数值类型定制一个渲染器 。
所谓渲染, 就是指以特定地方式来显示数据 。 例如, 我们可以在单元格中使用一个标签来显示数值, 并
要求数值在标签的右端显示, 同时保留小数点后面两位 。 这里的标签我们称之为渲染器 。 例 6.4.14定
义了类 DoubleRender,该类继承了 JLabel并实现接口 TableCellRenderer。 DoubleRender类型的对
象可以作为数值类型数据的渲染器 。 之所以要实现 TableCellRenderer接口, 是因为表格视图要调用
TableCellRenderer接口中的 getTableCellRendererComponent方法来确定如何进行渲染:
public Component getTableCellRendererComponent(
JTable table,Object value,
boolean isSelected,boolean hasFocus,
int row,int column)
table- 需要渲染的表格
value- 需要渲染的值
isSelected- 该单元格是否选中
hasFocus- 该单元格是否有焦点
row- 该单元格行索引
column- 该单元格列索引
表格视图调用 getTableCellRendererComponent会自动传入上面的 6个参数, 因此只要定制该方法, 就可以
按照要求实现特定的数据渲染方式 (显示方式 )。
定制表模型-续
? 阅读例 例 6.4.14 。
? 在例 6.4.13中生成表格对象语句后加入如下语句 (粗体显示 ),即可完成
对薪水列的定制渲染:
?,..
? JTable table = new JTable(model);
? //使用 DoubleRender渲染器,使得数值保持两位小数,靠右显示
? TableColumn col=table.getColumn(columnNames[4]);
? col.setCellRenderer(new DoubleRender());
?,..
? 运行将得到与图 6.4.27一样的结果。