GUI简介
Java GUI的核心技术:Swing、AWT
已经不常用了:
- 由于界面不够美观
- 需要JRE环境
学习目的:
- 学习MVC思想
- 自己实现一些快捷工具
- 了解MVC架构,了解监听
AWT
AWT(Abstract Windows Tools),包含了很多的类和接口,提供了各种元素:窗口、按钮、文本框等,用于GUI编程。
其中最重要的组成部分:组件Component,包含button、textArea、label等控件;以及容器组件container、frame、dialog等
UML图:
Frame
Frame frame = new Frame("MyWindow");
// 设置窗口可见性
frame.setVisible(true);
// 设置窗口大小
frame.setSize(800, 500);
// 设置背景颜色
Color color = new Color(0, 0, 0);
frame.setBackground(color);
// 设置弹出初始位置
frame.setLocation(400, 200);
// 固定窗口大小
frame.setResizable(false);
关闭Frame
适配器模式:当接口中包含多个方法,且只想使用其中的某个方法时,可以通过实例化接口的抽象子类,然后重写抽象类中的方法来实现。
// 监听事件,监听窗口关闭事件
// 适配器模式:通过接口的抽象子类进行实例化
frame.addWindowListener(new WindowAdapter() {
// 窗口关闭事件
@Override
public void windowClosing(WindowEvent e) {
// 结束程序
System.exit(0);
}
});
Panel
// 流布局
Panel panel1 = new Panel();
panel1.setBounds(50, 50, 400, 100);
panel1.setBackground(Color.CYAN);
// 在frame中添加panel
frame.add(panel1);
布局管理器
- 流式布局
- 边界布局(东西南北中)
- 表格布局
流式布局
Frame frame = new Frame();
frame.setBounds(200, 200, 400, 400);
// 设置为流式布局
// FlowLayout默认构造参数为1/CENTER,其余可选参数有0/LEFT,2/RIGHT
frame.setLayout(new FlowLayout());
// 按钮组件
Button button1 = new Button("button1");
Button button2 = new Button("button2");
frame.add(button1);
frame.add(button2);
frame.setVisible(true); // setVisible(true)要写在添加控件之后,不然控件初始会不可见
边界布局
Frame frame = new Frame("BorderLayout");
frame.setBounds(200, 200, 300, 300);
// 设置button控件的边界布局位置
frame.add(new Button("1"), BorderLayout.EAST);
frame.add(new Button("2"), BorderLayout.WEST);
frame.add(new Button("3"), BorderLayout.SOUTH);
frame.add(new Button("4"), BorderLayout.NORTH);
frame.add(new Button("5"), BorderLayout.CENTER);
frame.setVisible(true);
边界布局效果
表格布局
Frame frame = new Frame("GridLayout");
frame.setBounds(200, 200, 300, 300);
// 设置3行2列的表格布局
frame.setLayout(new GridLayout(3, 2));
frame.add(new Button("1"));
frame.add(new Button("2"));
frame.add(new Button("3"));
frame.add(new Button("4"));
frame.add(new Button("5"));
frame.add(new Button("6"));
frame.setVisible(true);
表格布局效果:
frame.pack()
用于使frame自适应调整大小,添加frame.pack()
后的效果:
事件监听
Frame对象
添加窗口监听
// 监听事件,监听窗口关闭事件
// 适配器模式:通过接口的抽象子类进行实例化
frame.addWindowListener(new WindowAdapter() {
// 窗口关闭事件
@Override
public void windowClosing(WindowEvent e) {
// 结束程序
System.exit(0);
}
});
Button对象
添加事件监听(通过Lambda表达式)
// 添加事件监听需要一个事件监听器作为参数,可以通过Lambda表达式实现事件监听器接口
// 事件监听器需要提供一个动作事件参数e
button.addActionListener((e)-> System.out.println("111"));
多个控件可以使用一个监听器
// 通过Lambda表达式创建动作监听器对象
ActionListener actionListener =(e) -> {
// e.getActionCommand()获取控件的信息
if (e.getActionCommand().equals("start")) {
System.out.println("Start!!");
} else {
System.out.println("Plz click start");
}
};
Button start = new Button("start");
Button stop = new Button("stop");
// 给两个按钮添加动作监听对象
start.addActionListener(actionListener);
stop.addActionListener(actionListener);
TextField对象
一般编程规范:main方法只负责启动程序,尽量不要在main方法中写大量代码。
单行文本框添加事件监听器,减少main方法中的代码,将代码实现放在其他类的构造器中,main方法中只用实例化类即可启动。
class MyFrame extends Frame {
public MyFrame(){
setLocation(600, 400);
// 创建单行文本,设置长度为20
TextField textField = new TextField(20);
// setEchoChar():设置替换字符
textField.setEchoChar('*');
add(textField);
pack();
// Lambda表达式创建监听器,按下回车才会触发事件
textField.addActionListener(e -> {
// e.getSource用于获取绑定的对象
TextField textField1 = (TextField) e.getSource();
// textField.getText():获取文本框中的文本
System.out.println(textField1.getText());
// setText():设置文本框中的文本
textField1.setText("");
});
setVisible(true);
}
}
程序启动中会输出一个warning:java[28590:6332245] TSM AdjustCapsLockLEDForKeyTransitionHandling - _ISSetPhysicalKeyboardCapsLockLED Inhibit
,这是因为输入法设置中存在其他非英语的输入法,可以忽视。
(参考自stack overflow,在遇到不懂的报错时,推荐用stack overflow查询)。
CalcDemo
尝试用TextField和Button连接监控器实现简易的计算器。
class Calculator extends Frame {
public Calculator() {
// 创建控件
TextField num1 = new TextField(5);
TextField num2 = new TextField(5);
TextField rst = new TextField(6);
Button calc = new Button("=");
Button clear = new Button("Clear");
clear.setForeground(Color.RED);
Label plus = new Label("+");
// 设置为流式布局,按顺序添加控件
setLayout(new FlowLayout());
setLocation(500, 400);
add(num1);
add(plus);
add(num2);
add(calc);
add(rst);
add(clear);
pack();
// 创建事件监听器
ActionListener actionListener = e -> {
int n1 = Integer.parseInt(num1.getText());
int n2 = Integer.parseInt(num2.getText());
rst.setText((n1 + n2) + "");
};
// 绑定事件监听器
calc.addActionListener(actionListener);
clear.addActionListener(e -> {
num1.setText("");
num2.setText("");
rst.setText("");
});
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setVisible(true);
}
}
界面效果:
OOP组合
OOP原则:组合优于继承。
组合是指想要使用其他类中的方法或属性时,通过实例化需要的类,来使用类中的属性和方法,而非继承。组合可以降低耦合性。
当A类想要使用B中的方法时:
- 继承方式:
class A extends B {}
- 组合方式:
class A {
public B b;
b.method();
...
}
采用面向对象的思想重写CalcDemo
类中主要实现类的属性和方法,不应将代码全部写在构造器中,这是面向过程的思想,应该通过方法和属性实现。
如:以下Calculator2类提供了Textfield类型的属性和loadFrame方法。
package xyz.lilmoon.awt.listener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class Calc02 {
public static void main(String[] args) {
new Calculator2().loadFrame();
}
}
class Calculator2 extends Frame {
TextField num1, num2, rst;
public void loadFrame() {
// 创建控件
num1 = new TextField(5);
num2 = new TextField(5);
rst = new TextField(5);
Button calc = new Button("=");
Button clear = new Button("clear");
clear.setForeground(Color.RED);
Label plus = new Label("+");
// 绑定监听器
calc.addActionListener(new Listener(this));
clear.addActionListener(new Clear((this)));
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
// 设为流式布局,添加控件,设置自适应大小及可见性
setLayout(new FlowLayout());
setLocation(500, 400);
add(num1);
add(plus);
add(num2);
add(calc);
add(rst);
add(clear);
pack();
setVisible(true);
}
}
class Listener implements ActionListener {
// 组合:在类中实例化需要的类,来使用类中的属性和方法
Calculator2 calculator;
public Listener(Calculator2 calculator) {
this.calculator = calculator;
}
@Override
public void actionPerformed(ActionEvent e) {
int n1 = Integer.parseInt(calculator.num1.getText());
int n2 = Integer.parseInt(calculator.num2.getText());
calculator.rst.setText((n1 + n2) + "");
}
}
class Clear implements ActionListener {
// 组合:在类中实例化需要的类,来使用类中的属性和方法
Calculator2 calculator2;
public Clear(Calculator2 calculator2) {
this.calculator2 = calculator2;
}
@Override
public void actionPerformed(ActionEvent e) {
calculator2.num1.setText("");
calculator2.num2.setText("");
calculator2.rst.setText("");
}
}
使用内部类进一步优化
将计算和清楚类移到计算器类内部,可以直接使用外部类的属性和方法。内部类一般使用private
修饰。
class Calculator3 extends Frame {
TextField num1, num2, rst;
public void loadFrame() {
// 创建控件
num1 = new TextField(5);
num2 = new TextField(5);
rst = new TextField(5);
Button calc = new Button("=");
Button clear = new Button("clear");
clear.setForeground(Color.RED);
Label plus = new Label("+");
// 绑定监听器
calc.addActionListener(new Listener());
clear.addActionListener(new Clear());
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
// 设为流式布局,添加控件,设置自适应大小及可见性
setLayout(new FlowLayout());
setLocation(500, 400);
add(num1);
add(plus);
add(num2);
add(calc);
add(rst);
add(clear);
pack();
setVisible(true);
}
private class Listener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int n1 = Integer.parseInt(num1.getText());
int n2 = Integer.parseInt(num2.getText());
rst.setText((n1 + n2) + "");
}
}
private class Clear implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
num1.setText("");
num2.setText("");
rst.setText("");
}
}
}