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

server/2025/1/12 15:17:53/

文章目录

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

说明

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

代码详解

创建窗体代码

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是线程不安全的,应该对窗体单独建立窗体方法容器,例如:

java"> public AnimationJFrame() {SwingUtilities.invokeLater(() -> {initUI();});}private void initUI() {//代码块...........}

创建绘图板

java">    //创建绘图板class DrawCircleAndStar extends JLabel{@Overridepublic void paintComponent(Graphics g){//重写paintComponentsuper.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);//填充星型}}

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

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

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

创建线程

java">    //创建运行线程class AnimationRun extends Thread{boolean flag = false;//设置挂起标志synchronized void pause(){//暂停方法flag = true;}synchronized void restart(){//重启方法notifyAll();flag = false;}//重写run@Overridepublic 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

运行结果

在这里插入图片描述

完整代码

java">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{@Overridepublic void paintComponent(Graphics g){//重写paintComponentsuper.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@Overridepublic 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.ppmy.cn/server/157791.html

相关文章

前端组件开发:组件开发 / 定义配置 / 配置驱动开发 / 爬虫配置 / 组件V2.0 / form表单 / table表单

一、最早的灵感 最早的灵感来自sprider / 网络爬虫 / 爬虫配置&#xff0c;在爬虫爬取网站文章时候&#xff0c;会输入给爬虫一个配置文件&#xff0c;里边的内容是一个json对象。里边包含了所有想要抓取的页面的信息。爬虫通过这个配置就可以抓取目标网站的数据。其实本文要引…

【Uniapp-Vue3】表单focus和blue事件的用法

focus就是input框聚焦时触发&#xff0c;blue就是input框失去焦点时触发。 分别用focus和blue触发事件。 下面这个例子中&#xff0c;就用focus和blur来改变Image的class样式。 触发函数时可以设置参数来获取输入框的值&#xff1a; 获取输入框的值就是e.detail.value 该案…

axios的替代方案onion-middleware

onion-middleware的由来 嗯。。。闲来无事瞎搞的&#xff01;&#xff01;&#xff01;&#xff01;主要用来实现请求/相应拦截&#xff0c;当然队列性的数据操作都是可以的 直接上使用教程 安装 npm install onion-middleware使用 import { OnionMiddleware } from onion…

MySQL 14 章——视图

一、常见的数据库对象 常见的数据库对象&#xff1a; 对象描述表(TABLE)表是存储数据的逻辑单元&#xff0c;以行和列的形式存在&#xff0c;列就是字段&#xff0c;行就是记录数据字典就是系统表&#xff0c;存放数据库相关信息的表。系统表的数据通常由数据库系统维护&#…

uniapp 使用 pinia 状态持久化

1.创建文件 stores -index.js -global.js2.对应文件内容 index.js 安装插件 npm i pinia-plugin-persistedstate import { createPinia } from pinia; import persist from pinia-plugin-persistedstate; const pinia createPinia(); pinia.use(persist); export default pi…

TR-069协议学习--Soap报文、事件、RPC方法

目录 一、SOAP报文 二、事件 三、RPC方法 四、错误码 一、SOAP报文 SOAP&#xff08;Simple Object Access Protocol&#xff0c;简单对象访问协议&#xff09;是一种基于XML的协议&#xff0c;用于在网络上交换结构化信息。SOAP报文是SOAP协议中用于传输数据的XML文档…

C++ 多线程异步操作

C 多线程异步操作 文章目录 C 多线程异步操作std::future主要功能&#xff1a;如何使用 std::future1. 使用 std::async 和 std::future2. 使用 std::promise 和 std::future std::future 的常用方法注意事项使用模板函数与 std::async 结合 std::packaged_task主要特点工作原理…

SQLite 命令

关于《SQLite 命令》的文章&#xff0c;我可以为您提供一个概要。SQLite是一个轻量级的嵌入式关系数据库管理系统&#xff0c;它以单个文件的形式存储数据&#xff0c;非常适合用于不需要传统数据库服务器的场景。SQLite3的命令行工具&#xff08;sqlite3.exe&#xff09;是一个…