画笔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.*;<br/>
import java.awt.event.*;<br/>
import java.util.Random;<br/>
import java.util.concurrent.atomic.AtomicReference;

/**<br/>
 * @ClassName DrawSimple<br/>
 * @projectName: object1<br/>
 * @author: Zhangmingda<br/>
 * @description: XXX<br/>
 * date: 2021/5/7.<br/>
 */<br/>
public class DrawSimple {<br/>
    public static void main(String[] args) {<br/>
        //窗口<br/>
        Frame frame = new Frame("简单画图示例");

        //窗口关闭按钮动作<br/>
        WindowListener closeListener = new WindowAdapter() {<br/>
            @Override<br/>
            public void windowClosing(WindowEvent e) {<br/>
                System.out.println("窗口关闭");<br/>
                System.exit(0);<br/>
            }<br/>
        };<br/>
        frame.addWindowListener(closeListener);<br/>
        //绘制图形形状变量<br/>
        AtomicReference<String> shape = new AtomicReference<>();<br/>
        //画布<br/>
        Canvas canvas = new Canvas(){<br/>
            Random random = new Random();<br/>
            @Override<br/>
            public void paint(Graphics g) { //g为画笔<br/>
                System.out.println("画图中");<br/>
                if (shape.get() != null){<br/>
                    switch (shape.get()){<br/>
                        case "rect":<br/>
                            //设置画笔颜色Color(红色值,绿色值,蓝色值) 红绿蓝三色取值范围0-255 组合起来可以组成人类可见的任何颜色<br/>
                            g.setColor(new Color(255,0,0));<br/>
                            //画矩形,x,y分别为起始位置,后面两个参数为宽,高 Random.nextInt(200) 为伪随机数<br/>
                            g.drawRect(20,20,random.nextInt(200),random.nextInt(200));<br/>
                            break;<br/>
                        case "oval":<br/>
                            //画椭圆<br/>
                            g.setColor(new Color(10,100,30));<br/>
                            g.drawOval(40,20,random.nextInt(200),random.nextInt(200));<br/>
                    }<br/>
                }<br/>
            }<br/>
        };<br/>
        canvas.setSize(250,250);<br/>
        frame.add(canvas);

        //容器<br/>
        Panel panel = new Panel();<br/>
        //按钮<br/>
        Button drawRectBtn = new Button("画矩形");<br/>
        Button drawOvalBtn = new Button("画椭圆");<br/>
        //按钮绑定事件<br/>
        drawRectBtn.addActionListener(e ->{<br/>
            shape.set("rect");<br/>
            canvas.repaint(); //清除后重新绘制<br/>
        });<br/>
        drawOvalBtn.addActionListener(e ->{<br/>
            shape.set("oval");<br/>
            canvas.repaint(); //清除后重新绘制<br/>
        });<br/>
        panel.add(drawOvalBtn);<br/>
        panel.add(drawRectBtn);<br/>
        frame.add(panel,BorderLayout.SOUTH);<br/>
        //窗口自动调整大小<br/>
        frame.setLocation(400,300);<br/>
        frame.pack();<br/>
        frame.setVisible(true);<br/>
    }<br/>
}

开发弹球小游戏

开发思路:动画,就是间隔一定的时间(通常小于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.*;<br/>
import java.awt.*;<br/>
import java.awt.event.*;<br/>
import java.util.Random;

/**<br/>
 * @ClassName Pinball<br/>
 * @projectName: object1<br/>
 * @author: Zhangmingda<br/>
 * @description: XXX<br/>
 * date: 2021/5/7.<br/>
 */<br/>
public class Pinball {<br/>
    //定义球拍初始位置用随机对象<br/>
    private Random random = new Random();<br/>
    /**<br/>
     * 设置画布大小:宽高<br/>
     */<br/>
    private int canvaWidth = 300;<br/>
    private int canvaHeight = 400;<br/>
    /**<br/>
     * 球拍初始参数<br/>
     */<br/>
    //拍子大小位置<br/>
    private int rectX = random.nextInt(200); //拍子所在横坐标位置,要在画布范围之内,<br/>
    private int rectY = 355; //拍子所在Y轴坐标位置小于400,和底部留一定空隙<br/>
    private int rectWidth = 60; //宽度60<br/>
    private int rectHeight = 15; //厚度<br/>
    //拍子按下按键拍子移动的像素大小(步伐)<br/>
    int pace =10;

    /**<br/>
     * 小球的尺寸位置初始参数<br/>
     */<br/>
    private int ballSize =15;<br/>
    private int ballX = random.nextInt(200);<br/>
    private int ballY = random.nextInt(100);

    /**<br/>
     * 小球运动速度值<br/>
     */<br/>
    private int ballYSpeed = 5; //Y 轴移动速度<br/>
    private double xyRate = random.nextDouble() - 0.5; //X轴相对比Y轴运动速度的比率,返回一个-0.5 ~0.5之间的数,移动方向为向左或者向右<br/>
    private int ballXSpeed = (int) (ballYSpeed * xyRate * 2); // X 轴运动的速度

    /**<br/>
     * 定时器Timer<br/>
     */<br/>
    private Timer timer;

    /**<br/>
     * 游戏是否结束<br/>
     */<br/>
    private boolean gameOver = false;

    /**<br/>
     * 方法<br/>
     */<br/>
    public void play(){<br/>
        /**<br/>
         * 定义窗口,设置位置和关闭动作<br/>
         */<br/>
        Frame frame = new Frame("弹球小游戏");<br/>
        frame.setLocation(400,300);<br/>
        frame.addWindowListener(new WindowAdapter() {<br/>
            @Override<br/>
            public void windowClosing(WindowEvent e) {<br/>
                System.out.println("关闭游戏");<br/>
                System.exit(0);<br/>
            }<br/>
        });

        /**<br/>
         * 定义画布<br/>
         */<br/>
        Canvas canvas = new Canvas(){<br/>
            @Override<br/>
            public void paint(Graphics g) {<br/>
                //如果没有结束<br/>
                if (!gameOver){<br/>
                    //画球<br/>
                    g.setColor(new Color(30,200,150));<br/>
                    g.fillOval(ballX,ballY,ballSize, ballSize);<br/>
                    //画下面的矩形拍子<br/>
                    g.setColor(new Color(75, 79, 194));<br/>
                    g.fillRect(rectX, rectY, rectWidth, rectHeight);<br/>
                }else { //gameOver了<br/>
                    g.setColor(Color.RED);<br/>
                    g.setFont(new Font("Times",Font.BOLD,30)); // 设置字体格式字体<br/>
                    g.drawString("Game Over",70, 200);<br/>
                }<br/>
            }<br/>
        };<br/>
        //设置画布大小<br/>
        canvas.setPreferredSize(new Dimension(canvaWidth,canvaHeight));<br/>
        frame.add(canvas);<br/>
        /**<br/>
         * 游戏核心逻辑:动画效果<br/>
         */<br/>
        timer = new Timer(50, new ActionListener() {<br/>
            @Override<br/>
            public void actionPerformed(ActionEvent actionEvent) {<br/>
                /**<br/>
                 * 如果到了X轴的两端,就向反方向画图<br/>
                 */<br/>
                if (ballX < 0 || ballX >= canvaWidth - ballSize)<br/>
                    ballXSpeed = -ballXSpeed;<br/>
                /**<br/>
                 * 如果球接触到了球拍的X轴和Y轴区域内,或者跑到顶端(ballY 坐标小于0)就把Y轴反向移动<br/>
                 */<br/>
                if (ballY < 0 || (ballY >= rectY - ballSize && ballY  < rectY - ballSize/2) && ballX + ballSize /2 >= rectX && ballX + ballSize /2 <= rectX + rectWidth) {<br/>
                    System.out.println("ballX:"+ ballX + "ballY:"+ ballY + "rectX:" + rectX + "rectY:" +rectY);<br/>
                    ballYSpeed = -ballYSpeed; //反向速度<br/>
                }else if (ballY >= canvaHeight){ //如果球已经掉到画布之外或者 球拍下 就停止timer循环<br/>
                    timer.stop();<br/>
                    gameOver = true;<br/>
                }<br/>
                ballX += ballXSpeed;<br/>
                ballY += ballYSpeed;<br/>
                canvas.repaint();<br/>
            }<br/>
        });<br/>
        timer.start();<br/>
        /**<br/>
         * 窗口监听键盘<br/>
         */<br/>
        KeyListener keyListener = new KeyAdapter() { //添加键盘监听器<br/>
            @Override<br/>
            public void keyPressed(KeyEvent e) { //当键盘被按下时触发<br/>
//                System.out.println("按下键盘");<br/>
                int KeyCode = e.getKeyCode(); //获取按下的键盘代号<br/>
                switch (KeyCode){<br/>
                    case KeyEvent.VK_LEFT://左键按下<br/>
                        if(rectX - pace > 0){<br/>
                            rectX -= pace;<br/>
                        }else {<br/>
                            rectX = 0;<br/>
                        }<br/>
                        break;<br/>
                    case KeyEvent.VK_RIGHT://右键按下<br/>
                        if (rectX + pace < canvaWidth - rectWidth){<br/>
                            rectX += pace;<br/>
                        }else {<br/>
                            rectX = canvaWidth -rectWidth;<br/>
                        }<br/>
                        break;<br/>
                }<br/>
                canvas.repaint();<br/>
            }<br/>
        };<br/>
        frame.addKeyListener(keyListener);<br/>
        /**<br/>
         * 窗口大小自动调节到最优,显示窗口<br/>
         */<br/>
        frame.pack();<br/>
        frame.setVisible(true);<br/>
    }<br/>
    public static void main(String[] args) {<br/>
        new Pinball().play();<br/>
    }<br/>
}