之前本科课程学习Java,大半个学期在讲前五章的类和面向对象的概念,之后就是第六章Java GUI设计。加之当时第一个接触程序设计语言实现GUI(之前的C语言没有这些),就晕菜了,而后又是java非常重要的几个概念和应用:流和文件,多线程,网络编程,数据库编程,servlet技术和JSP技术。因为中间的GUI设计已经让我失去上课的兴趣,之后的这些重要概念和技术到大四的时候才开始捡起来再学,都堪称Java必备经典 ---- 两年后我才在《Java核心技术卷 I》上看到“如果只想用Java编写服务器端的应用程序,对GUI编程不感兴趣,可以跳过这几章(图形程序设计,事件处理,Swing用户界面组件)”。 悲催。。。
吐槽完毕。
一:简单的图形程序设计(不涉及事件处理)
一般来说就是设计一个扩展了 JFrame 的类( class SimpleFrame extends JFrame),在这个类的实例化方法中用setTitle、setBounds、setSize等方法设置框架的标题位置大小等信息,具体可以到JFrame类中。
JFrame的结构画图来说确实有点复杂(详见《Java核心技术卷 I》P250),但是可以简单的把JFrame就当做一个框架,上面的标题栏,下面的大片空白区域就是内容窗格,这也是程序员最关心的部分。通过 frame.add() 方法可以加入很多元素,button, panel等。 可以有层层叠加,JButton添加到JPanel中,然后再将JPanel添加到JFrame中等。另外对需要将一个绘制消息的组件添加大框架中的话,绘制一个组件,这个组件是一个扩展了JComponent的类,并且覆盖了JComponent类中的paintComponent方法。 这个方法中有一个 Graphics 类型的参数,保存了用语绘制图像和文本的设置(字体颜色等)。 在java中,所有的绘制都必须使用 Graphics 对象。
class DrawFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 400;
public DrawFrame()
{
setTitle("DrawTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
DrawComponent component = new DrawComponent();
add(component);
}
}
class DrawComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
double leftx = 100;
double topy = 100;
double width = 200;
double height = 150;
Rectangle2D rect = new Rectangle2D.Double(leftx, topy, width, height);
g2.draw(rect);
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(rect);
g2.draw(ellipse);
g2.draw(new Line2D.Double(leftx, topy, leftx+width, topy+height));
double centerx = rect.getCenterX();
double centery = rect.getCenterY();
double radius = 150;
Ellipse2D circle = new Ellipse2D.Double();
circle.setFrameFromCenter(centerx, centery, centerx+radius, centery+radius);
g2.draw(circle);
}
}
二、事件处理
这也是Java GUI设计的核心,java事件处理采用的是事件委托模型。关键是理解三个对象: 事件对象,监听器对象,事件源(对象)。
事件对象: java是将事件的相关信息封装在一个事件对象中,所有的事件对象都派生于java.util.EventObject类,当然每个事件类型还有子类,如ActionEvent和WindowEvent,类的继承关系如下图
ActionEvent:指示发生了组件定义的动作的语义事件。当特定于组件的动作(比如被按下)发生时,由组件(比如 Button
)生成此高级别事件。事件被传递给每一个 ActionListener
对象,这些对象是使用组件的 addActionListener
方法注册的,用以接收这类事件。
WindowEvent:指示窗口状态改变的低级别事件。当打开、关闭、激活、停用、图标化或取消图标化 Window 对象时,或者焦点转移到 Window 内或移出 Window 时,由 Window 对象生成此低级别事件。 该事件被传递给每一个使用窗口的 addWindowListener 方法注册以接收这种事件的 WindowListener 或 WindowAdapter 对象。
监听器对象:是一个实现了特定接听器接口的类的实例; //类定义:class ColorAction implements ActionListener{},必须实现接听接口ActionListener中的public void actionPerformed(ActionEvent event)方法; 事件处理的核心就是把监听器类写好。
事件源对象:是一个能够注册监听器对象并发送事件对象的对象。// 前面事件源如果是一个button, 用button.addActionListener(new ColorAction)即可完成监听器对象在事件源中的注册;
那么上面三个对象之间的关系就组成了java AWT事件处理的机制:
- 监听器对象是一个实现了特定监听器接口的类的实例;
- 事件源是一个能够注册监听器对象并发送事件对象的对象;
- 当事件发生时,事件源将事件对象传递给所有注册的监听器;
- 监听器对象将利用事件对象中的信息决定如何对事件作出响应;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ButtonTest {
public static void main(String[] args)
{
ButtonFrame frame = new ButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class ButtonFrame extends JFrame
{
private JPanel buttonPanel;
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
public ButtonFrame()
{
setTitle("ButtonTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
buttonPanel = new JPanel();
//makeButton("red", Color.RED);
JButton yellowButton = new JButton("YellowButton");
JButton blueButton = new JButton("Blue");
JButton redButton = new JButton("Red");
buttonPanel = new JPanel();
buttonPanel.add(yellowButton);
buttonPanel.add(blueButton);
buttonPanel.add(redButton);
add(buttonPanel);
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);
}
private class ColorAction implements ActionListener
{
private Color backgroudColor;
public ColorAction(Color c)
{
backgroudColor = c;
}
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroudColor);
}
}
public void makeButton(String name, final Color backgroundColor)
{
JButton button = new JButton(name);
buttonPanel.add(button);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
});
}
三:Swing用户界面组件
这个主要是掌握在类的设计时一种设计模式思想:MVC即模型-视图-控制器模式。另外理解不同布局管理器的特点。各个界面组件(文本输入、单复选按钮/框、菜单、对话框等)可以参考《java核心技术卷 I》都有详细的例子。
下面贴一个简单计算器的实现,主要是布局管理器的练习:
import java.awt.BorderLayout;
import java.awt.DisplayMode;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
//Demo 主要是练习java GUI设计几种布局管理器的使用
public class Calculator {
public static void main(String[] args)
{
CalculatorFrame frame = new CalculatorFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class CalculatorFrame extends JFrame
{
public CalculatorFrame()
{
setTitle("Calculator");
CalculatorJPanel panel = new CalculatorJPanel();
add(panel);
pack(); // 调整此窗口的大小,以适合其子组件的首选大小和布局。
}
}
class CalculatorJPanel extends JPanel
{
private JButton display;
private JPanel panel;
private double result;
private String lastcommand;
private boolean start;
public CalculatorJPanel()
{
setLayout(new BorderLayout());
result = 0;
lastcommand = "=";
start = true;
//add display
display = new JButton("0");
display.setEnabled(false); //
add(display, BorderLayout.NORTH);
ActionListener insert = new insertAction();
ActionListener command = new commandAction();
//add the button in a 4*4 grid
panel = new JPanel();
panel.setLayout(new GridLayout(4, 4));
addButton("1", insert);
addButton("2", insert);
addButton("3", insert);
addButton("4", insert);
addButton("5", insert);
addButton("6", insert);
addButton("7", insert);
addButton("8", insert);
addButton("9", insert);
addButton("0", insert);
addButton("+", command);
addButton("-", command);
addButton("*", command);
addButton("/", command);
addButton("=", command);
addButton(".", insert);
add(panel, BorderLayout.CENTER);
}
private void addButton(String lable, ActionListener listener)
{
JButton button = new JButton(lable);
button.addActionListener(listener);
panel.add(button);
}
private class insertAction implements ActionListener //定义监听器类,监听输入数字和小数点
{
public void actionPerformed(ActionEvent event)
{
String input = event.getActionCommand();
if(start)
{
display.setText("");
start = false;
}
display.setText(display.getText() + input);
}
}
private class commandAction implements ActionListener //定义监听器类,监听输入运算符和负号
{
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();
if(start)
{
if(command.equals("-")) // 负号
{
display.setText(command);
start = false;
}
else {
lastcommand = command;
}
}
else
{
calculate(Double.parseDouble(display.getText()));
lastcommand = command;
start = true;
}
}
}
public void calculate(double x)
{
if(lastcommand.equals("+"))
result += x;
else if (lastcommand.equals("-")) {
result -= x;
}
else if (lastcommand.equals("*")) {
result *= x;
}
else if (lastcommand.equals("/")) {
result /= x;
}
else if (lastcommand.equals("=")) {
result = x;
}
display.setText("" + result);
}
}