画笔Graphics
Java中提供了Graphics类,他是一个抽象的画笔,可以在Canvas组件(画布)上绘制丰富多彩的几何图和位图。
Graphics常用的画图方法如下:
- drawLine(): 绘制直线
- drawString(): 绘制字符串
- drawRect(): 绘制矩形
- drawRoundRect(): 绘制带圆角的矩形
- drawOval():绘制椭圆形
- drawPolygon():绘制多边形边框
- drawArc():绘制一段圆弧(可能是椭圆的圆弧)
- drawPolyline():绘制折线
- fillRect():填充一个矩形区域
- fillRoundRect():填充一个圆角矩形区域
- fillOval():填充椭圆形
- fillPolygon():填充多边形边框
- fillArc():填充一段圆弧(可能是椭圆的圆弧)
- drawImage():绘制位图
AWT专门提供了一个Canvas类作为绘图的画布,程序可以通过创建Canvas的子类,并重写它的paint()方法来实现绘图。
测试代码:
- Canvas()画布类 paint方法画图,方法中传入画笔形参
- Canvas()画布类 setSize(250,250);方法设置画布大小
- Canvas()画布类 repaint(); //清除后重新绘制
- Graphics().setColor方法设置画笔颜色,画笔执行画图动作(红色值,绿色值,蓝色值) 红绿蓝三色取值范围0-255 组合起来可以组成人类可见的任何颜色
import java.awt.*; import java.awt.event.*; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; /** * @ClassName DrawSimple * @projectName: object1 * @author: Zhangmingda * @description: XXX * date: 2021/5/7. */ public class DrawSimple { public static void main(String[] args) { //窗口 Frame frame = new Frame("简单画图示例"); //窗口关闭按钮动作 WindowListener closeListener = new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.out.println("窗口关闭"); System.exit(0); } }; frame.addWindowListener(closeListener); //绘制图形形状变量 AtomicReference<String> shape = new AtomicReference<>(); //画布 Canvas canvas = new Canvas(){ Random random = new Random(); @Override public void paint(Graphics g) { //g为画笔 System.out.println("画图中"); if (shape.get() != null){ switch (shape.get()){ case "rect": //设置画笔颜色Color(红色值,绿色值,蓝色值) 红绿蓝三色取值范围0-255 组合起来可以组成人类可见的任何颜色 g.setColor(new Color(255,0,0)); //画矩形,x,y分别为起始位置,后面两个参数为宽,高 Random.nextInt(200) 为伪随机数 g.drawRect(20,20,random.nextInt(200),random.nextInt(200)); break; case "oval": //画椭圆 g.setColor(new Color(10,100,30)); g.drawOval(40,20,random.nextInt(200),random.nextInt(200)); } } } }; canvas.setSize(250,250); frame.add(canvas); //容器 Panel panel = new Panel(); //按钮 Button drawRectBtn = new Button("画矩形"); Button drawOvalBtn = new Button("画椭圆"); //按钮绑定事件 drawRectBtn.addActionListener(e ->{ shape.set("rect"); canvas.repaint(); //清除后重新绘制 }); drawOvalBtn.addActionListener(e ->{ shape.set("oval"); canvas.repaint(); //清除后重新绘制 }); panel.add(drawOvalBtn); panel.add(drawRectBtn); frame.add(panel,BorderLayout.SOUTH); //窗口自动调整大小 frame.setLocation(400,300); frame.pack(); frame.setVisible(true); } }
开发弹球小游戏
开发思路:动画,就是间隔一定的时间(通常小于1秒)重新绘制新的图像,两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画。这个程序我们要借助Swing包的一个Timer类。
Timer(int delay, ActionListener listener): 每间隔delay秒,系统自动出发ActionListener监听器里的事件处理器(actionPerformed方法)
知识点:
- KeyListener 实现监听键盘按键 触发移动球拍
- 画图逻辑:球到了画布的X轴左右端,向相反方向移动位置,到了Y轴顶端位置0,或者到了球拍接触区域向相反方向移动位置
- Timer定时器,每隔毫秒级别重新画图
- 用到的变量全部定义为类变量
示例代码:
import com.sun.source.tree.NewClassTree; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Random; /** * @ClassName Pinball * @projectName: object1 * @author: Zhangmingda * @description: XXX * date: 2021/5/7. */ public class Pinball { //定义球拍初始位置用随机对象 private Random random = new Random(); /** * 设置画布大小:宽高 */ private int canvaWidth = 300; private int canvaHeight = 400; /** * 球拍初始参数 */ //拍子大小位置 private int rectX = random.nextInt(200); //拍子所在横坐标位置,要在画布范围之内, private int rectY = 355; //拍子所在Y轴坐标位置小于400,和底部留一定空隙 private int rectWidth = 60; //宽度60 private int rectHeight = 15; //厚度 //拍子按下按键拍子移动的像素大小(步伐) int pace =10; /** * 小球的尺寸位置初始参数 */ private int ballSize =15; private int ballX = random.nextInt(200); private int ballY = random.nextInt(100); /** * 小球运动速度值 */ private int ballYSpeed = 5; //Y 轴移动速度 private double xyRate = random.nextDouble() - 0.5; //X轴相对比Y轴运动速度的比率,返回一个-0.5 ~0.5之间的数,移动方向为向左或者向右 private int ballXSpeed = (int) (ballYSpeed * xyRate * 2); // X 轴运动的速度 /** * 定时器Timer */ private Timer timer; /** * 游戏是否结束 */ private boolean gameOver = false; /** * 方法 */ public void play(){ /** * 定义窗口,设置位置和关闭动作 */ Frame frame = new Frame("弹球小游戏"); frame.setLocation(400,300); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.out.println("关闭游戏"); System.exit(0); } }); /** * 定义画布 */ Canvas canvas = new Canvas(){ @Override public void paint(Graphics g) { //如果没有结束 if (!gameOver){ //画球 g.setColor(new Color(30,200,150)); g.fillOval(ballX,ballY,ballSize, ballSize); //画下面的矩形拍子 g.setColor(new Color(75, 79, 194)); g.fillRect(rectX, rectY, rectWidth, rectHeight); }else { //gameOver了 g.setColor(Color.RED); g.setFont(new Font("Times",Font.BOLD,30)); // 设置字体格式字体 g.drawString("Game Over",70, 200); } } }; //设置画布大小 canvas.setPreferredSize(new Dimension(canvaWidth,canvaHeight)); frame.add(canvas); /** * 游戏核心逻辑:动画效果 */ timer = new Timer(50, new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { /** * 如果到了X轴的两端,就向反方向画图 */ if (ballX < 0 || ballX >= canvaWidth - ballSize) ballXSpeed = -ballXSpeed; /** * 如果球接触到了球拍的X轴和Y轴区域内,或者跑到顶端(ballY 坐标小于0)就把Y轴反向移动 */ if (ballY < 0 || (ballY >= rectY - ballSize && ballY < rectY - ballSize/2) && ballX + ballSize /2 >= rectX && ballX + ballSize /2 <= rectX + rectWidth) { System.out.println("ballX:"+ ballX + "ballY:"+ ballY + "rectX:" + rectX + "rectY:" +rectY); ballYSpeed = -ballYSpeed; //反向速度 }else if (ballY >= canvaHeight){ //如果球已经掉到画布之外或者 球拍下 就停止timer循环 timer.stop(); gameOver = true; } ballX += ballXSpeed; ballY += ballYSpeed; canvas.repaint(); } }); timer.start(); /** * 窗口监听键盘 */ KeyListener keyListener = new KeyAdapter() { //添加键盘监听器 @Override public void keyPressed(KeyEvent e) { //当键盘被按下时触发 // System.out.println("按下键盘"); int KeyCode = e.getKeyCode(); //获取按下的键盘代号 switch (KeyCode){ case KeyEvent.VK_LEFT://左键按下 if(rectX - pace > 0){ rectX -= pace; }else { rectX = 0; } break; case KeyEvent.VK_RIGHT://右键按下 if (rectX + pace < canvaWidth - rectWidth){ rectX += pace; }else { rectX = canvaWidth -rectWidth; } break; } canvas.repaint(); } }; frame.addKeyListener(keyListener); /** * 窗口大小自动调节到最优,显示窗口 */ frame.pack(); frame.setVisible(true); } public static void main(String[] args) { new Pinball().play(); } }