Swing repaint()在循环或线程中不起作用

sid*_*ney 1 java graphics animation swing multithreading

我有以下代码:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Random;

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

public class ex10 extends JPanel  {
    private int x=1;
    int y=1;

    //Constructor 
    public ex10() {


        while(true) {

          System.out.println("x ->"+ x);
          System.out.println("y ->" + y);


          x = randomposition(x);
          y = randomposition(y);

          this.repaint();
        }
    }

  public int randomposition(int value) {
        Random random = new Random();

    if (random.nextBoolean() == true) {
      if (value+1 != 500) {
        value++;
      }
    }
    else {
      if (value-1 != 0) {
        value--;  
      }
    }
    return value;
  }
    @Override
    public void paintComponent(Graphics g) {
        //super.paintComponent(g);
        g.setColor(Color.green);
    g.fillRect(x, y, 20, 20);
    }


    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.add(new ex10());
    }

}
Run Code Online (Sandbox Code Playgroud)

不幸的是,当this.repaint()被调用时,该点未被显示,但我仍然得到了System.out.println.我尝试分开设置一个新线程,但无济于事.我尝试了一些其他的解决方案(invokelater,和paintimmediately),也无济于事.

我的目标是设置一个在屏幕上徘徊的绿点.你有什么解决方案吗?

Hov*_*els 8

while (true)正在阻止将应用程序置于休眠状态的Swing事件线程.

对于简单的动画和游戏循环,请使用Swing Timer.如果你有长时间运行的代码需要在后台,那么使用后台线程,如SwingWorker,但要注意确保所有调用Swing组件状态的调用应该在Swing事件线程上完成.

例如,你可以改变这个:

    while(true) {

      System.out.println("x ->"+ x);
      System.out.println("y ->" + y);


      x = randomposition(x);
      y = randomposition(y);

      this.repaint();
    }
Run Code Online (Sandbox Code Playgroud)

使用Swing Timer(javax.swing.Timer):

int timerDelay = 20;
new Timer(timerDelay, new ActionListener(){
  public void actionPerformed(ActionEvent e) {
    x = randomposition(x);
    y = randomposition(y);
    repaint();
  }
}).start();
Run Code Online (Sandbox Code Playgroud)

关于DSquare的评论:

  • 事实上,你并没有在Swing事件线程上运行你的GUI,你应该做的事情,然而你的while循环仍然会冻结你的绘画,因为你的无限循环阻止了组件完全自己创建.
  • 如上所述,实际上你应该在Swing事件线程上启动所有Swing GUI,你可以通过将Swing创建代码放入Runnable并通过SwingUtilities方法invokeLater在事件线程上排队Runnable来完成.
  • 您需要在paintComponent覆盖中调用super的paintComponent方法,以便JPanel可以执行其内务处理图形工作,包括清除"脏"像素.

例如,改变这个:

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(500, 500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    frame.add(new ex10());
}
Run Code Online (Sandbox Code Playgroud)

对此:

public static void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable() {
     public void run() {
        JFrame frame = new JFrame();
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.add(new Ex10());
     }
  });
}
Run Code Online (Sandbox Code Playgroud)

并改变这个:

@Override
public void paintComponent(Graphics g) {
  //super.paintComponent(g);
  g.setColor(Color.green);
  g.fillRect(x, y, 20, 20);
}
Run Code Online (Sandbox Code Playgroud)

对此:

@Override
public void paintComponent(Graphics g) {
  super.paintComponent(g);
  g.setColor(Color.green);
  g.fillRect(x, y, 20, 20);
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然这一般是正确和好的建议,但EDT被阻止并不是它在这种情况下失败的原因,因为他没有从EDT调用`ex10()`(这本身就是一个错误).它不起作用,因为构造函数进入循环,所以这行`frame.add(new ex10());`永远不会完全执行.EDT很好,并执行重绘.他还必须取消注释`super.paintComponent(g);` (2认同)
  • @sidney:不客气.但请参阅重要的编辑来回答. (2认同)