How to use a swing Timer with JPanel

2019-09-11 15:44发布

问题:

I'm fairly sure I understand how a swing timer works, I just cannot figure out how to apply it in my code. The way I apply it in my code doesn't allow it to draw because Graphics g is outside of its scope.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class JayFrame extends JFrame
{
  public JayFrame()
  {
    super("My Frame");
    setContentPane(new DrawPane());
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(1200, 675);
    setResizable(false);
    setVisible(true);
  }

  class DrawPane extends JPanel
  {
    Timer timer = new Timer(1000, new MyTimer());

    public void paintComponent(Graphics g)
    {
      //Paint stuff
      super.paintComponent(g);

      timer.start();

      for(int i = 0; i < 1000; i += 110)
      {
        g.fillRect(i, 10, 100, 100);

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

      timer.stop();
    }
  }

  class MyTimer implements ActionListener
  {
    public void actionPerformed(ActionEvent e)
    {
      //Loop stuff
      repaint();
    }
  }

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

EDIT: I have updated the code to show what I think should work, but does not. So I probably do have a flawed understanding of swing timers.

回答1:

  • Don't call timer.start(); and/or timer.stop() in the paintComponent method, this makes no sense what so ever, painting may occur for any number of reasons, my of which you have no control over.
  • Don't call Thread.sleep in your paintComponent, that's the point of have the Timer. You're just preventing Swing from updating the screen or process any new events

Timer acts as a psudo loop, on each iteration of the Timer, you update some state, check some exit condition and make your decisions about what should happen.

paintComponent simply paints the current state.

For example:

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class JayFrame extends JFrame {

    public JayFrame() {
        super("My Frame");
        setContentPane(new DrawPane());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(1200, 675);
        setResizable(false);
        setVisible(true);
    }

    class DrawPane extends JPanel {

        private int x = 0;

        public DrawPane() {
            Timer timer = new Timer(1000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    x += 110;
                    if (x >= 1000) {
                        x = 1000;
                        ((Timer)e.getSource()).stop();
                    }
                    repaint();
                }
            });
            timer.start();
        }

        public void paintComponent(Graphics g) {
            //Paint stuff
            super.paintComponent(g);

            g.fillRect(x, 10, 100, 100);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                new JayFrame();
            }
        });
    }
}