第十二章 图形用户接口
1. 第一个GUI
我们用JFrame来作图像用户接口。一旦创建出JFrame后,你就可以把组件(widget)加到上面。有很多的Swing组件可以使用,它们在javax.swing这个包中。最常使用的组件包括: JButton、JRadioButton、JCheckBox、JLabel、JList、JScrollPane、JSlider、JTextArea、JTextField和JTable等。大部分很容易使用。
import javax.swing.*; // 别忘记引进swing包
public class SimpleGui1 {
JFrame frame = new JFrame(); // 创建出frame
JButton button = new JButton("click me"); // 创建出button
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 这一行程会在Windows关闭时把程序结束掉
frame.setContentPane().add(button); // 把button加到frame的pane上
frame.setSize(300, 300); // 设定frame的大小
frame.setVisible(true); // 最后把frame显示出来
运行代码会生成如下的窗口:
窗口中"click me"按钮占据了所有窗口面积,而且点击后并没有实现任何功能。如果想要知道按钮的事件,就会监听事件的接口。监听接口是介于监听(你)和事件源(按钮)间的桥梁。
事件源(按钮)会在用户做出相关动作时(按下按钮)产生事件的对象。你的程序在大多数情况下是事件的接受方而不是创建方。也就是说,你会花较多的时间当监听者而不是时间来源。
实现监听接口让按钮有一个回头调用程序的方式。interface正是声明调用(call-back)方法的地方。
取得按钮的ActionEvent
- 实现ActionListener这个接口
- 向按钮注册(告诉它你要监听事件)
- 定义事件处理的方法(实现接口上的方法)
import javax.swing.*;
import java.awt.event.*; // ActionListener和ActionEvent所在的包
public class SimpleGUI implements ActionListener { //实现接口,表示其是个ActionListener
JButton button;
public static void main(String[] args) {
SimpleGUI gui = new SimpleGUI();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
button = new JButton("click me!");
button.addActionListener(this); // 向按钮注册
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) { // 实现interface上的方法
button.setText("I have been clicked!"); // 按钮会以ActionEvent对象作为参数来调用此方法
}
}
2. 制作板绘图
我们有时候会想要设定自己想要的图案,这时我们可以创建JPanel的子类并覆盖掉paintComponent()这个方法,并在其中写出自己想要实现的图形或者样式。注意的是这里调用该方法的参数是个和实际屏幕有关的Graphics对象,你无法取得它,只能由系统交给你。
在主程序中创建出JPanel子类的对象,将其加入框架会自动调用paintComponent()方法,不需要用"对象名.paintComponent()"的形式。
2.1 画出图形
import java.awt.*;
import javax.swing.*;
class MyDrawPanel extends JPanel { // 创建JPanel的子类
public void paintComponent(Graphics g) { // 覆盖paintComponent方法,注意一定要加上Graphics对象
g.setColor(Color.blue); // 设定颜色
g.fillRect(20, 50, 100, 100); // Rect表示方形,起始位置(20,50),大小(100,100)
}
}
2.2 显示JPEG
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
Image image = new ImageIcon("PeppaPig.jpg").getImage(); // 引号内为文件名小猪佩奇
g.drawImage(image, 3, 4, this); // 3,4表示图片离左方边缘3个像素,顶端边缘4个像素
}
}
2.3 在黑色背景板显示随机彩色圆圈
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.fillRect(0, 0, this.getWidth(), this.getHeight()); // 用和窗口一样大的黑色方块盖住全部窗口,当做黑色背景
int red = (int) (Math.random()*255); // 设定随机的三基色RGB
int green = (int) (Math.random()*255);
int blue = (int) (Math.random()*255);
Color randomColor = new Color(red, green, blue); // 混合形成随机新颜色
g.setColor(randomColor);
g.fillOval(70, 70, 100, 100); // Oval表示建立椭圆
}
}
Graphics
每个Graphics引用的后面的有个Graphics2D对象,你可以将其转换成2D形式,Graphics2D类有更多的方法。如果你要调用Graphics2D类的方法,就不能直接用g参数。但你可以将其转换为Graphics2D变量,操作方法只需在paintComponent()方法中加入:
Graphics2D g2d = (Graphics2D) g;
可以对Graphics引用调用的方法(部分):
- drawImage( )
- drawLine( )
- drawPolygon( )
- drawRect( )
- drawOval( )
- fillRect( )
- fillRoundRect( )
- setColor
可以对Graphics2D引用调用的方法(部分):
- fill3DRect( )
- draw3DRect( )
- rotate( )
- scale( )
- shear( )
- transform( )
- setRenderingHints( )
下面是个使用Graphics2D类方法制作随机渐变色圆的例子:
public class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g; // 转变类型为Graphics2D
int red = (int) (Math.random()*255);
int green = (int) (Math.random()*255);
int blue = (int) (Math.random()*255);
Color startColor = new Color(red, green, blue);
red = (int) (Math.random()*255);
green = (int) (Math.random()*255);
blue = (int) (Math.random()*255);
Color endColor = new Color(red, green, blue);
GradientPaint gradient = new GradientPaint(70, 70, startColor, 150, 150, endColor); // (70,70)为起点,(150,150)为颜色终点
g2d.setPaint(gradient); // 将“笔刷”设定为渐层
g2d.fillOval(70, 70, 100, 100); // 用目前笔刷填满图形
}
}
GUI的布局
这里先简单说一下GUI的layout,frame默认有5个区域可以安置widget。每个区域可以安置一项。可以通过这样实现:
frame.getContentPane().add(BorderLayout.SOUTH, button);
这段代码可以把button放在界面的最南端,同样你还可以输入EAST, WEST, NORTH, CENTER, 就能将button分别放在东、西、北、 中。
3. 内部类
当我们有两个及以上的按钮时,我们不能同时写两个actionPerformed()方法,这个时候就需要内部类了。内部类就是在其它类中的类,实现的话只需要其在大类的最外部括号中就行。
内部类可以使用外部类所有的方法和变量,即使是私有变量
。
class Myouter {
private int x; // 外部类的私有的x实例变量
MyInner inner = new MyInner(); // 创建内部的实例
public void doStuff() {
inner.go(); // 调用内部的方法
}
class MyInner {
void go() { // 内部也可以使用外部的x变量
x = 42;
}
} // 关闭内部类
} // 关闭外部类
运用内部类我们可以实现多个按钮的功能
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class TwoButton {
JFrame frame;
JButton botton;
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel panel = new MyDrawPanel();
botton = new JButton("I'm a label");
JButton changeLabel = new JButton("Change Label!");
JButton changeColor = new JButton("change color!");
changeLabel.addActionListener(new ChangeLabel());
changeColor.addActionListener(new ChangeColor());
frame.getContentPane().add(BorderLayout.WEST, botton);
frame.getContentPane().add(BorderLayout.EAST, changeLabel);
frame.getContentPane().add(BorderLayout.SOUTH, changeColor);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(500, 500);
frame.setVisible(true);
}
class ChangeLabel implements ActionListener { // 运用内部类实现“changeLable”按钮的功能
public void actionPerformed(ActionEvent event) {
botton.setText("Ouch!");
}
}
class ChangeColor implements ActionListener { //实现“changeColor”按钮的功能
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
}
public static void main(String[] args) {
TwoButton gui = new TwoButton();
gui.go();
}
}
以内部类执行动画效果
运用内部类还可以实现简单的动画,我们只需要把JPanel的子类当做内部类,就可以实现这个功能。
import java.awt.*;
import javax.swing.*;
public class MoveTest {
int x = 50;
int y = 50;
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawPanel panel = new DrawPanel();
frame.getContentPane().add(panel);
frame.setSize(300,300);
frame.setVisible(true);
for(int i = 0; i < 130; i++) {
x++;
y++;
panel.repaint();
try {
Thread.sleep(50); //延迟可以放慢
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
class DrawPanel extends JPanel { // drawPanel充当内部类,可共享想x,y坐标,实现位置变换
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
g.fillOval(x, y, 50, 50);
}
}
public static void main(String[] args) {
MoveTest test = new MoveTest();
test.go();
}
}