Java Canvas在我调整JFrame大小时,画布停止绘制

时间:2017-01-10 18:09:02

标签: java swing canvas jframe

更详细地说,我有一个附加到JFrame的componentResized事件(它包含画布,没有别的),并且在那个事件中调用一个方法来相应地设置画布的边界。这很好,除了我调整画布大小时,它没有任何显示。我只看到JFrame的背面。一旦我停止调整JFrame的大小,画布就会重新打印。

public class MyCanvas implements ComponentListener {

  public static void main(String[] args) {
    new MyCanvas("MyCanvas",new Dimension(300,300));
  }

  private static final int frameRate = 30;

  private JFrame frame;
  private JPanel panel;
  private Canvas canvas;
  private BufferStrategy strategy;
  private int delta;
  private boolean running = false;
  private int frameCount = 0;

  public MyCanvas(String name, Dimension size) {
    frame = new JFrame(name);
    panel = (JPanel) frame.getContentPane();
    canvas = new Canvas();
    frame.setSize(size);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel.setPreferredSize(size);
    panel.setLayout(null);
    canvas.setBounds(0,0,size.width,size.height);
    panel.add(canvas);
    canvas.setIgnoreRepaint(true);
    frame.setResizable(true);
    frame.pack();
    frame.addComponentListener(this);
    canvas.createBufferStrategy(2);
    strategy = canvas.getBufferStrategy();
    running = true;
    frame.setVisible(true);
    long lastLoopTime = 0;
    while (running) {
      frameCount++;
      delta = (int) (System.currentTimeMillis() - lastLoopTime);
      lastLoopTime = System.currentTimeMillis();
      Graphics2D graphics = (Graphics2D) strategy.getDrawGraphics();
      graphics.setColor(Color.black);
      graphics.fillRect(0,0,getSize().width,getSize().height);
      graphics.dispose();
      strategy.show();
      try {
        Thread.sleep(1000/frameRate);
      } catch (InterruptedException e) {}
    }
  }

  public final Dimension getSize() {
    return frame.getSize();
  }

  public final void setSize(Dimension size) {
    frame.setSize(size);
    canvas.setBounds(0,0,size.width,size.height);
  }

  public synchronized void componentResized(ComponentEvent e) {
    setSize(frame.getSize());
  }

  public synchronized void componentHidden(ComponentEvent e) {
    // unused
  }

  public synchronized void componentShown(ComponentEvent e) {
    // unused
  }

  public synchronized void componentMoved(ComponentEvent e) {
    // unused
  }

}

修改

经过一段时间的代码调整后,我想出了一个解决方案:

public class MyCanvas {

  public static void main(String[] args) {
    new MyCanvas("MyCanvas",new Dimension(400,400));
  }

  private static final int frameRate = 1000 / 30;

  private JFrame frame;
  private JPanel panel;
  private int delta;
  private long lastLoopTime;
  private boolean running = false;
  private int frameCount = 0;
  private BufferedImage backBuffer = null;
  private int lastPaintFrame = -1;

  public MyCanvas(String name, Dimension size) {
    frame = new JFrame(name);
    panel = new JPanel() {
      protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        redraw(g);
      }
    };
    frame.setContentPane(panel);
    frame.setSize(size);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel.setPreferredSize(size);
    panel.setLayout(null);
    frame.setResizable(true);
    running = true;
    frame.setVisible(true);
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    backBuffer = new BufferedImage(screenSize.width,screenSize.height,BufferedImage.TYPE_INT_ARGB);
    lastLoopTime = System.nanoTime();
    while (running) {
      long thisLoopTime = System.nanoTime();
      delta = (int) ((thisLoopTime - lastLoopTime) / 1000000);
      draw(backBuffer.getGraphics());
      frameCount++;
      lastLoopTime = thisLoopTime;
      redraw(panel.getGraphics());
      try {
        Thread.sleep(1000/30);
      } catch (InterruptedException e) {}
    }
  }

  private final void redraw(Graphics g) {
    if (g != null && backBuffer != null) {
      g.drawImage(backBuffer,0,0,null);
    }
  }

  int x = 30;

  public final void draw(Graphics g) {
    g.setColor(Color.darkGray);
    g.fillRect(0,0,getSize().width,getSize().height);
    g.setColor(Color.gray);
    g.fillRect(0,0,500,500);
    g.setColor(Color.blue);
    g.fillRect(x,30,300,300);
    x++;
  }

  public final Dimension getSize() {
    return frame.getSize();
  }

  public final void setSize(Dimension size) {
    frame.setSize(size);
  }

}

然而,这还没有完全解决,因为它仍然有奇怪的小图形故障,只有当从面板的paintComponent方法调用重绘时才会出现,尽管不一致。那些毛刺表现为奇怪的颜色矩形(通常是黑色或灰色),它们会立即消失。我真的不确定它可能是什么......也许是双缓冲的问题?顺便说一句,如果我在paintComponent中抛出一个运行时异常,那就完美了。

如果这应该转移到一个新问题,请告诉我。

1 个答案:

答案 0 :(得分:0)

我找到了解决方案:绘制和更新的不同循环:

public class MyCanvas {

  public static void main(String[] args) {
    new MyCanvas("MyCanvas",new Dimension(400,400));
  }

  private static final int frameRate = 1000 / 30;

  private JFrame frame;
  private JPanel panel;
  private int delta;
  private long lastLoopTime;
  private volatile boolean running = false;
  private int frameCount = 0;

  public MyCanvas(String name, Dimension size) {
    frame = new JFrame(name);
    panel = new JPanel() {
      protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        draw(g);
        if (running) repaint();
      }
    };
    frame.setContentPane(panel);
    frame.setSize(size);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel.setPreferredSize(size);
    panel.setLayout(null);
    frame.setResizable(true);
    running = true;
    frame.setVisible(true);
    lastLoopTime = System.nanoTime();
    new Thread(()->{
      while (running) {
        update();
        frameCount++;
        try {
          Thread.sleep(frameRate);
        } catch (InterruptedException e) {}
      }
    },"Game Loop").start();
  }

  int x = 30;

  public final void update() {
    x++;
  }

  public final void draw(Graphics g) {
    g.setColor(Color.darkGray);
    g.fillRect(0,0,getSize().width,getSize().height);
    g.setColor(Color.gray);
    g.fillRect(0,0,500,500);
    g.setColor(Color.blue);
    g.fillRect(x,30,300,300);
  }

  public final Dimension getSize() {
    return frame.getSize();
  }

  public final void setSize(Dimension size) {
    frame.setSize(size);
  }

}