使用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;
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);
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;
public class PinBall {
private final int TABLE_WIDTH = 300;
private final int TABLE_HEIGHT = 400;
private final int RACKET_Y = 340;
private final int RACKET_HEIGHT = 20;
private final int RACKET_WIDTH = 60;
private final int BALL_SIZE = 16; private Frame f = new Frame("探求游戏"); Random rand = new Random();
private int ySpeed = 10;
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); } } } }
|