绘图(一)弹球小游戏

AWT编程 · 语雀

仓库Java图形化界面: Java图形化界面学习demo与资料 (gitee.com)

很多程序如各种小游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时, 有 时候也必须"动态"地向客户 端生成各种图形、图表,比如 图形验证码、统计图等,这都需要利用AWT的绘图功能。

组件绘图原理

之前我们已经学习过很多组件,例如Button、Frame、Checkbox等等,不同的组件,展示出来的图形都不一样,其实这些组件展示出来的图形,其本质就是用AWT的绘图来完成的。

在AWT中,真正提供绘图功能的是Graphics对象,那么Component组件和Graphics对象存在什么关系,才能让Component绘制自身图形呢?在Component类中,提供了下列三个方法来完成组件图形的绘制与刷新:

paint(Graphics g):绘制组件的外观;

update(Graphics g):内部调用paint方法,刷新组件外观;

repaint():调用update方法,刷新组件外观;

一般情况下,update和paint方法是由AWT系统负责调用,如果程序要希望系统重新绘制组件,可以调用repaint方法完成。

Graphics类的使用

实际生活中如果需要画图,首先我们得准备一张纸,然后在拿一支画笔,配和一些颜色,就可以在纸上画出来各种各样的图形,例如圆圈、矩形等等。

程序中绘图也一样,也需要画布,画笔,颜料等等。AWT中提供了Canvas类充当画布,提供了Graphics类来充当画笔,通过调用Graphics对象的setColor()方法可以给画笔设置颜色。

画图的步骤:

1.自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;

2.在paint方法内部,真正开始画图之前调用Graphics对象的setColor()、setFont()等方法设置画笔的颜色、字体等属性;

3.调用Graphics画笔的drawXxx()方法开始画图。

其实画图的核心就在于使用Graphics画笔在Canvas画布上画出什么颜色、什么样式的图形,所以核心在画笔上,下表中列出了Graphics类中常用的一些方法:

案例:

使用AWT绘图API,完成下图效果

演示代码:

java 复制代码
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

public class SimpleDraw {

    private final String RECT_SHAPE="rect";
    private final String OVAL_SHAPE="oval";

    private Frame frame = new Frame("这里测试绘图");

    private Button drawRectBtn = new Button("绘制矩形");
    private Button drawOvalBtn = new Button("绘制椭圆");

    //用来保存当前用户需要绘制什么样的图形
    private String shape="";

    private MyCanvas drawArea = new MyCanvas();




    public void init(){

        //为按钮添加点击事件
        drawRectBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                shape = RECT_SHAPE;
                drawArea.repaint();
            }
        });

        drawOvalBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                shape = OVAL_SHAPE;
                drawArea.repaint();
            }
        });

        //定义一个Panel,装载两个按钮
        Panel p = new Panel();
        p.add(drawRectBtn);
        p.add(drawOvalBtn);

        //把panel添加到frame底部
        frame.add(p,BorderLayout.SOUTH);

        //设置画布的大小
        drawArea.setPreferredSize(new Dimension(300,200));
        //把画布添加到frame中

        frame.add(drawArea);

        frame.pack();
        frame.setVisible(true);


    }

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


    //1.自定义类,继承Canvas类,重写paint方法

    private class MyCanvas extends Canvas{
        @Override
        public void paint(Graphics g) {
            Random r = new Random();

            if (shape.equals(RECT_SHAPE)){
                //绘制矩形
                g.setColor(Color.BLACK);
                g.drawRect(r.nextInt(200),r.nextInt(100),40,60);
            }

            if(shape.equals(OVAL_SHAPE)){
                //绘制椭圆
                g.setColor(Color.RED);
                g.drawOval(r.nextInt(200),r.nextInt(100),60,40);
            }

        }
    }
}

Java也可用于开发一些动画。所谓动画,就是间隔一定的时间(通常小于0 . 1秒 )重新绘制新的图像,两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画 。

为了实现间隔一定的时间就重新调用组件的 repaint()方法,可以借助于 Swing 提供的Timer类,Timer类是一个定时器, 它有如下一个构造器 :Timer(int delay, ActionListener listener): 每间隔 delay 毫秒,系统自动触发 ActionListener 监听器里的事件处理器方法,在方法内部我们就可以调用组件的repaint方法,完成组件重绘。

案例2:

使用AWT画图技术及Timer定时器,完成下图中弹球小游戏。

演示代码2:

java 复制代码
package awt_swimg.day03;

import awt_swimg.day01.Utils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class PinBall {
    //桌面宽度
    private final int TABLE_WIDTH = 300;
    //桌面高度
    private final int TABLE_HEIGHT = 400;


    //球拍的高度和宽度
    private final int RACKET_WIDTH = 60;
    private final int RACKET_HEIGHT = 20;

    //小球的大小
    private final int BALL_SIZE = 16;

    //定义小球纵向运行速度
    private int ySpeed = 10;
    //小球横向运行速度
    private int xSpeed = 5;

    //定义小球的初始坐标
    private int ballX = 120;
    private int ballY = 20;

    //定义球拍的初始坐标,x坐标会发生变化,y坐标不会发生变化
    private int RACKET_X = 120;
    private final int RACKET_Y = 340;

    //声明定时器
    private Timer timer;
    private JFrame frame=new JFrame("弹球游戏");
    private MyCanvas canvas=new MyCanvas();
    //定义游戏结束的标记
    private boolean isLose = false;

    private KeyListener event = new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();
            if (keyCode == KeyEvent.VK_LEFT) {
                if (RACKET_X > 0) {
                    RACKET_X -= 10;
                }
            }
            if (keyCode == KeyEvent.VK_RIGHT) {
                if (RACKET_X < TABLE_WIDTH - RACKET_WIDTH) {
                    RACKET_X += 10;
                }
            }
        }
    };
    private ActionListener timerTask=new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (ballX<=0||ballX>=TABLE_WIDTH-BALL_SIZE) {
                xSpeed=-xSpeed;
            }
            if (ballY<=0||ballY>=RACKET_Y-BALL_SIZE&&ballX>= RACKET_X &&ballX<= RACKET_X +RACKET_WIDTH) {
                ySpeed = -ySpeed;
            }
            if (ballY>RACKET_Y&&(ballX<RACKET_X||ballX>RACKET_X+RACKET_WIDTH)) {
                timer.stop();
                isLose=true;
                canvas.repaint();
            }

            ballX += xSpeed;
            ballY += ySpeed;
            canvas.repaint();
        }
    };

    private MenuBar menuBar=new MenuBar();
    private Menu option=new Menu("选项");
    private MenuItem reStart=new MenuItem("重新开始");
    private MenuItem exit=new MenuItem("退出");
    public void init(){
        frame.add(canvas);
        frame.setMenuBar(menuBar);
        menuBar.add(option);
        option.add(reStart);
        option.add(exit);
        exit.addActionListener(e -> System.exit(0));
        reStart.addActionListener(e -> {
            isLose=false;
            ballX = 120;
            ballY = 20;
            RACKET_X = 120;
            canvas.repaint();
            timer.start();
        });
        canvas.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));

        canvas.addKeyListener(event);
        frame.addKeyListener(event);
        timer=new Timer(50,timerTask);
        timer.start();

        Utils.setJFrame(frame);
    }

    public static void main(String[] args) {
        new PinBall().init();
    }
    public class MyCanvas extends Canvas{
        @Override
        public void paint(Graphics g) {
            if (isLose) {
                g.setColor(Color.BLUE);
                g.setFont(new Font("Times",Font.BOLD,30));
                g.drawString("游戏结束!",80,200);
            }else{
                g.setColor(Color.red);
                g.fillOval(ballX, ballY, BALL_SIZE, BALL_SIZE);
                g.setColor(Color.green);
                g.fillRect(RACKET_X, RACKET_Y, RACKET_WIDTH, RACKET_HEIGHT);
            }

        }
    }
}
相关推荐
天天扭码9 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶10 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺14 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序21 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜39 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、39 分钟前
Spring Boot 注解
java·spring boot
java亮小白199744 分钟前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康1 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql