简体   繁体   中英

Java Canvas when I resize the JFrame, the canvas stops drawing

In more detail, I have a componentResized event attached to the JFrame (which contains the canvas, and nothing else), and in that event a call a method which sets the bounds of the canvas accordingly. This works fine, except that while I'm resizing the canvas, it doesn't show anything. I just see the back of the JFrame. Once I've stopped resizing the JFrame, the canvas paints fine again.

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
  }

}

Edit

After tweaking with the code for a while, I have come up with a solution:

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

}

However, this is not quite solved yet, because it still has odd little graphical glitches which only seem to appear when redraw is called from panel's paintComponent method, though not consistently. Those glitches manifest themselves as odd rectangles of color (usually black or grey) which promptly disappear again. I'm really not sure of what it could be... maybe problems with the double buffering? BTW, if I threw a runtime exception in paintComponent, it worked perfectly.

If this should be moved to a new question, please let me know.

I found the solution: different loops for draw and update:

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM