如何在JPanel中为Rectangle设置动画?

我想为我的项目学习一些关于JAVA的技巧。

我想动画我的Rectangle leftoright和righttoleft,但我不能为球动画应用相同的function。

另外,如何在不同的x方向上以y坐标的边界开始我的球?

非常感谢您的建议和帮助。

在此处输入图像描述

我的代码:

import javax.swing.Timer; import java.util.ArrayList; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class MultipleBall extends JApplet { public MultipleBall() { add(new BallControl()); } class BallControl extends JPanel { private BallPanel ballPanel = new BallPanel(); private JButton Suspend = new JButton("Suspend"); private JButton Resume = new JButton("Resume"); private JButton Add = new JButton("+1"); private JButton Subtract = new JButton("-1"); private JScrollBar Delay = new JScrollBar(); public BallControl() { // Group buttons in a panel JPanel panel = new JPanel(); panel.add(Suspend); panel.add(Resume); panel.add(Add); panel.add(Subtract); // Add ball and buttons to the panel ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red)); Delay.setOrientation(JScrollBar.HORIZONTAL); ballPanel.setDelay(Delay.getMaximum()); setLayout(new BorderLayout()); add(Delay, BorderLayout.NORTH); add(ballPanel, BorderLayout.CENTER); add(panel, BorderLayout.SOUTH); // Register listeners Suspend.addActionListener(new Listener()); Resume.addActionListener(new Listener()); Add.addActionListener(new Listener()); Subtract.addActionListener(new Listener()); Delay.addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { ballPanel.setDelay(Delay.getMaximum() - e.getValue()); } }); } class Listener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource() == Suspend) ballPanel.suspend(); else if (e.getSource() == Resume) ballPanel.resume(); else if (e.getSource() == Add) ballPanel.add(); else if (e.getSource() == Subtract) ballPanel.subtract(); } } } class BallPanel extends JPanel { private int delay = 30; private ArrayList list = new ArrayList(); // Create a timer with the initial delay protected Timer timer = new Timer(delay, new ActionListener() { /** Handle the action event */ public void actionPerformed(ActionEvent e) { repaint(); } }); public BallPanel() { timer.start(); } public void add() { list.add(new Ball()); } public void subtract() { if (list.size() > 0) list.remove(list.size() - 1); // Remove the last ball } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawRect(185, 279, 50, 15); g.setColor(Color.RED); g.fillRect(185, 279, 50, 15); for (int i = 0; i < list.size(); i++) { Ball ball = (Ball) list.get(i); // Get a ball g.setColor(ball.color); // Set ball color // Check boundaries if (ball.x  getWidth()) ball.dx = -ball.dx; if (ball.y  getHeight()) ball.dy = -ball.dy; // Adjust ball position ball.x += ball.dx; // ball.y += ball.dy; g.fillOval(ball.x - ball.radius, ball.y - ball.radius, ball.radius * 2, ball.radius * 2); } } public void suspend() { timer.stop(); } public void resume() { timer.start(); } public void setDelay(int delay) { this.delay = delay; timer.setDelay(delay); } } class Ball { int x = 20; int y = 20; // Current ball position int dx = 2; // Increment on ball's x-coordinate int dy = 2; // Increment on ball's y-coordinate int radius = 15; // Ball radius Color color = new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)); } /** Main method */ public static void main(String[] args) { JFrame frame = new JFrame(); JApplet applet = new MultipleBallApp(); frame.add(applet); frame.setTitle("MultipleBallApp"); frame.setLocationRelativeTo(null); // Center the frame frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.setLocationRelativeTo(null); // Center the frame frame.setVisible(true); } } 

不能对球动画应用相同的function

这可能是你的第一个错误。 事实上,这正是你应该尝试做的事情。 这个想法是,你应该尝试通过绘制/动画是抽象的方法设计一种方法,这样你要绘制的形状无关紧要,你可以将它应用到sam基本动画过程……

例如,您可以从描述动画实体的基本属性的某种interface开始…

 public interface AnimatedShape { public void update(Rectangle bounds); public void paint(JComponent parent, Graphics2D g2d); } 

这表示动画实体可以更新(移动)和绘制。 按照惯例(因为我很懒),我喜欢创建一个实现最常见方面的abstract实现…

 public abstract class AbstractAnimatedShape implements AnimatedShape { private Rectangle bounds; private int dx, dy; public AbstractAnimatedShape() { } public void setBounds(Rectangle bounds) { this.bounds = bounds; } public Rectangle getBounds() { return bounds; } public int getDx() { return dx; } public int getDy() { return dy; } public void setDx(int dx) { this.dx = dx; } public void setDy(int dy) { this.dy = dy; } @Override public void update(Rectangle parentBounds) { Rectangle bounds = getBounds(); int dx = getDx(); int dy = getDy(); bounds.x += dx; bounds.y += dy; if (bounds.x < parentBounds.x) { bounds.x = parentBounds.x; setDx(dx *= -1); } else if (bounds.x + bounds.width > parentBounds.x + parentBounds.width) { bounds.x = parentBounds.x + (parentBounds.width - bounds.width); setDx(dx *= -1); } if (bounds.y < parentBounds.y) { bounds.y = parentBounds.y; setDy(dy *= -1); } else if (bounds.y + bounds.height > parentBounds.y + parentBounds.height) { bounds.y = parentBounds.y + (parentBounds.height - bounds.height); setDy(dy *= -1); } } } 

然后开始创建实现……

 public class AnimatedBall extends AbstractAnimatedShape { private Color color; public AnimatedBall(int x, int y, int radius, Color color) { setBounds(new Rectangle(x, y, radius * 2, radius * 2)); this.color = color; setDx(Math.random() > 0.5 ? 2 : -2); setDy(Math.random() > 0.5 ? 2 : -2); } public Color getColor() { return color; } @Override public void paint(JComponent parent, Graphics2D g2d) { Rectangle bounds = getBounds(); g2d.setColor(getColor()); g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height); } } 

通过这种方式,您可以自定义实体的动画和绘制方式,但实体的每个实例的基本逻辑都是相同的……

但这有什么意义呢……

基本上,它允许我们做的是生成所有动画对象的“虚拟”概念并简化管理,例如……

我们可以使用松散耦合的List来代替使用“紧密”耦合的List

 private ArrayList list = new ArrayList(); 

然后,当我们想要更新实体时,我们只需要迭代List并要求实体更新……

 protected Timer timer = new Timer(delay, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { for (AnimatedShape ball : list) { ball.update(getBounds()); } repaint(); } }); 

当他们需要画画时……

 @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; for (AnimatedShape ball : list) { ball.paint(this, g2d); } } 

因为BallPane并不关心它实际上是什么类型的实体,而只是它是一种AnimatedShape …让生活更轻松……

现在,我对AnimatedBall实现已经随机化了球的每个实例的方向,但是当使用类似的东西添加球时,你也可以随机化起始位置。

 public void add() { int radius = 15; // Randomised position int x = (int)(Math.random() * (getWidth() - (radius * 2))) + radius; int y = (int)(Math.random() * (getHeight() - (radius * 2))) + radius; Color color = new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)); AnimatedBall ball = new AnimatedBall(x, y, radius, color); list.add(ball); } 

但是,这如何帮助您添加矩形?

您现在需要创建一个从AbstractAnimatedShape扩展的AnimatedRectangle ,并实现所需的方法,并将其实例添加到BallPaneAnimatedShape List中。

如果您不希望在同一列表中管理矩形,您可以创建另一个列表并单独管理它(它创建另外两个方法, update(List)paint(List, Graphics2D)传递在每个单独的列表中,以减少重复的代码,但那是我)…

例如,您可以通过覆盖setDy方法并忽略任何更改来限制矩形垂直移动

例

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class MultipleBall { public MultipleBall() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("MultipleBallApp"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new BallControl()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class BallControl extends JPanel { private BallPanel ballPanel = new BallPanel(); private JButton Suspend = new JButton("Suspend"); private JButton Resume = new JButton("Resume"); private JButton Add = new JButton("+1"); private JButton Subtract = new JButton("-1"); private JScrollBar Delay = new JScrollBar(); public BallControl() { // Group buttons in a panel JPanel panel = new JPanel(); panel.add(Suspend); panel.add(Resume); panel.add(Add); panel.add(Subtract); // Add ball and buttons to the panel ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red)); Delay.setOrientation(JScrollBar.HORIZONTAL); ballPanel.setDelay(Delay.getMaximum()); setLayout(new BorderLayout()); add(Delay, BorderLayout.NORTH); add(ballPanel, BorderLayout.CENTER); add(panel, BorderLayout.SOUTH); // Register listeners Suspend.addActionListener(new Listener()); Resume.addActionListener(new Listener()); Add.addActionListener(new Listener()); Subtract.addActionListener(new Listener()); Delay.addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { ballPanel.setDelay(Delay.getMaximum() - e.getValue()); } }); } class Listener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource() == Suspend) { ballPanel.suspend(); } else if (e.getSource() == Resume) { ballPanel.resume(); } else if (e.getSource() == Add) { ballPanel.add(); } else if (e.getSource() == Subtract) { ballPanel.subtract(); } } } } class BallPanel extends JPanel { private int delay = 30; private ArrayList list = new ArrayList(); private AnimatedRectange rectangle; public BallPanel() { this.rectangle = new AnimatedRectange(-25, 200, 50, 25, Color.RED); timer.start(); } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } // Create a timer with the initial delay protected Timer timer = new Timer(delay, new ActionListener() { /** * Handle the action event */ @Override public void actionPerformed(ActionEvent e) { for (AnimatedShape ball : list) { ball.update(getBounds()); } rectangle.update(getBounds()); repaint(); } }); public void add() { int radius = 15; // Randomised position int x = (int) (Math.random() * (getWidth() - (radius * 2))) + radius; int y = (int) (Math.random() * (getHeight() - (radius * 2))) + radius; Color color = new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)); AnimatedBall ball = new AnimatedBall(x, y, radius, color); list.add(ball); } public void subtract() { if (list.size() > 0) { list.remove(list.size() - 1); // Remove the last ball } } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; for (AnimatedShape ball : list) { ball.paint(this, g2d); } rectangle.paint(this, g2d); } public void suspend() { timer.stop(); } public void resume() { timer.start(); } public void setDelay(int delay) { this.delay = delay; timer.setDelay(delay); } } public interface AnimatedShape { public void update(Rectangle bounds); public void paint(JComponent parent, Graphics2D g2d); } public abstract class AbstractAnimatedShape implements AnimatedShape { private Rectangle bounds; private int dx, dy; public AbstractAnimatedShape() { } public void setBounds(Rectangle bounds) { this.bounds = bounds; } public Rectangle getBounds() { return bounds; } public int getDx() { return dx; } public int getDy() { return dy; } public void setDx(int dx) { this.dx = dx; } public void setDy(int dy) { this.dy = dy; } @Override public void update(Rectangle parentBounds) { Rectangle bounds = getBounds(); int dx = getDx(); int dy = getDy(); bounds.x += dx; bounds.y += dy; if (bounds.x < parentBounds.x) { bounds.x = parentBounds.x; setDx(dx *= -1); } else if (bounds.x + bounds.width > parentBounds.x + parentBounds.width) { bounds.x = parentBounds.x + (parentBounds.width - bounds.width); setDx(dx *= -1); } if (bounds.y < parentBounds.y) { bounds.y = parentBounds.y; setDy(dy *= -1); } else if (bounds.y + bounds.height > parentBounds.y + parentBounds.height) { bounds.y = parentBounds.y + (parentBounds.height - bounds.height); setDy(dy *= -1); } } } public class AnimatedBall extends AbstractAnimatedShape { private Color color; public AnimatedBall(int x, int y, int radius, Color color) { setBounds(new Rectangle(x, y, radius * 2, radius * 2)); this.color = color; setDx(Math.random() > 0.5 ? 2 : -2); setDy(Math.random() > 0.5 ? 2 : -2); } public Color getColor() { return color; } @Override public void paint(JComponent parent, Graphics2D g2d) { Rectangle bounds = getBounds(); g2d.setColor(getColor()); g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height); } } public class AnimatedRectange extends AbstractAnimatedShape { private Color color; public AnimatedRectange(int x, int y, int width, int height, Color color) { setBounds(new Rectangle(x, y, width, height)); this.color = color; setDx(2); } // Don't want to adjust the vertical speed @Override public void setDy(int dy) { } @Override public void paint(JComponent parent, Graphics2D g2d) { Rectangle bounds = getBounds(); g2d.setColor(color); g2d.fill(bounds); } } /** * Main method */ public static void main(String[] args) { new MultipleBall(); } } 

修订

  • 你真的应该避免将JApplet添加到JFrame ,applet有一个你忽略的规定的生命周期和管理过程。 最好只关注使用BallControl面板作为核心UI元素,然后将其添加到您想要的顶级容器中
  • 您可能会发现JSliderJScrollBar更加海盗,更不用说,它会在不同平台上看起来更好,大多数用户都了解滑块的用途……

添加一个像ballCount这样的静态变量,并在每次制作球时添加1。 在Ball类中,将y的定义更改为y = 20 + ballcount*(radius*2+distanceInBalls)

 public class RandomTests extends JApplet { public RandomTests() { add(new BallControl()); } static int ballCount = 0; class BallControl extends JPanel { private BallPanel ballPanel = new BallPanel(); private JButton Suspend = new JButton("Suspend"); private JButton Resume = new JButton("Resume"); private JButton Add = new JButton("+1"); private JButton Subtract = new JButton("-1"); private JScrollBar Delay = new JScrollBar(); public BallControl() { // Group buttons in a panel JPanel panel = new JPanel(); panel.add(Suspend); panel.add(Resume); panel.add(Add); panel.add(Subtract); // Add ball and buttons to the panel ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red)); Delay.setOrientation(JScrollBar.HORIZONTAL); ballPanel.setDelay(Delay.getMaximum()); setLayout(new BorderLayout()); add(Delay, BorderLayout.NORTH); add(ballPanel, BorderLayout.CENTER); add(panel, BorderLayout.SOUTH); // Register listeners Suspend.addActionListener(new Listener()); Resume.addActionListener(new Listener()); Add.addActionListener(new Listener()); Subtract.addActionListener(new Listener()); Delay.addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { ballPanel.setDelay(Delay.getMaximum() - e.getValue()); } }); } class Listener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource() == Suspend) ballPanel.suspend(); else if (e.getSource() == Resume) ballPanel.resume(); else if (e.getSource() == Add) ballPanel.add(); else if (e.getSource() == Subtract) ballPanel.subtract(); } } } class BallPanel extends JPanel { private int delay = 30; private ArrayList list = new ArrayList(); // Create a timer with the initial delay protected Timer timer = new Timer(delay, new ActionListener() { /** Handle the action event */ public void actionPerformed(ActionEvent e) { repaint(); } }); public BallPanel() { timer.start(); } public void add() { list.add(new Ball()); ballCount++; } public void subtract() { if (list.size() > 0) list.remove(list.size() - 1); // Remove the last ball } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawRect(185, 279, 50, 15); g.setColor(Color.RED); g.fillRect(185, 279, 50, 15); for (int i = 0; i < list.size(); i++) { Ball ball = (Ball) list.get(i); // Get a ball g.setColor(ball.color); // Set ball color // Check boundaries if (ball.x < 0 || ball.x > getWidth()) ball.dx = -ball.dx; if (ball.y < 0 || ball.y > getHeight()) ball.dy = -ball.dy; // Adjust ball position ball.x += ball.dx; // ball.y += ball.dy; g.fillOval(ball.x - ball.radius, ball.y - ball.radius, ball.radius * 2, ball.radius * 2); } } public void suspend() { timer.stop(); } public void resume() { timer.start(); } public void setDelay(int delay) { this.delay = delay; timer.setDelay(delay); } } class Ball { int radius = 15; // Ball radius int x = radius; int y = 20 + (radius * ballCount * 2 + 15); // Current ball position int dx = 2; // Increment on ball's x-coordinate int dy = 2; // Increment on ball's y-coordinate Color color = new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)); } public static void main(String[] args) { JFrame frame = new JFrame(); JApplet applet = new RandomTests(); frame.add(applet); frame.setTitle("MultipleBallApp"); frame.setLocationRelativeTo(null); // Center the frame frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.setLocationRelativeTo(null); // Center the frame frame.setVisible(true); } }