【问题标题】:Repaint a JPanel重绘 JPanel
【发布时间】:2012-02-01 10:36:31
【问题描述】:

我想重新粉刷我的 jPanel 我有一个 App 类来处理这个 JFrame 中显示的屏幕我添加了一个处理所有 JPanel 的 Board JPanel 对象。 在董事会课上我有

ex = new Explosion(10, 10); 
new Thread(ex).start();

在我的 Explosion 类中,我有一个构造函数来裁剪我的 sprite 文件,覆盖 paint() 和:

public void run()
    {
        while(cursor < (rows*cols))
        {
            repaint();

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(cursor);

            cursor++;
        }
    }

循环工作正常,但我的屏幕上没有重绘,只有第一张图像显示。

我该怎么做才能刷新?

谢谢

编辑: 这是我的代码:

public class Explosion extends JPanel implements Runnable{

private BufferedImage img;

final int width = 320;
final int height = 320;
final int rows = 5;
final int cols = 5;

private int x,y;

private int cursor;

BufferedImage[] sprites = new BufferedImage[rows * cols];

public Explosion(int x, int y)
{
    this.x = x;
    this.y = y;

    try {
        try {
            this.img = ImageIO.read(new File((this.getClass().getResource("files/explosion2.png")).toURI()));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    cursor = 0;

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            sprites[(i * cols) + j] = img.getSubimage(
                i * (width/rows),
                j * (height/cols),
                width/rows,
                height/cols
            );
        }
    }
}

public void run()
{
    ActionListener taskPerformer = new ActionListener() {
          public void actionPerformed(ActionEvent evt) {
              cursor++;
              repaint();
          }
      };

    while(cursor < (rows*cols))
    {           
          new Timer(50, taskPerformer).start();


        if (cursor==(rows*cols)-1)
            cursor=0;
    }
}

public void paintComponent(Graphics g){
    System.out.println("paintComponent");
    Graphics2D g2d = (Graphics2D)g;
    g2d.drawImage(sprites[cursor], x, y, this);
    g.dispose();    
}

/*public void paint(Graphics g) {
        super.paint(g);

        System.out.println("paint");

        Graphics2D g2d = (Graphics2D)g;
        g2d.drawImage(sprites[cursor], x, y, this);
        Toolkit.getDefaultToolkit().sync();
        g.dispose();        
}*/


}
public class Main extends JFrame{

    public Main() 
    {
        Board board = new Board();
        add(board);
        setTitle("Explosion");
        setSize(500, 500);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);  
    }

    public static void main(String[] args) {
        new Main();
    }

}
public class Board extends JPanel{

    Explosion ex;

    public Board() 
    {           
        setDoubleBuffered(true); 

        ex = new Explosion(10,10);
        new Thread(ex).start();
    }

    public void paint(Graphics g) {
        ex.paintComponent(g);

        super.paint(g);
        Graphics2D g2d = (Graphics2D)g;
        Toolkit.getDefaultToolkit().sync();
        g.dispose();
    }
}

【问题讨论】:

  • 如需尽快获得更好的帮助,请发帖SSCCE。

标签: java swing jpanel repaint concurrency


【解决方案1】:

@StanislavL(我还不能发表评论):不,你不必在美国东部时间打电话给repaint():

以下 JComponent 方法可以安全地从任何线程调用: repaint()、revalidate() 和 invalidate()。 repaint() 和 revalidate() 方法排队请求事件调度线程分别调用 paint() 和 validate()。 invalidate() 方法只是将一个组件及其所有直接祖先标记为需要验证。

来源:http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#exceptions

cursor 是否声明为 volatile?如果没有,EDT 可能看不到线程对cursor 所做的更改。因此,总是绘制第一张图像。

【讨论】:

  • 即使这也应该是正确的,但不会被 Thread.sleep(int) 阻塞 EDT
  • 例如,当我调整屏幕大小时,图像重新绘制得很好。如果屏幕上没有任何事件,我的图像不会重新绘制。
  • 正如您问题的 cmets 中所述,您确实应该提供更多上下文。
【解决方案2】:
  1. 不要覆盖paint,而是覆盖paintComponent
  2. 不要使用Thread.sleep,而是使用实用程序类,例如javax.swing.Timer,以指定的时间间隔执行特定操作。这将确保所有更改都在 EDT 上完成,并且等待发生在后台线程中。

请注意,如果没有可编译的代码,就很难判断真正的问题是什么;这都是猜测。

【讨论】:

    【解决方案3】:

    必须在 EDT 中调用重绘。

    使用 SwingUtilities.invokeAndWait() 或 invokeLater() 调用 repaint。

    【讨论】:

    • 你的意思是我必须在我的 Explosion.run() 中添加类似SwingUtilities.invokeLater(new Runnable() { public void run() { repaint(); } });
    • -1,repaint不需要在Event Dispatch Thread中调用; RepaintManager 会为您处理这个问题。
    • @mre 这个论坛充满了线程安全方法在 java.util.*、>Future、Thread#sleep(int) 中不起作用的问题,请阅读 OP 的评论关于调整大小后重绘,这个答案默认是正确的
    猜你喜欢
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    • 2011-01-10
    • 2011-11-15
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多