第九章 图形用户界面开发
9.1 了解Java的GUI开发
一、AWT、Swing与SWT/JFACE
AWT(Abstract Windowing Toolkit,抽象窗口工具包)类库作为Java最早提供的Java标准类库(位于java.awt包中),提供了一组用于开发Java Applet和Java Application的用户界面组件类、事件处理类、图形绘制类、字体类和布局管理器类等。
Swing完全使用Java编写,因此,其优点是可以很好地跨平台运行。但是,Swing的缺点也很明显,首先,由于它过于灵活,从而导致它过于复杂,进而导致学习困难;其次,由于Java语言的运行效率很低,进而导致Swing的运行速度很慢。
利用SWT/JFace可以轻松、高效地开发出用户所需要的任何GUI程序,而且拥有标准的Windows外观,Eclipse软件就是基于SWT/JFace构建的。SWT/JFace的缺点主要有两点:它不是Java语言标准;某些平台并不支持。
二、AWT类库简介
1.组件(Component)类
组件是一个可以以图形化的方式显示在屏幕上并能与用户进行交互的对象,例如一个按钮,一个标签等。组件中不能再放置其他组件,并且组件也不能独立显示,而必须将其放在某个容器中。
2.容器(Container)类
容器类也是组件类的子类,但它与其他组件类有所不同,我们可在其中放置组件或其他容器。因此,我们不再称其为组件,而称其为容器。常见的容器包括窗口(Window)、窗体(Frame)、对话框(Dialog)、面板(Panel)等。
3.布局管理器(LayoutManager)
为了使我们生成的图形用户界面具有良好的平台无关性,Java语言提供了布局管理器这个工具来管理组件在容器中的布局,从而使得用户不必再直接设置组件的位置和大小。
4.事件处理(AWTEvent)类
当用户与组件交互时,会触发一些事件。AWTEvent 类及其子类用于表示AWT组件能够触发的事件。
在Java中,要为组件增加事件处理功能,其步骤如下:
(1)为组件实例注册一个或多个事件侦听器,其参数为一个对应的事件处理类对象。
(2)事件处理类应完全实现事件侦听器接口中的各个方法,以处理事件的各种行为。
5.字体(Font)类
用来创建字体对象,以设置所用字体、大小和效果等。字体对象可用于Graphics 对象和Component对象。
6.图形(Graphics)类
Graphics类为一抽象类,它为Java提供了底层的图形处理功能,使用Graphics类提供的方法可以设置字体和颜色、显示图像和文本,以及绘制和填充各种几何图形。
实例9-1 会说话的按钮
// JavaGUI.java
package Chapter9;
import java.awt.*;
import java.awt.event.*;
public class JavaGUI {
// 创建一个窗体
static Frame f = new Frame("Java GUI演示程序");
// 创建三个标签
static Label lb1 = new Label("欢迎学习Java GUI编程!");
static Label lb2 = new Label("当前发生的按钮事件:");
static Label lb3 = new Label("按 钮 事 件 描 述");
// 创建两个按钮
static Button b1 = new Button("会说话的按钮");
static Button b2 = new Button("退出按钮");
public static void main(String args[]) {
// ----------------------------------------------
// 设置窗体的背景色(橘黄色)与前景色(红色)
// 窗体的前景色用于设置按钮标签、标签等的文字颜色
f.setBackground(Color.orange);
f.setForeground(Color.red);
// 设置窗体的宽度和高度
f.setSize(200, 200);
// 将窗体的布局设置为顺序布局
f.setLayout(new FlowLayout());
// 设置标签3的背景色为青色
lb3.setBackground(Color.CYAN);
// ----------------------------------------------
// 将各标签和按钮顺序添加到窗体中
f.add(lb1);
f.add(lb2);
f.add(lb3);
f.add(b1);
f.add(b2);
// (1)通过调用addMouseListener方法为按钮b1注册MouseEvent事件
// 侦听器MouseListener。其中,要处理的事件类型可以从方法名中
// 看出。例如,本方法是 addMouseListener,则要处理的事件为MouseEvent
// (2)该方法的参数是事件处理类的对象,它必须实现侦听器接口MouseListener
// (3)MouseListener侦听器有多个方法,以处理鼠标的各种动作,如
// 鼠标进入按钮上方、单击按钮、鼠标离开按钮等
b1.addMouseListener(new Button1Handler());
// 为按钮b2注册ActionEvent事件侦听器ActionListener。只有单击
// 按钮时才发生ActionEvent事件
b2.addActionListener(new Button2Handler());
// -----------------------------------------------
// 使窗体在屏幕上居中放置
f.setLocationRelativeTo(null);
// 使窗体可见
f.setVisible(true);
}
}
// 定义实现MouseListener接口的MouseEvent事件处理类
class Button1Handler implements MouseListener {
// 鼠标按键在组件上单击(按下并释放)时调用此方法
public void mouseClicked(MouseEvent e) {
JavaGUI.lb3.setText("你已单击鼠标!");
}
// 鼠标进入到组件上方时调用此方法
public void mouseEntered(MouseEvent e) {
JavaGUI.lb3.setText("你已进入按钮上方!");
}
// 鼠标离开组件时调用此方法
public void mouseExited(MouseEvent e) {
JavaGUI.lb3.setText("你已离开按钮上方!");
}
// 鼠标按键在组件上按下时调用此方法
public void mousePressed(MouseEvent e) {
JavaGUI.lb3.setText("你已按下按钮!");
}
// 鼠标按钮在组件上释放时调用此方法
public void mouseReleased(MouseEvent e) {
}
}
// 定义实现ActionListener接口的ActionEvent事件处理类
class Button2Handler implements ActionListener {
// 本接口只有一个方法,因此事件发生时,系统会自动调用本方法
// 系统产生的ActionEvent事件对象被当作参数传递给该方法
public void actionPerformed(ActionEvent e) {
System.exit(0); // 退出系统
}
}
9.2 掌握容器组件的用法
容器是一种特殊的组件,特殊性在于它可以容纳组件对象与其他容器对象。Container类作为容器类的基类,直接派生出Window、Panel等子类。
容器作为特殊的组件,具有以下特征:
容器具有一定的空间范围和位置坐标,该位置坐标既可以表示容器的绝对位置,也可以表示相对其上层容器的相对位置。
Window、Frame及Dialog是三个能作为顶级容器的组件。所谓顶级容器,是指能直接加载到桌面,不需要放置在其他容器中便能显示,并能容纳其他容器的容器。
一、窗体容器Frame
窗体容器Frame是应用程序经常使用的基本容器之一,窗体是一个可以带边框、标题栏、菜单栏与窗口缩放功能按钮(包括窗口最大化、最小化及关闭)的窗口。
Frame类的常用构造方法如下:
Frame():创建没有标题内容的不可见窗体。要使对象显示,必须调用对象的setVisible()方法。
Frame(String title):创建以参数title为标题内容的不可见窗体。
Frame类的常用方法如下:
public void addFocusListener(FocusListener l):为组件添加焦点侦听器,以便当此组件获得或失去键盘焦点时能够进行一些操作。
public void addMouseListener(MouseListener l):为组件添加鼠标侦听器,以便程序能够处理诸如鼠标移至组件上方、单击组件或鼠标离开组件等鼠标事件。
public void setTitle(String title):设置Frame的标题为title。
public void setSize(int width, int height):调整组件的大小,使其宽度为width,高度为height。
public void setLocation(int x, int y):将组件移到新位置。通过此组件父级坐标空间中的x和y参数来指定新位置的左上角。
二、面板容器Panel
面板是包含在窗体中的一个矩形区域,它不带有标题栏、菜单栏以及边框,因而通常用于辅助定位组件。
可使用构造方法Panel()或Panel(LayoutManager layout)来创建面板。其中,使用构造方法Panel()创建面板时,其默认布局管理器是FlowLayout布局管理器。
实例9-2 创建简易文本编辑器
【实例描述】
首先创建一个窗体容器,然后为其添加了一个主菜单项和一个文本区,利用这些菜单项和文本区可分别完成文件内容编辑,新建、打开和保存文件的功能。
【技术要点】
(1)菜单的结构为:MenuBar(菜单条)→ Menu(主菜单项)→ MenuItem(子菜单项),因此,我们可以通过调用MenuObject.add(MenuItemObject)方法创建主菜单,通过调用MenuBarObject.add(MenuObject)方法将主菜单添加到菜单条中。
(2)要为菜单添加相应的功能,应为各子菜单项对象增加事件侦听器,然后编写相应的事件处理程序。
(3)TextArea对象可用于编辑文本内容,利用其setText()方法可设置在文本区显示的内容,利用其getText()方法可获取文本区的内容。使用时,我们只需将其加入窗体即可。
在Java中,针对一些事件侦听器接口,系统定义了对应的实现类,称为事件适配器类。事件侦听器类只要继承事件适配器类,仅需重写需要的方法就可以处理某个特定事件了。
// TextEditor.java
package Chapter9;
import java.awt.*;
import java.awt.event.*; // 引入所有的事件类
import java.io.FileReader;
import java.io.FileWriter;
import javax.swing.JFileChooser;
public class TextEditor extends Frame implements ActionListener {
MenuBar mainmenubar = new MenuBar(); // 声明菜单条
Menu file; // 声明主菜单项
MenuItem nw; // 声明各子菜单项
MenuItem op;
MenuItem cl;
MenuItem sf;
MenuItem ex;
TextArea tx; // 声明文本区对象
public TextEditor(String title) {
super(title); // 调用父类构造方法
CloseHandler handler = new CloseHandler(); // 定义窗体事件的侦听器对象
this.addWindowListener(handler); // 为当前窗体注册侦听器对象
setSize(400, 400); // 设置窗体尺寸
setLocationRelativeTo(null); // 使窗体在屏幕上居中放置
menuinit(); // 构建与处理菜单
tx = new TextArea(); // 创建文本区对象
this.add(tx); // 将文本区对象放入窗体
setVisible(true); // 使窗体可见
}
// 菜单构建与处理
void menuinit() {
mainmenubar = new MenuBar(); // 定义主菜单栏
file = new Menu("文件"); // 定义主菜单项file
nw = new MenuItem("新建文件"); // 定义各子菜单项
op = new MenuItem("打开文件");
cl = new MenuItem("关闭文件");
sf = new MenuItem("保存文件");
ex = new MenuItem("退 出");
file.add(nw); // 将各子菜单项加入到主菜单项中
file.add(op);
file.add(cl);
file.add(sf);
file.add(ex);
mainmenubar.add(file); // 将主菜单项加入到主菜单栏
setMenuBar(mainmenubar); // 为窗体设置主菜单
nw.addActionListener(this); // 为各菜单项注册事件侦听器
op.addActionListener(this);
cl.addActionListener(this);
sf.addActionListener(this);
ex.addActionListener(this);
}
// 窗体的ActionEvent事件处理方法
public void actionPerformed(ActionEvent e) {
Object ob = e.getSource(); // 获取事件对象
JFileChooser f = new JFileChooser(); // 创建文件选择器对象
if ((ob == nw) || (ob == cl)) { // 选择"新建文件"或"关闭文件"子菜单项
tx.setText(""); // 清空文本区
} else if (ob == op) { // 选择"打开文件"子菜单项
// 弹出具有自定义 approve 按钮的自定义文件选择器对话框
f.showOpenDialog(this);
try {
// 定义一个字符缓冲器对象s
StringBuffer s = new StringBuffer();
// 构造一个FileReadder对象in,其参数为在文件选择器
// 对话框中选中的文件
FileReader in = new FileReader(f.getSelectedFile());
// 读入文件内容,将其放入字符缓冲器对象s中
while (true) {
int b = in.read();
if (b == -1)
break;
s.append((char) b);
}
tx.setText(s.toString()); // 将文件内容显示在文本区
in.close(); // 关闭文件
} catch (Exception ee) {
}
} else if (ob == sf) { // 选择"保存文件"子菜单项
f.showSaveDialog(this); // 显示文件选择对话框
try {
// 创建FileWriter对象,其参数为前面选择的文件
FileWriter out = new FileWriter(f.getSelectedFile());
out.write(tx.getText()); // 将文本区内容写入文件
out.close(); // 关闭文件
} catch (Exception ee) {
}
} else if (ob == ex) // 选择"退 出"子菜单项
System.exit(0); // 退出系统
}
public static void main(String[] args) {
new TextEditor("简易文本编辑器");
}
}
// CloseHandler类实现关闭窗口的功能
class CloseHandler extends WindowAdapter { // 继承适配器类
public void windowClosing(WindowEvent e) { // 处理关闭窗口事件的方法
System.exit(0); // 终止当前线程
}
}
程序运行结果:
9.3 掌握常用非容器组件的用法
一、标签组件Label
标签(Label)一般用于信息提示,它没有边框和其他修饰,并且显示的文本是静态的,即运行时用户不能对文本内容进行修改,但可以利用程序通过调用其setText()方法重设标签内容。
Label类的构造方法如下:
Label():创建一个空的标签,不显示任何内容。
Label(String label):创建一个显示内容为label的标签。
二、按钮组件Button
按钮(Button)主要用于响应用户的鼠标单击或键盘按下事件。Button对象一般都带有一个文本标题,用来说明自身的功能或作用。
Button类的构造方法如下:
public Button():创建一个没有标签的按钮。
public Button(String label):创建一个标签为label的按钮。
Button类的常用方法如下:
public String getLabel():返回按钮的标签。
public void setLabel(String label):将按钮的标签设置为label。
三、文本框组件TextField
文本框(TextField)表示只有一行显示空间的矩形框,用户可以在该区域编辑、修改字符串等操作。
TextField类的构造方法如下:
TextField():创建一个默认长度的文本框。
TextField(int columns):创建一个长度为columns的文本框。
TextField(String text):创建一个带有初始字符串text的文本框。
TextField(String text,int columns):创建一个带有初始字符串text并且长度为columns的文本框。
TextField类的常用方法如下:
public void addActionListener(ActionListener l):添加指定的操作侦听器,以从此文本字段接收操作事件。
public void setEchoChar(char c):设置用户输入的回显字符。当文本框作为密码输入框时,通常将回显字符设置为星号(*)。
public void setText(String text):设置文本框显示内容为text。
public String getText():获取文本框的内容。
public void setEditable(boolean b):设置文本框内容是否可编辑。
实例9-3 创建用户登录界面
【实例描述】
利用标签和文本框组件创建了一个用户登录界面。其中,当用户在用户名文本框中输入用户名后,如果按回车键或者在密码文本框中单击,则程序会在控制台中显示用户输入的用户名;当用户在密码文本框中输入密码后,如果按回车键或者在用户名文本框中单击,则程序会在控制台中显示用户输入的密码。
【技术要点】
我们分别为用户名文本框和密码文本框注册了两个侦听器KeyListener(监视键盘输入)和FocusListener(监视组件获取或失去键盘焦点),并编写了相关事件处理类,从而使得当用户在文本框中按回车键,或者在另外一个文本框中单击,使得当前文本框失去键盘焦点时,用户能对在文本框中输入的内容进行处理。
// TextFieldTest.java
package Chapter9;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class UserLogin {
Frame app = new Frame("TextField 组件");
Label lblName = new Label("UserName:");
static TextField txtName = new TextField();
Label lblPass = new Label("Password:");
static TextField txtPass = new TextField();
public UserLogin() {
app.setSize(300, 150); // 设置窗体尺寸
app.setLayout(null); // 取消窗体的布局管理器
/* 设置姓名栏对应的标签与文本域的位置与大小 */
lblName.setBounds(60, 50, 70, 20);
txtName.setBounds(135, 50, 100, 20);
// 为文本框添加事件侦听器
txtName.addKeyListener(new keyHandler());
txtName.addFocusListener(new focusHandler());
/* 设置密码栏对应的标签与文本域的位置与大小 */
lblPass.setBounds(60, 90, 70, 20);
txtPass.setBounds(135, 90, 100, 20);
txtPass.setEchoChar('*'); // 设置密码框文本域的回显字符
// 为密码框添加事件侦听器
txtPass.addKeyListener(new keyHandler());
txtPass.addFocusListener(new focusHandler());
/* 将组件添加到窗体容器内 */
app.add(lblName);
app.add(txtName);
app.add(lblPass);
app.add(txtPass);
/* 设置窗体的位置与可见性 */
app.setLocation(200, 100);
app.setVisible(true);
}
public static void main(String[] args) {
// 创建对象
UserLogin tft = new UserLogin();
}
}
// 定义实现keyListener接口的keyEvent事件处理类
class keyHandler implements KeyListener {
// 按下某个键时调用此方法
public void keyPressed(KeyEvent e) {
// 获取事件对象
Object ob = e.getSource();
// 如果事件对象为txtName,并且按下回车键,则在控制台中
// 显示输入的用户名
if ((ob == UserLogin.txtName) && (e.getKeyCode() == 10)) {
System.out.println(UserLogin.txtName.getText());
}
// 如果事件对象为txtPass,并且按下回车键,则在控制台中显示输入的密码
else if ((ob == UserLogin.txtPass) && (e.getKeyCode() == 10)) {
System.out.println(UserLogin.txtPass.getText());
}
}
// 释放某个键时调用此方法
public void keyReleased(KeyEvent e) {
}
// 键入某个键时调用此方法
public void keyTyped(KeyEvent e) {
}
}
// 定义实现FocusListener接口的FocusEvent事件处理类
class focusHandler implements FocusListener {
// 获取键盘焦点
public void focusGained(FocusEvent e) {
}
// 失去键盘焦点
public void focusLost(FocusEvent e) {
// 获取事件对象
Object ob = e.getSource();
// 如果事件对象为txtName,则在控制台中显示输入的用户名
if (ob == UserLogin.txtName) {
System.out.println(UserLogin.txtName.getText());
}
// 如果事件对象为txtPass,则在控制台中显示输入的密码
else if (ob == UserLogin.txtPass) {
System.out.println(UserLogin.txtPass.getText());
}
}
}
程序运行结果
四、文本区组件TextArea
文本区组件TextArea可以显示多行多列的文本。TextArea类的构造方法如下:
TextArea():创建一个空的文本区。
TextArea(int row,int columns):创建一个大小为row行columns列的文本区。
TextArea(String text,int row,int columns):创建一个包含初始内容text并且大小为row行columns列的文本区。
TextArea类的常用方法如下:
public void append(String s):在文本区的末尾处追加字符串s。
public void insert(String str,int pos):在文本区指定位置pos处插入文本str。
void replaceRange(String s,int start,int end):用文本s替换start与end位置之间的文本。
五、复选框组件Checkbox和单选按钮组件CheckboxGroup
Checkbox是一个带有文本标签的组件,该组件通过一个可以勾选的方框,提供了一种在两种状态之间相互转换的操作。
Checkbox类的构造方法如下:
Checkbox():创建一个没有标签的复选框。
Checkbox(String label, boolean state, CheckboxGroup group):创建一个标签为label,状态为state的复选框,并使其处于复选框组group中。
单选按钮是Checkbox类在CheckboxGroup组件的控制下,创建出来的一种特殊的选择工具。创建单选按钮的过程如下:
① 创建一个CheckboxGroup对象,假定对象命名为chBtnGrp。
② 使用Checkbox类的构造方法创建若干个复选框对象。
③ 对每个复选框对象,通过调用其setCheckboxGroup(chBtnGrp) 方法将它们加入到CheckboxGroup对象chBtnGrp中,即可将它们转变为一组单选按钮。
六、选项框组件Choice
选项框(Choice)又称为下拉式列表,它允许从一系列的文本列表中选取一个,并将它置于列表顶端的文本框内。
Choice类只有一个无参数的构造方法Choice(),Choice对象初始时只拥有一个空的列表,可以使用添加表项的add()方法,插入表项的insert()方法,以及删除表项的remove()方法对选项框内容实行动态管理。此外,还可利用getSelectedItem()方法获取当前选择项的字符串表示形式。
提示:有关复选框组件Checkbox、单选按钮组件CheckboxGroup和选项框组件Choice的详细创建和使用方法,请参考实例9-4。
七、列表框组件List
与选项框Choice组件不同,列表框(List)组件一次可以选中多个选项,List组件显示的是单一的选项列表 。
List类的构造方法如下:
public List():创建新的滚动列表。默认情况下,有四个可视行,并且不允许进行多项选择。
public List(int rows):创建一个用指定可视行数初始化的新滚动| 列表。默认情况下,不允许进行多项选择。
List类的常用方法如下:
public void add(String item):向滚动列表的末尾添加指定的项。
public void add(String item, int index):向滚动列表中索引指示的位置添加指定的项。索引是从零开始的。如果索引值小于零,或者索引值大于或等于列表中的项数,则将该项添加到列表的末尾。
public String[] getSelectedItems():获取此滚动列表中选中的项,将 其放入字符串数组。如果没有选中的项,则返回一个零长度的数组。
八、滚动条组件Scrollbar
滚动条(Scrollbar)为用户提供了一种从给定值域范围内选取特定值的便捷方式,它分为垂直方向与水平方向两种类型。Scrollbar组件两端表示滚动条的最大值与最小值,并且各有一个方向相反的箭头,指示当前的增减方向。
Scrollbar类的构造方法如下:
public Scrollbar():构造一个新的垂直滚动条。滚动条的默认属性下表所示。
public Scrollbar(int orientation):构造一个具有指定方向的新滚动条。orientation参数必须是Scrollbar.HORIZONTAL或Scrollbar.VERTICAL这两个值之一,它们分别指示滚动条是水平滚动条,还是垂直滚动条。
public Scrollbar(int orientation, int value, int visible, int
minimum, int maximum):构造一个新的滚动条,它具有指定的 方向、初始值、可见量、最小值和最大值。
Scrollbar类的常用方法如下:
public int getValue()获取此滚动条的当前值。
public void setValue(int newValue)将此滚动条的值设置为指定
值。
// ScrollbarTest.java
package Chapter9;
import java.awt.*;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
public class ScrollbarTest implements AdjustmentListener {
// 声明并创建窗体
Frame app = new Frame("Scrollbar组件");;
// 创建三个标签
Label lblColor[] = { new Label("Red:"), new Label("Green:"),
new Label("Blue:") };
// 创建三个水平方向的滚动条,滑块宽度为20,滚动条最小值为0,最大值为255
Scrollbar scbColor[] = {
new Scrollbar(Scrollbar.HORIZONTAL, 0, 20, 0, 255),
new Scrollbar(Scrollbar.HORIZONTAL, 0, 20, 0, 255),
new Scrollbar(Scrollbar.HORIZONTAL, 0, 20, 0, 255) };
void sbinit() {
app.setSize(260, 250);
app.setLayout(null);
for (int k = 0; k < 3; k++) { // 设置标签与滚动条的位置与大小
lblColor[k].setBounds(110, 60 + 60 * k, 50, 20);
scbColor[k].setBounds(30, 85 + 60 * k, 200, 20);
// 为滚动条注册侦听器
scbColor[k].addAdjustmentListener(this);
}
for (int k = 0; k < 3; k++) { // 将标签与滚动条添加到窗体中
app.add(lblColor[k]);
app.add(scbColor[k]);
}
app.setLocation(200, 100);
app.setVisible(true);
}
public static void main(String[] args) {
// 声明并创建ScrollbarTest对象
ScrollbarTest sbt = new ScrollbarTest();
// 初始化对象
sbt.sbinit();
}
public void adjustmentValueChanged(AdjustmentEvent e) {
Object ob = e.getSource(); // 获取事件对象
// 在控制台中显示滚动条当前值
// 滚动条的getValue()方法用于读取滚动条当前值
if (ob == scbColor[0]) {
System.out.print(scbColor[0].getValue()+" ");
} else if (ob == scbColor[1]) {
System.out.print(scbColor[1].getValue()+" ");
} else if (ob == scbColor[2]) {
System.out.print(scbColor[2].getValue()+" ");
}
}
}
9.4 了解布局管理器的特点
一、顺序布局FlowLayout
顺序布局管理器FlowLayout将组件按照从左到右、从上到下的顺序进行布局。采用FlowLayout布局,组件按原有的尺寸显示。如果一行排列不完组件时,则自动转换到下一行继续排列。改变窗口大小时,组件会随着窗口的大小自动调整位置。
FlowLayout类的构造方法如下:
FlowLayout():创建一个FlowLayout对象,居中对齐,水平和垂直间距为默认值。
FlowLayout(int align):创建一个指定对齐方式为align的FlowLayout对象。
FlowLayout(int align,int hgap,int vgap):创建一个FlowLayout对象,具有指定的对齐方式align,其水平间距为hgap,垂直间距为vgap。
二、边界布局BorderLayout
边界布局管理器BorderLayout是将整个容器划分为东(East)、西(West)、南(South)、北(North)、中(Center)5个区域进行布局。
BorderLayout类的构造方法如下:
BorderLayout()
BorderLayout(int hgap,int vgap)
其中,参数hgap指定水平间距;vgap指定垂直间距。方法add(Componet comp,Object constrains)可以将组件添加到容器中的指定区域。其中,参数comp表示添加的组件对象,参数constrains表示组件在容器中的放置区域:East、West、South、North与Center。
三、网格布局GridLayout
网格布局管理器(GridLayout)是将整个容器划分为m行×n列的规则网格,每个网格单元都是大小相同的矩形区域,系统按照从左到右、从上到下的顺序向网格中逐个添加组件。
GridLayout类的构造方法如下:
GridLayout()
GridLayout(int rows,int cols)
GridLayout(int rows,int cols,int hgap,int vgap)
其中,参数rows表示行数;cols表示列数;hgap表示水平间距;vagp表示垂直间距。
四、卡片布局CardLayout
卡片布局管理器CardLayout用于将组件以卡片的形式进行布局。每张卡片属于不同的层次,并且充满整个容器。当想向容器中添加组件时,需要指定所在卡片的名字。作为卡片的组件可以是一般的GUI组件元素(如Button、Label组件等),也可以是包含其他组件的容器组件(如Panel容器等)。
CardLayout类的构造方法如下:
CardLayout()
CardLayout(ing hgap,int vgap)
其中,参数hgap指定水平间距;vgap指定垂直间距。
9.5 进一步了解GUI的事件处理机制与方法
一、GUI事件处理机制
如果在用户与事件源(各种GUI组件)的交互过程中触发了事件,事件源将把事件的处理权委托给事件侦听器,事件侦听器一旦接收到事件对象,即刻调用处理该事件的方法,完成对事件的响应。
Java在java.awt.event包中定义了各种事件类。其中,AWTEvent类是所有事件类的基类。
二、事件适配器类
在Java中,针对一些事件侦听器接口,系统定义了对应的实现类,称为事件适配器类。事件侦听器类只要继承事件适配器类,仅需重写需要的方法就可以处理某个特定事件了。
// EventAdapter.java
package Chapter9;
import java.awt.*;
import java.awt.event.*;
public class EventAdapter extends Frame {
private TextArea txtApp = new TextArea("这是文本区域");
private Button btnApp = new Button("这是按钮");
// 创建鼠标事件侦听器
MouseEventHandler handler = new MouseEventHandler();
public EventAdapter(String title) {
super(title);
// 为窗体注册窗口事件侦听器
this.addWindowListener(new WindowAdapter() {
// 重写windowClosing()方法
public void windowClosing(WindowEvent e) { // 关闭当前窗口
System.exit(0);
}
});
// 设置窗体的尺寸,取消其布局管理器,并使窗体在屏幕上居中放置
this.setSize(300, 200);
this.setLayout(null);
this.setLocationRelativeTo(null);
// 设置文本域的字体、位置和尺寸
txtApp.setFont(new Font("宋体", Font.PLAIN, 16));
txtApp.setBounds(0, 30, 300, 100);
// 为文本域注册鼠标事件侦听器
txtApp.addMouseListener(handler);
// 设置按钮的位置和尺寸
btnApp.setBounds(120, 150, 60, 30);
// 为按钮注册鼠标事件侦听器
btnApp.addMouseListener(handler);
// 将文本域和按钮添加到窗体中
this.add(txtApp);
this.add(btnApp);
// 显示窗体
this.setVisible(true);
}
public static void main(String args[]) {
new EventAdapter("Adapter Usage Demo");
}
// 定义继承MouseAdapter适配器的事件侦听器类
class MouseEventHandler extends MouseAdapter {
public void mousePressed(MouseEvent e) {
// 检测事件源是文本域还是按钮
if (e.getSource() == txtApp) {
String s = "鼠标所点击的位置为文本域!\n";
s += "鼠标所处的位置为:\nX=" + e.getX() + ";Y=" + e.getY();
// 用红色字体显示鼠标点击的位置坐标
txtApp.setForeground(Color.RED);
txtApp.setText(s);
}
if (e.getSource() == btnApp) {
String s = "鼠标所点击的位置为按钮!\n";
s += "鼠标所处的位置为:\nX=" + e.getX() + ";Y=" + e.getY();
// 用蓝色字体显示鼠标点击的位置坐标
txtApp.setForeground(Color.BLUE);
txtApp.setText(s);
}
}
}
}
综合实例——围棋对弈
// ChessPad.java
package Chapter9;
import java.awt.*;
import java.awt.event.*;
public class ChessPad extends Panel implements MouseListener, ActionListener {
// 定义棋子颜色变量,1代表黑色棋子,-1代表白色棋子
int x = -1, y = -1, chessmancolor = 1;
Button btn = new Button("重新开局"); // 定义重新开局按钮
// 定义提示下棋的两个文本框
TextField text_1 = new TextField("请黑棋下子");
TextField text_2 = new TextField();
ChessPad() { // 棋盘构造方法
// 设置面板的尺寸和背景颜色,并取消其布局管理器
this.setSize(440, 400);
this.setBackground(Color.pink);
this.setLayout(null);
addMouseListener(this); // 为当前对象注册MouseEvent侦听器
btn.setBounds(10, 5, 60, 26); // 设置按钮的位置和大小
btn.addActionListener(this); // 为按钮注册ActionEvent侦听器
this.add(btn); // 将按钮添加到面板中
// 将两个提示文本框加入面板,并设置其位置和大小
this.add(text_1);
text_1.setBounds(90, 5, 90, 24);
this.add(text_2);
text_2.setBounds(290, 5, 90, 24);
// 设置文本框不可以被编辑
text_1.setEditable(false);
text_2.setEditable(false);
}
// 绘制棋盘外观的方法
public void paint(Graphics g) {
for (int i = 40; i <= 380; i = i + 20) {
g.drawLine(40, i, 400, i); // 绘制棋盘直线
}
g.drawLine(40, 400, 400, 400); // 绘制棋盘下边界
for (int j = 40; j <= 380; j = j + 20) { // 绘制棋盘竖线
g.drawLine(j, 40, j, 400);
}
g.drawLine(400, 40, 400, 400); // 绘制棋盘右边界
// 添加5个椭圆,参数表示椭圆的左上角x.、y坐标、宽度和高度*
g.fillOval(97, 97, 6, 6);
g.fillOval(337, 97, 6, 6);
g.fillOval(97, 337, 6, 6);
g.fillOval(337, 337, 6, 6);
g.fillOval(217, 217, 6, 6);
}
public void mousePressed(MouseEvent e) { // 实现鼠标按下的方法
// 如果用户单击棋子,则记录棋子的X、Y坐标
// InputEvent类的getModifiers()方法用来返回当前鼠标的状态
if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
// 得到棋子的X、Y坐标
x = (int) e.getX();
y = (int) e.getY();
// 创建黑棋和白棋对象
ChessPoint_black blackPoint = new ChessPoint_black(this);
ChessPoint_white whitePoint = new ChessPoint_white(this);
int a = (x + 10) / 20, b = (y + 10) / 20;
// 如果鼠标在棋盘外单击,则不下棋子
if (x / 20 < 2 || y / 20 < 2 || x / 20 > 19 || y / 20 > 19) {
} else {
// 如果为黑色棋子,则添加到棋盘,并设置 其大小、位置以及提示框的文本
if (chessmancolor == 1) {
this.add(blackPoint);
blackPoint.setBounds(a * 20 - 7, b * 20 - 7, 16, 16);
chessmancolor = chessmancolor * (-1);
text_2.setText("请白棋下子");
text_1.setText("");
// 如果为白色棋子,则添加到棋盘
} else if (chessmancolor == -1) {
this.add(whitePoint);
whitePoint.setBounds(a * 20 - 7, b * 20 - 7, 16, 16);
chessmancolor = chessmancolor * (-1);
text_1.setText("请黑棋下子");
text_2.setText("");
}
}
}
}
// 实现重新开局的方法,即清除棋盘上的棋子并设置为初始状态
public void actionPerformed(ActionEvent e) {
this.removeAll(); // 从容器中移除全部组件
chessmancolor = 1;
add(btn);
btn.setBounds(10, 5, 60, 26);
text_1.setBounds(90, 5, 90, 24);
text_1.setText("请黑棋下子");
add(text_1);
text_2.setText("");
text_2.setBounds(290, 5, 90, 24);
add(text_2);
}
// 实现侦听器接口MouseListener中的方法
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
// ChessPoint_black.java
package Chapter9;
import java.awt.*;
import java.awt.event.*;
//定义继承画布的黑棋子类并实现鼠标侦听器接口
public class ChessPoint_black extends Canvas implements MouseListener {
ChessPad cp = null;
ChessPoint_black(ChessPad cp) {
this.cp = cp;
addMouseListener(this); // 注册鼠标侦听器
}
// 设置黑棋子的颜色、位置和大小
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillOval(0, 0, 14, 14);
}
// 实现鼠标按下方法,当鼠标右击棋子时,从棋盘中去掉该棋子(悔棋)
public void mousePressed(MouseEvent e) {
if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
cp.remove(this);
cp.chessmancolor = 1;
cp.text_2.setText("");
cp.text_1.setText("请黑棋下子");
}
}
// 当双击棋子时,则吃掉当前棋子
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() >= 2)
cp.remove(this);
}
// 实现鼠标侦听口类的未实现的方法
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
// ChessPoint_white.java
package Chapter9;
import java.awt.*;
import java.awt.event.*;
public class ChessPoint_white extends Canvas implements MouseListener {
ChessPad cp = null;
ChessPoint_white(ChessPad cp) {
this.cp = cp;
addMouseListener(this);
}
// 设置白棋子的颜色、位置和大小
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillOval(0, 0, 14, 14);
}
// 实现悔棋的方法
public void mousePressed(MouseEvent e) {
if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
cp.remove(this);
cp.chessmancolor = -1;
cp.text_2.setText("请白棋下子");
cp.text_1.setText("");
}
}
// 实现吃掉棋子的方法
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() >= 2)
cp.remove(this);
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
// Chess.java
package Chapter9;
import java.awt.*;
import java.awt.event.*;
public class Chess extends Frame {
ChessPad cp = new ChessPad(); // 定义棋盘类
Chess() { // 定义Chess类的构造方法
this.setLayout(null); // 取消窗体的默认布局管理器
// 定义标签并设置其位置、大小和背景颜色,然后将其加入窗体
Label lb = new Label("单击左键下棋子,双击吃棋子,用右键单击棋子悔棋", Label.CENTER);
lb.setBounds(70, 55, 440, 26);
lb.setBackground(Color.orange);
add(lb);
add(cp); // 将棋盘加入窗体
cp.setBounds(70, 90, 440, 440); // 设置棋盘的位置和大小
// 实现关闭窗口方法
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setSize(600, 550); // 设置窗体的大小
setLocationRelativeTo(null); // 使窗体在屏幕上居中放置
this.setVisible(true); // 使窗体可见
}
public static void main(String args[]) {
Chess cs = new Chess();
}
}
本章小结
本章介绍了Java语言中用于图形界面开发的AWT类库的相关内容,重点介绍了各种容器、组件、布局管理器和事件处理。其中,容器和组件的综合运用是本章的重点和难点,读者只有根据具体需求灵活运用各种组件才能设计出友好的图形界面,并利用其事件处理机制实现程序的各项功能。