更详细地讲,我在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中抛出了运行时异常,它会完美运行。

如果您想提出一个新的问题,请告诉我。

最佳答案

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

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);
  }

}

10-07 19:30
查看更多