java - 通过 InvokeLater 的多个动画 - Java

标签 java multithreading swing event-dispatch-thread jcomponent

我正在尝试制作一个 GUI,它本质上有多个弹跳球。这些球是通过 JButton 添加的。我能够成功地创建 Ball 类,并仅用一个球在屏幕上制作动画,但是我无法通过使用按钮添加多个球。我尝试制作一个 ActionListener 来创建新线程并调用 SwingUtilities.InvokeLater,但它只会使 GUI 卡住。我尝试按照本指南了解如何使用 InvokeLater:http://www.javamex.com/tutorials/threads/invokelater.shtml

这是到目前为止我的代码。任何帮助表示赞赏。 (我意识到之前已经在这里问过弹跳球问题,但我无法理解回复中解释的方法,并且认为我应该在这里问而不是评论一个2年前的问题)

球类

package BouncingBall;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Random;

public class Ball extends JComponent
{
    private double dx;
    private double dy;
    private double x;
    private double y;
    private Color color;
    private Random rand = new Random();
    private Ellipse2D.Double ball;
    private final static int diam = 10;
    private final static int xLim = BallFrame.FRAME_WIDTH-diam;
    private final static int yLim = BallFrame.FRAME_HEIGHT-diam*7;
    boolean xUpperBound = true;
    boolean yUpperBound = true;

    public Ball()
    {
        color = new Color(rand.nextFloat(),rand.nextFloat(),rand.nextFloat());
        x = rand.nextInt(xLim);
        y = rand.nextInt(yLim);
        dx = rand.nextInt(9)+1;
        dy = rand.nextInt(9)+1;
    }

    public void paintComponent(Graphics g)
    {
        Graphics2D g2 = (Graphics2D) g;
        draw(g2);
    }

    public void draw(Graphics2D g2)
    {
        ball = new Ellipse2D.Double(x,y,diam,diam);
        g2.setColor(color);
        g2.fill(ball);
    }

    public void move()
    {
        if (((x+dx) < xLim) && xUpperBound)
            x+=dx;
        else if (x > 0)
        {
            xUpperBound = false;
            x-=dx;
        }
        else
            xUpperBound = true;

        if (((y+dy) < yLim) && yUpperBound)
            y+=dy;
        else if (y > 0)
        {
            yUpperBound = false;
            y-=dy;
        }
        else
            yUpperBound = true;
    }

    public void animate()
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    while (true)
                    {
                        repaint();
                        move();
                        Thread.sleep(40);
                    }
                }
                catch (InterruptedException e)
                {
                    System.out.println("Thread was interrupted!");
                }
            }
        });
    }
}

BallFrame 类

package BouncingBall;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class BallFrame extends JFrame
{
    public final static int FRAME_WIDTH = 800;
    public final static int FRAME_HEIGHT = 600;

    public BallFrame()
    {
        class ballListener implements ActionListener
        {
            @Override
            public void actionPerformed(ActionEvent actionEvent)
            {
                Thread thread = new Thread()
                {
                    public void run()
                    {
                        Ball temp = new Ball();
                        temp.animate();
                        add(temp);
                    }
                };
                thread.start();
            }
        }

        setLayout(new BorderLayout());
        JButton addBall = new JButton("Add Ball");
        ActionListener listener = new ballListener();
        addBall.addActionListener(listener);
        add(addBall, BorderLayout.SOUTH);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                BallFrame frame = new BallFrame();
                frame.setSize(FRAME_WIDTH, FRAME_HEIGHT );
                frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                frame.setVisible( true );
            }
        });
    }
}

最佳答案

我在设计中已经遇到的一个问题是,你正在制作 Ball延伸JComponent这已经在很多方面限制了你。你应该只做 Ball一个简单的状态持有者/状态操纵类,具有动画和绘制球状态的方法。

然后只需一个 JComponent/JPanel 类来绘制球。在该类(class)中,您可以有 List<Ball>您将在 paintComponent 中绘制方法。每当你想添加一个球时,只需添加 Ball到列表中。

此外,您应该使用 javax.swing.Timer ,而不是使用单独的线程,这不应该通过绘画来完成(因为所有绘画都应该在 EDT 上完成)。用于计时动画。您可以在How to Use Swing Timers查看更多信息

您还可以看到上述方法的一堆示例 herehereherehereherehere .

<小时/>

更新

"Can you explain extending JComponent limits it? As for not using threads. How would I have multiple Ball objects animate independently? Doesn't that require multiple threads?"

  1. 您必须将多个组件添加到一个可见的“绘画表面”。因此基本上您必须将这些组件分层到表面,必须处理组件不透明度等问题。
  2. 您使用 javax.swing.Timer,在其中循环遍历 List 并调用每个球的 animate() 方法。
  3. 不。

请参阅我发布的链接中的示例。他们有使用计时器和对象列表的示例

这是一些代码片段的基本思想

球类

public class Ball {
    public void draw(Graphics g) {}
    public void animate() {}
}

BallPanel 类

public class BallPanel extends JPanel {
    List<Ball> balls;

    public BallPanel() {
        Timer timer = new Timer(40, new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                for (Ball ball : balls) {
                    ball.animate();
                }
                repaint();
            }
        });
        timer.start();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (Ball ball : ball ) {
            ball.draw(g);
        }
    }

    public void addBall(...) {
        balls.add(new Ball(..));
    }
}

关于java - 通过 InvokeLater 的多个动画 - Java,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23798441/

相关文章:

java - 将 List<Subclass> 传递给需要 List<SuperClass> 的方法

c# - Web请求突然停止工作

java - Thread、Arraylist对内存的影响 - Java

java - JDatePicker 和属性不起作用

java - 什么情况下JVM会停止打印gc.log?

java - Spring 批处理 : SimpleJobRepository - Example not working

java - ORMLite 存储所有类字段注释

iOS AVFoundation 和多线程

Java - 使用 JScrollPane

java - Applet 中的 CardLayout 未显示