当前位置: 首页 > article >正文

Java一个简单的反弹动画练习

文章目录

  • 说明
  • 代码详解
    • 创建窗体代码
    • 创建绘图板
    • 创建线程
  • 运行结果
  • 完整代码

说明

做了一个小球和星型做反弹动画的窗体作为练习,分享给大家,为了方便和我一样的小白可以看的比较明白,所以尽量详细的标注了注释,希望能帮到同样在学习路上的朋友

代码详解

创建窗体代码

public class AnimationJFrame extends JFrame {

    //实例化属性
    private final DrawCircleAndStar drawCircleAndStar = new DrawCircleAndStar();//实例化图形绘制
    private final JButton jButton = new JButton();//实例化按钮
    private final AnimationRun animationRun = new AnimationRun();//实例化线程

    //设置绘图全局属性
    private int circleX = 0;//圆形的初始位置X坐标
    private int circleY = 10;//圆形的初始位置Y坐标
    private int circleXDirection = 1;//圆形运动X轴方向
    private int circleYDirection = 1;//圆形运动Y轴方向

    private int starX = 355;//星型的初始位置X坐标
    private int starY = 200;//星型的初始位置Y坐标
    private int starXDirection = 1;//星形运动X轴方向
    private int starYDirection = 1;//星形运动Y轴方向

    //构造方法构造窗体
    public AnimationJFrame(){

        //窗体基本设置
        Container conn = getContentPane();//建立窗体容器
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗体关闭方式
        setBounds(300,300,400,400);//设置窗体位置及大小
        setResizable(false);//窗体大小不可改变
        setLayout(null);//清空窗体布局管理器,不采用默认布局

        //创建动画布局
        JPanel jPanel = new JPanel();//实例化布局
        jPanel.setLayout(null);//清空布局的布局管理器,不采用默认布局
        jPanel.setBounds(10, 10, 365, 300);//动画布局的位置及大小
        jPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));//设置动画布局的边框
        jPanel.setBackground(Color.LIGHT_GRAY);//设置动画布局的底色

        //创建动画图形的标签容器
        JLabel jLabel = new JLabel();//实例化标签
        jLabel.setBounds(0,0,365,300);//设置动画标签的大小和位置

        //设置绘图标签
        drawCircleAndStar.setBackground(new Color(0,0,0,0));//设置绘图标签的背景透明
        drawCircleAndStar.setSize(365,300);//设置绘图标签的大小
        jLabel.add(drawCircleAndStar);//添加绘图板到标签
        jPanel.add(jLabel);//添加动画标签到动画布局

        //设置按键
        jButton.setText("开始");//设置按键显示文字
        jButton.setFont(new Font("黑体", Font.PLAIN, 14));//设置按键文字的字体
        jButton.setBounds(275, 320, 100, 22);//设置按键的位置和大小

        //添加原件到窗体容器
        conn.add(jPanel);//添加动漫布局到容器
        conn.add(jButton);//添加按钮到容器

        //给按钮添加监听
        jButton.addActionListener(e -> {
            if (e.getActionCommand().equals("开始")){
                jButton.setText("暂停一下");//更改按键文字
                animationRun.start();//启动线程

            }else if (e.getActionCommand().equals("暂停一下")){
                jButton.setText("继续");
                animationRun.pause();//暂停线程
            }else if (e.getActionCommand().equals("继续")){
                jButton.setText("暂停一下");
                animationRun.restart();//重启线程
            }
        });
    }

这里需要说明的是,代码中直接把窗体创建在构造方法中,但是直接在构造方法中设置窗体只适用于线程较简单的程序,如果在正式的项目中,应该避免这种写法,因为swing是线程不安全的,应该对窗体单独建立窗体方法容器,例如:

 public AnimationJFrame() {
        SwingUtilities.invokeLater(() -> {
            initUI();
        });
    }

    private void initUI() {
    //代码块...........
    }

创建绘图板

    //创建绘图板
    class DrawCircleAndStar extends JLabel{
        @Override
        public void paintComponent(Graphics g){//重写paintComponent
            super.paintComponent(g);//继承父类的构造方法
            Graphics2D graphics2D = (Graphics2D) g;
            graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//设置绘图抗锯齿

            //绘制圆形
            graphics2D.setColor(Color.RED);//设置填充颜色,红色
            graphics2D.fillOval(circleX,circleY,15,15);//设置圆形的位置和大小

            //绘制星型
            graphics2D.setColor(Color.BLUE);//设置填充色,蓝色

            //设置星型的属性
            int radius = 10;//设置星型的大小
            int[] xPoints = new int[10];//星型顶点的X坐标
            int[] yPoints = new int[10];//星型顶点的Y坐标

            //计算星型顶点的坐标及初始角度
            for (int i = 0; i < 10; i++) {
                double baseAngle = i * 2 * Math.PI / 10 + Math.PI / 2;//初始角度
                if (i % 2 == 0) {//外围顶点的坐标
                    xPoints[i] = starX + (int) (radius * Math.cos(baseAngle));
                    yPoints[i] = starY - (int) (radius * Math.sin(baseAngle));
                } else {//内部顶点的坐标
                    xPoints[i] = starX + (int) (radius * Math.cos(baseAngle) * 0.5);
                    yPoints[i] = starY - (int) (radius * Math.sin(baseAngle) * 0.5);
                }
            }
            Polygon star = new Polygon(xPoints, yPoints, 10);//绘制星型
            graphics2D.fillPolygon(star);//填充星型
        }
    }

创建绘图板,别的没有什么可说的,这里切记一点,无论绘图板继承自布局还是标签,相对的布局和标签是没有大小的,必须在窗体设置中,为它们设定尺寸,比如本代码中的

        drawCircleAndStar.setBackground(new Color(0,0,0,0));//设置绘图标签的背景透明
        drawCircleAndStar.setSize(365,300);//设置绘图标签的大小

如果不设置大小,将无法将绘图板图形显示到容器

创建线程

    //创建运行线程
    class AnimationRun extends Thread{

        boolean flag = false;//设置挂起标志

        synchronized void pause(){//暂停方法
            flag = true;
        }
        synchronized void restart(){//重启方法
            notifyAll();
            flag = false;
        }

        //重写run
        @Override
        public void run(){

            //动画运行
            while (true) {

                //挂起区
                synchronized (this){
                    while (flag){
                        try {
                            wait();//等待挂起
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }

                //判断圆形运动方向
                if (circleX == 0) {
                    circleXDirection = 1;
                } else if (circleX == 350) {
                    circleXDirection = -1;
                }
                if (circleY == 0) {
                    circleYDirection = 1;
                } else if (circleY == 285) {
                    circleYDirection = -1;
                }

                //判断星型运动方向
                if (starX == 10){
                    starXDirection = 1;
                }else if (starX == 355){
                    starXDirection = -1;
                }
                if (starY == 10){
                    starYDirection = 1;
                }else if (starY == 290){
                    starYDirection = -1;
                }

                //设置绘图延迟
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //计算新的圆形位置和星型的位置
                circleX += circleXDirection;
                circleY += circleYDirection;
                starX += starXDirection;
                starY += starYDirection;

                //重绘绘图板
                drawCircleAndStar.repaint();
            }
        }
    }

synchronized相对来说并不是最优的选择,消耗较高,建议优化,另外一个这里重写的不是paint而是重写的paintComponent因为重写paintComponent只绘制图形主体,不会影响边框和背景,建议单独绘制元素的时候使用paintComponent而不是paint

运行结果

在这里插入图片描述

完整代码

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

public class AnimationJFrame extends JFrame {

    //实例化属性
    private final DrawCircleAndStar drawCircleAndStar = new DrawCircleAndStar();//实例化图形绘制
    private final JButton jButton = new JButton();//实例化按钮
    private final AnimationRun animationRun = new AnimationRun();//实例化线程

    //设置绘图全局属性
    private int circleX = 0;//圆形的初始位置X坐标
    private int circleY = 10;//圆形的初始位置Y坐标
    private int circleXDirection = 1;//圆形运动X轴方向
    private int circleYDirection = 1;//圆形运动Y轴方向

    private int starX = 355;//星型的初始位置X坐标
    private int starY = 200;//星型的初始位置Y坐标
    private int starXDirection = 1;//星形运动X轴方向
    private int starYDirection = 1;//星形运动Y轴方向

    //构造方法构造窗体
    public AnimationJFrame(){

        //窗体基本设置
        Container conn = getContentPane();//建立窗体容器
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗体关闭方式
        setBounds(300,300,400,400);//设置窗体位置及大小
        setResizable(false);//窗体大小不可改变
        setLayout(null);//清空窗体布局管理器,不采用默认布局

        //创建动画布局
        JPanel jPanel = new JPanel();//实例化布局
        jPanel.setLayout(null);//清空布局的布局管理器,不采用默认布局
        jPanel.setBounds(10, 10, 365, 300);//动画布局的位置及大小
        jPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));//设置动画布局的边框
        jPanel.setBackground(Color.LIGHT_GRAY);//设置动画布局的底色

        //创建动画图形的标签容器
        JLabel jLabel = new JLabel();//实例化标签
        jLabel.setBounds(0,0,365,300);//设置动画标签的大小和位置

        //设置绘图标签
        drawCircleAndStar.setBackground(new Color(0,0,0,0));//设置绘图标签的背景透明
        drawCircleAndStar.setSize(365,300);//设置绘图标签的大小
        jLabel.add(drawCircleAndStar);//添加绘图板到标签
        jPanel.add(jLabel);//添加动画标签到动画布局

        //设置按键
        jButton.setText("开始");//设置按键显示文字
        jButton.setFont(new Font("黑体", Font.PLAIN, 14));//设置按键文字的字体
        jButton.setBounds(275, 320, 100, 22);//设置按键的位置和大小

        //添加原件到窗体容器
        conn.add(jPanel);//添加动漫布局到容器
        conn.add(jButton);//添加按钮到容器

        //给按钮添加监听
        jButton.addActionListener(e -> {
            if (e.getActionCommand().equals("开始")){
                jButton.setText("暂停一下");//更改按键文字
                animationRun.start();//启动线程

            }else if (e.getActionCommand().equals("暂停一下")){
                jButton.setText("继续");
                animationRun.pause();//暂停线程
            }else if (e.getActionCommand().equals("继续")){
                jButton.setText("暂停一下");
                animationRun.restart();//重启线程
            }
        });
    }

    //创建绘图板
    class DrawCircleAndStar extends JLabel{
        @Override
        public void paintComponent(Graphics g){//重写paintComponent
            super.paintComponent(g);//继承父类的构造方法
            Graphics2D graphics2D = (Graphics2D) g;
            graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//设置绘图抗锯齿

            //绘制圆形
            graphics2D.setColor(Color.RED);//设置填充颜色,红色
            graphics2D.fillOval(circleX,circleY,15,15);//设置圆形的位置和大小

            //绘制星型
            graphics2D.setColor(Color.BLUE);//设置填充色,蓝色

            //设置星型的属性
            int radius = 10;//设置星型的大小
            int[] xPoints = new int[10];//星型顶点的X坐标
            int[] yPoints = new int[10];//星型顶点的Y坐标

            //计算星型顶点的坐标及初始角度
            for (int i = 0; i < 10; i++) {
                double baseAngle = i * 2 * Math.PI / 10 + Math.PI / 2;//初始角度
                if (i % 2 == 0) {//外围顶点的坐标
                    xPoints[i] = starX + (int) (radius * Math.cos(baseAngle));
                    yPoints[i] = starY - (int) (radius * Math.sin(baseAngle));
                } else {//内部顶点的坐标
                    xPoints[i] = starX + (int) (radius * Math.cos(baseAngle) * 0.5);
                    yPoints[i] = starY - (int) (radius * Math.sin(baseAngle) * 0.5);
                }
            }
            Polygon star = new Polygon(xPoints, yPoints, 10);//绘制星型
            graphics2D.fillPolygon(star);//填充星型
        }
    }

    //创建运行线程
    class AnimationRun extends Thread{

        boolean flag = false;//设置挂起标志

        synchronized void pause(){//暂停方法
            flag = true;
        }
        synchronized void restart(){//重启方法
            notifyAll();
            flag = false;
        }

        //重写run
        @Override
        public void run(){

            //动画运行
            while (true) {

                //挂起区
                synchronized (this){
                    while (flag){
                        try {
                            wait();//等待挂起
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }

                //判断圆形运动方向
                if (circleX == 0) {
                    circleXDirection = 1;
                } else if (circleX == 350) {
                    circleXDirection = -1;
                }
                if (circleY == 0) {
                    circleYDirection = 1;
                } else if (circleY == 285) {
                    circleYDirection = -1;
                }

                //判断星型运动方向
                if (starX == 10){
                    starXDirection = 1;
                }else if (starX == 355){
                    starXDirection = -1;
                }
                if (starY == 10){
                    starYDirection = 1;
                }else if (starY == 290){
                    starYDirection = -1;
                }

                //设置绘图延迟
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //计算新的圆形位置和星型的位置
                circleX += circleXDirection;
                circleY += circleYDirection;
                starX += starXDirection;
                starY += starYDirection;

                //重绘绘图板
                drawCircleAndStar.repaint();
            }
        }
    }
    public static void main(String[] args) {
        new AnimationJFrame().setVisible(true);//启动程序
    }
}

http://www.kler.cn/a/487747.html

相关文章:

  • 软考信安18~网络安全测评技术与标准
  • web作业
  • C++ STL 中的 vector 总结
  • 【MySQL系列文章】Linux环境下安装部署MySQL
  • 有关Redis的相关概述
  • 【设计模式-2】23 种设计模式的分类和功能
  • 统一门户单点登入(C#-OOS机制)
  • 物联网:七天构建一个闭环的物联网DEMO-MQTT的配置
  • MySQL核心揭秘:InnoDB存储引擎高级特性
  • 从MySQL5.7平滑升级到MySQL8.0的最佳实践分享
  • webrtc之rtc::ArrayView<const uint8_t>
  • QtCreator快捷键失效的解决办法
  • 大语言模型兵马未动,数据准备粮草先行
  • 03.01、三合一
  • C#实现凸壳算法
  • krpano 实现文字热点中的三角形和竖杆
  • LabVIEW数据库管理系统
  • php 使用simplexml_load_string转换xml数据格式失败
  • NineData云原生智能数据管理平台新功能发布|2024年12月版
  • 基于vue框架的的校园生活服务平台8vwac(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • QT 端口扫描附加功能实现 端口扫描5
  • 新活动平台建设历程与架构演进
  • C#,图论与图算法,任意一对节点之间最短距离的弗洛伊德·沃肖尔(Floyd Warshall)算法与源程序
  • 【Redis源码】 RedisObject结构体
  • 基于Springboot科研工作量管理系统【附源码】
  • MySQL innodb中一条sql的执行流程