使用JFrame和JPanel的简单Java动画

时间:2014-12-14 22:48:33

标签: java multithreading swing animation

好的,所以程序的目的是绘制椭圆形并在屏幕上移动它。代码在Eclipse上编译时没有错误,但是在运行时,不会在屏幕上绘制或移动椭圆。我一直在研究,似乎线程必须做很多事情,但是我需要一个这个简单的程序吗?我显然是使用Swing进行GUI编程的新手,所以我很感激解释或链接到一个关于线程或相关概念的程序的任何添加。

public class Game extends JPanel
{
    int x =0;
    int y =0;

    private void moveBall()
    {

        x+=1;
        y+=1;
    }

    public void paint (Graphics g)
    {
        super.paint(g);
        g.fillOval(x, y, 30, 30);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Animation");
        Game game = new Game();
        frame.add(game);
        frame.setVisible(true);
        frame.setSize(300,400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        while (true)
        {
            game.moveBall();
            game.repaint();

        }
    }
}

2 个答案:

答案 0 :(得分:5)

可能的问题是,该线程对UI的运行速度太快,在“球”离开可见区域后,UI显示得很好。

你需要做几件事......

首先,您需要确保在事件调度线程中正确安排更新,其次,更新之间存在短暂延迟。例如,25fps在更新之间延迟大约40毫秒,60fps大约是16毫秒

有很多方法可以实现这一点,具体取决于您希望实现的目标,例如,您可以简单地使用Thread.sleep使线程在更新之间暂停一小段时间。这个问题是Swing不是线程安全的,UI的所有更新都应该在Event Dispatching Thread的上下文中进行。

虽然编程很简单,但在更新状态时可能会运行一个绘制周期,导致更新很脏。

另一种解决方案可能是使用Swing Timer,它允许您定期更新在事件调度线程的上下文中触发的更新,使其更安全。

有关详细信息,请查看Concurrency in SwingHow to use Swing Timers

作为一个例子......

Moving Ball

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class BallAnimation {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new BallAnimation();
    }

    public BallAnimation() {
        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("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private int x = 0;
        private int y = 0;

        public TestPane() {
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    moveBall();
                    repaint();
                }
            });
            timer.start();
        }

        protected void moveBall() {
            x++;
            y++;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.RED);
            g2d.fillOval(x, y, 30, 30);
            g2d.dispose();
        }

    }

}

作为旁注,除非您确实有理由这样做,否则应避免覆盖paint,而应使用paintComponent

答案 1 :(得分:-1)

尝试使用sleep作为适合代码的最简单方法。 main实际上是一个线程。并且JFrame创建了自己的主题。

while (true)
{
   game.moveBall();
   game.repaint();
   try { Thread.sleep(50); } catch (Exception e){}
}

我刚刚意识到,你不要用默认颜色绘制整个屏幕。

将您的绘画方法更改为此

public void paint (Graphics g)
{
    super.paint(g);
    g.setColor(Color.white); //default color
    g.fillRect(0, 0, getWidth(), getHeight()); // fill whole canvas
    g.setColor(Color.black); //change color
    g.fillOval(x, y, 30, 30); // draw oval
}