在摆动中循环时,油漆方法不起作用

时间:2014-11-18 22:54:05

标签: java swing jpanel paint thread-sleep

我有两节课。我试着移动圈子。当我在Game.java中键入break语句时,它会为我绘制一个没有移动的圆圈。没关系。但是,当我想移动圆圈时(通过删除break它什么也没有显示。我不知道如何调试它。我发现当我在没有break;语句的情况下使用时,程序不会进入paint方法。<登记/> 任何人都可以告诉我如何调试这样的问题。提前致谢

App.java

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class App extends JFrame {

    App() {

        JFrame jf = new JFrame(); 
        JPanel jp = new JPanel();
        jf.add(jp);
        jp.setLayout(new BorderLayout()); 
        jf.setSize(500, 500);
        jf.setTitle("Chain Reactor Game");
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);

        Game game = new Game();
        jp.add(game, BorderLayout.CENTER);

        MenuBar menuBar = new MenuBar();
        jf.setJMenuBar(menuBar.createMenuBar());
    }

    public static void main(String[] args) {

        /*
         * oracle recommendation to run swing applications in special thread    
         * they suggest it during thread implementation
         */

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new App();          
            }
        });
    }
}

Game.java

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JPanel;

public class Game extends JPanel {

    private static final long serialVersionUID = 1L;
    int x = 0;
    int y = 0;

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        System.out.println("im goint to filloval now");
        g2d.fillOval(x, y, 30, 30);
    }
    public Game() {

        while(true) {

            moveBall();
            repaint();
            try {
                System.out.println("inside thread");
                Thread.sleep(2000);
            } 
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        break;     // if i delete this line it not drawing. Why is that ?

        }
    }
    private void moveBall() {
        x = x + 1;
        y = y + 1;
    }
}

3 个答案:

答案 0 :(得分:4)

你需要在Swing事件线程中循环,因为它会阻止Swing执行其绘图功能或用户交互。你基本上是踩着Swing事件线程,违反了Swing并发规则。使用Swing Timer代替为这类事物构建的简单动画。

答案 1 :(得分:4)

让我们打破代码...

程序从这里开始......

public static void main(String[] args) {

    /*
     * oracle recommendation to run swing applications in special thread    
     * they suggest it during thread implementation
     */

    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            new App();          
        }
    });
}

这很好,它在事件调度线程的上下文中启动GUI,如Initial Threads ...

中所述

接下来,构建App ......

App() {

    JFrame jf = new JFrame(); 
    JPanel jp = new JPanel();
    jf.add(jp);
    jp.setLayout(new BorderLayout()); 
    jf.setSize(500, 500);
    jf.setTitle("Chain Reactor Game");
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jf.setVisible(true);

    Game game = new Game();
    jp.add(game, BorderLayout.CENTER);

    MenuBar menuBar = new MenuBar();
    jf.setJMenuBar(menuBar.createMenuBar());
}

好的,这似乎“好像”,但如果我们仔细看看Game

public Game() {

    while(true) {

        moveBall();
        repaint();
        try {
            System.out.println("inside thread");
            Thread.sleep(2000);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    break;     // if i delete this line it not drawing. Why is that ?

    }
}

啊,这是一个问题...... while已经在事件调度线程的上下文中执行,EDT用于处理事件并帮助安排重新绘制,任何阻止EDT的东西都会阻止它除其他外,绘制UI

首先看一下Concurrency in Swing

现在,你有一个catch 22的情况,Swing是一个单线程框架并且不是线程安全的,这意味着你可以做长时间运行/阻塞进程,但你也无法从事件外部更新UI调度线程。

在您的情况下,一个简单的解决方案是将while-loop替换为javax.swing.Timer

public Game() {
    javax.swing.Timer timer = new javax.swing.Timer(2000, new javax.swing.ActionListener() {
        public void actionPerformed(javax.swing.ActionEvent evt) {
            moveBall();
            repaint();
        }
    });
    timer.start();
}

请查看How to use Swing Timers了解更多详情......

作为旁注,通常建议使用paintComponentpaint执行自定义绘画。绘画是一系列复杂的方法调用,你可以做的任何事情都可以防止它被搞砸......

有关详细信息,请查看Painting in AWT and SwingPerforming Custom Painting

答案 2 :(得分:2)

当你离开break;时,你有一个无限循环。它永远不会结束,这意味着行Game game = new Game();将永远不会返回,这意味着将Game添加到JPanel的下一行将永远不会运行,这就是为什么没有绘制任何内容的原因屏幕。要使其工作,您需要创建GUI,然后执行绘图。

让这个工作的一种方法是创建一个新的Thread来执行连续绘图:

public Game() {
    (new Thread() {
        public void run() {
            draw();
        }
     ).start();
 }

private void draw() {
    while(true) {
        moveBall();
        repaint();
        try {
            System.out.println("inside thread");
            Thread.sleep(2000);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

或者,由于您在Swing线程上设置GUI,您可以在主线程上运行绘图。