使用AWT做点简单图形.

画图实现原理

 在Component类里提供了和绘图有关的三个方法。

  • paint(Graphics g):绘制组件的外观。
  • update(Graphics g):调用paint()方法,刷新组件外观。
  • repaint():调用update()方法,刷新组件外观.

 上面三个方法的调用关系为:repaint()方法调用update()方法,update()方法调用paint()方法。
Container类中的update()方法先以组件的背景色填充整个组件区域,然后调用paint()方法重画组件。
Container类的 update()方法代码如下:

1
2
3
4
5
6
7
8
public void update(Graphics g) {
if (isShowing()) {
if (! (peer instanceof LightweightPeer)) {
g.clearRect(0, 0, width, height);
}
paint(g);
}
}

 普通组件(Component)的update()方法:

1
2
3
public void update(Graphics g) {
paint(g);
}

 下图是三者调用关系:

关系

 从 图中可以看出,程序不应该主动调用组件的paint()和 update()方法,这两个方法都由AWT系统负责调用。如果程序希望AWT系统重新绘制该组件,则调用该组件的repaint()方法即可。而paint()和update()方法通常被重写。 在通常情况下,程序通过重写paint()方法实现在AWT组件上绘图。重写update()或 paint()方法时,该方法里包含了一个Graphics类型的参数,通过该Graphics参数就可以实现绘图功能。

使用Graphics类

 Graphics类提供了如下几个方法用于绘制几何图形和位图。

  • drawLine():绘制直线。
  • drawString():绘制字符串。
  • drawRect():绘制矩形。
  • drawRoundRect(): 绘制圆角矩形。
  • drawOval():绘制椭圆形状。
  • drawPolygon():绘制多边形边框。
  • drawArc():绘制一段圆弧(可能是椭圆的圆弧)。
  • drawPolyline():绘制折线。
  • fillRect():填充一个矩形区域。
  • fillRoundRect():填充一个圆角矩形区域。
  • fillOval():填充椭圆区域。
  • fillPolygon():填充一个多边形区域。
  • fillArcO:填充圆弧和圆弧两个端点到中心连线所包围的区域。
  • drawlmage():绘制位图。

 除此之外,Graphics还提供了setColor()和setFont()两个方法用于设置画笔的颜色和字体(仅当绘制字符串时有效),其中setColor()方法需要传入一个Color参数,它可以使用RGB、CMYK等方式设 置一个颜色;而 setFont()方法需要传入一个Font参数,Font参数需要指定字体名、字体样式、字体 大小三个属性。

 AWT普通组件也可以通过Color()和 Font()方法来改变它的前景色和字体。除此之外,所有组件都有一个setBackground()方法用于设置组件的背景色。

 AWT专门提供一个Canvas类作为绘图的画布,程序可以通过创建Canvas的子类,并重写它的paint() 方法来实现绘图。下面程序示范了一个简单的绘图程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import javax.swing.*;
import java.awt.*;
import java.util.Random;

/**
* @author YL
* @date 2018/7/28 13:46
*/
public class SimpleDraw {
private final String RECT_SHAPE = "rect";
private final String OVAL_SHAPE = "oval";
private Frame f = new Frame("绘图");
private JButton rect = new JButton("绘制矩形");
private JButton oval = new JButton("绘制圆形");
private String shape = "";
private MyCanvas drawArea = new MyCanvas();

public void init() {
Panel p = new Panel();
rect.addActionListener(e -> {
shape = RECT_SHAPE;
drawArea.repaint();
});
oval.addActionListener(e -> {
shape = OVAL_SHAPE;
drawArea.repaint();
});
p.add(rect);
p.add(oval);
// Dimension用于设置组件的首选大小
drawArea.setPreferredSize(new Dimension(250, 180));
f.add(drawArea);
f.add(p, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}

class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
Random rand = new Random();
if (shape.equals(RECT_SHAPE)) {
g.setColor(new Color(42, 46, 220));
g.drawRect(rand.nextInt(200), rand.nextInt(120), 40, 60);
}
if (shape.equals(OVAL_SHAPE)) {
g.setColor(new Color(200, 29, 30));
g.fillOval(rand.nextInt(200), rand.nextInt(12), 50, 40);
}
}
}

public static void main(String[] args) {
new SimpleDraw().init();
}
}

 Java也可用于开发一些动画。所谓动画,就是间隔一定的时间(通常小于0.1秒)重新绘制新的图像, 两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画。为了实现间隔一定的时间就重新调用组件的repaint()方法,可以借助于Swing提供的Timer类,Timer类是一个定时器,它有如下一个构造器。

  • Timer(int delay, ActionListener listener):每间隔delay毫秒 ,系统自动触发ActionListener监听器里的事件处理器(actionPerformed()方法)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;


/**
* @author YL
*/
public class PinBall {
/**
* 桌面宽度 {@value}
*/
private final int TABLE_WIDTH = 300;
/**
* 桌面高度 {@value}
*/
private final int TABLE_HEIGHT = 400;
/**
* 球拍的垂直位置 {@value}
*/
private final int RACKET_Y = 340;
/**
* 球拍的高度 {@value}
*/
private final int RACKET_HEIGHT = 20;
/**
* 球拍的宽度 {@value}
*/
private final int RACKET_WIDTH = 60;
/**
* 球的大小 {@value}
*/
private final int BALL_SIZE = 16;
private Frame f = new Frame("探求游戏");
Random rand = new Random();
/**
* 小球的纵向速度
*/
private int ySpeed = 10;
/**
* 返回一个-0.5-0.5的比率,用于控制小球的运行方向
*/
private double xyRate = rand.nextDouble() - 0.5;
/**
* 小球的横向速度
*/
private int xSpeed = (int) (ySpeed * xyRate * 2);
/**
* 水平坐标
*/
private int ballX = rand.nextInt(200) + 20;
/**
* 纵向坐标
*/
private int ballY = rand.nextInt(10) + 20;
/**
* 球拍的水平位置
*/
private int racketX = rand.nextInt(200);
private MyCanvas tableArea = new MyCanvas();
Timer timer;
private boolean isLose = false;

public void init() {
tableArea.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));
f.add(tableArea);
KeyAdapter keyProcessor = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (racketX > 0) {
racketX -= 10;
}
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
if (racketX < TABLE_HEIGHT - RACKET_WIDTH) {
racketX += 10;
}
}
}
};
f.addKeyListener(keyProcessor);
tableArea.addKeyListener(keyProcessor);

ActionListener taskPerformer = evt -> {
if (ballX <= 0 || ballX >= TABLE_WIDTH - BALL_SIZE) {
xSpeed = -xSpeed;
}
if (ballY >= RACKET_Y - BALL_SIZE && (ballX < racketX || ballX > racketX + RACKET_WIDTH)) {
timer.stop();
isLose = true;
tableArea.repaint();
} else if (ballY <= 0 || (ballY >= RACKET_Y - BALL_SIZE && ballX <= racketX + RACKET_WIDTH)) {
ySpeed = -ySpeed;
}
ballY += ySpeed;
ballX += xSpeed;
tableArea.repaint();
};
timer = new Timer(100, taskPerformer);
timer.start();
f.pack();
f.setVisible(true);
}

public static void main(String[] args) {
new PinBall().init();
}

class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
if (isLose) {
g.setColor(new Color(255, 0, 0));
g.setFont(new Font("Times", Font.BOLD, 30));
g.drawString("游戏已结束", 50, 200);
} else {
g.setColor(new Color(240, 240, 80));
g.fillOval(ballX, ballY, BALL_SIZE, BALL_SIZE);
g.setColor(new Color(80, 80, 200));
g.fillRect(racketX, RACKET_Y, RACKET_WIDTH, RACKET_HEIGHT);
}
}
}
}