Timer Won't Fire Correctly

2019-03-04 10:33发布

问题:

I'm using a Timer to toggle a boolean, but instead of firing every 250ms like it's supposed to, it fires as fast as it can. Here is my code:

package com.cgp.tetris;

import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import javax.imageio.ImageIO;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.sampled.DataLine.Info;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

public class SinglePlayerMenu extends JPanel implements Runnable {
    private static final long serialVersionUID = 1L;
    private Thread thread;
    private AudioInputStream themestream, blipstream;
    private Clip clip, blipclip;
    private BufferedImage base, bi1, bi2, bi3, bi4, bi1s, bi2s, bi3s, bi4s;
    private boolean typeselected = false, b1 = true, b2 = false, b3 = true, b4 = false, b5 = false, b6 = false, b1a = true, b2a = false;

    public SinglePlayerMenu() {
        super();
        this.addComponentListener(new ComponentAdapter() {
            public void componentShown(ComponentEvent e) {
                SinglePlayerMenu.this.requestFocusInWindow();
            }
        });
    }

    public void addNotify() {
        super.addNotify();
        thread = new Thread(this);
        thread.start();
    }

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

        g.drawImage(base, 0, 0, 640, 576, null);

        if (b1) {
            g.drawImage(bi1s, 80, 156, 240, 40, null);
        } else if (!b1) {
            g.drawImage(bi1, 80, 156, 240, 40, null);
        }
        if (b2) {
            g.drawImage(bi2s, 324, 156, 236, 40, null);
        } else if (!b2) {
            g.drawImage(bi2, 324, 156, 236, 40, null);
        }
        if (b3) {
            g.drawImage(bi1s, 80, 380, 240, 40, null);
        } else if (!b3) {
            g.drawImage(bi1, 80, 380, 240, 40, null);
        }
        if (b4) {
            g.drawImage(bi2s, 324, 380, 236, 40, null);
        } else if (!b4) {
            g.drawImage(bi2, 324, 380, 236, 40, null);
        }
        if (b5) {
            g.drawImage(bi3s, 80, 444, 240, 40, null);
        } else if (!b5) {
            g.drawImage(bi3, 80, 444, 240, 40, null);
        }
        if (b6) {
            g.drawImage(bi4s, 324, 444, 236, 40, null);
        } else if (!b6) {
            g.drawImage(bi4, 324, 444, 236, 40, null);
        }
    }

    public void run() {
        sound();
        loadImages();
        bind();
        while (true) {
            repaint();
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            checks();
        }
    }

    private void checks() {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                System.out.println("Being run");
                if (b1a && !typeselected) {
                    b1 = !b1;
                }
            }
        }, java.util.Calendar.getInstance().getTime(), 250);
    }

    private void bind() {
        InputMap im = getInputMap();
        ActionMap am = getActionMap();

        im.put(KeyStroke.getKeyStroke("LEFT"), "left");
        am.put("left", new AbstractAction() {
            private static final long serialVersionUID = 1L;

            public void actionPerformed(ActionEvent e) {
                if (!typeselected) {
                    if (!b1a) {
                        blipclip.start();
                        blipclip.setFramePosition(0);
                    }
                    b1 = true;
                    b2 = false;
                    b1a = true;
                    b2a = false;
                } else if (typeselected) {

                }
            }
        });

        im.put(KeyStroke.getKeyStroke("RIGHT"), "right");
        am.put("right", new AbstractAction() {
            private static final long serialVersionUID = 1L;

            public void actionPerformed(ActionEvent e) {
                if (!typeselected) {
                    if (!b2a) {
                        blipclip.start();
                        blipclip.setFramePosition(0);
                    }
                    b2 = true;
                    b1 = false;
                    b1a = false;
                    b2a = true;
                } else if (typeselected) {
                }
            }
        });

        im.put(KeyStroke.getKeyStroke("SPACE"), "space");
        am.put("space", new AbstractAction() {
            private static final long serialVersionUID = 1L;

            public void actionPerformed(ActionEvent e) {
                if (!typeselected) {
                    typeselected = true;
                    b1=b1a;
                    b2=b2a;
                } else if (typeselected) {

                }
            }
        });
    }

    private void loadImages() {
        try {
            base = ImageIO.read(new File("res/base.png"));
            bi1 = ImageIO.read(new File("res/1.png"));
            bi2 = ImageIO.read(new File("res/2.png"));
            bi3 = ImageIO.read(new File("res/3.png"));
            bi4 = ImageIO.read(new File("res/4.png"));
            bi1s = ImageIO.read(new File("res/1s.png"));
            bi2s = ImageIO.read(new File("res/2s.png"));
            bi3s = ImageIO.read(new File("res/3s.png"));
            bi4s = ImageIO.read(new File("res/4s.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sound() {
        File theme = new File("res/playtheme.wav");
        try {
            themestream = AudioSystem.getAudioInputStream(theme);
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        DataLine.Info info = new DataLine.Info(Clip.class, themestream.getFormat());
        clip = null;
        try {
            clip = (Clip) AudioSystem.getLine(info);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
        try {
            clip.open(themestream);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        File blip = new File("res/blip.wav");
        try {
            blipstream = AudioSystem.getAudioInputStream(blip);
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        DataLine.Info blipinfo = new DataLine.Info(Clip.class, blipstream.getFormat());
        blipclip = null;
        try {
            blipclip = (Clip) AudioSystem.getLine(blipinfo);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
        try {
            blipclip.open(blipstream);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startClip() {
        clip.loop(Clip.LOOP_CONTINUOUSLY);
        blipclip.start();
        blipclip.setFramePosition(0);
    }
}

Sadly, Eclipse is acting weird, so an SSCCE won't be possible ATM.

回答1:

You basic code looks wrong to me:

  1. Custom painting should be done in the paintComponent() method.

  2. When working with Swing you should be using a Swing Timer.

  3. You have a Thread that repaints every 2ms. Is that your problem?

  4. You don't need a Thread with a while () loop. That is what you use a Timer for. So I would guess the Thread and the Timer should be combined into a single Swing Timer. When the time fires you update your variables and then invoke repaint().



回答2:

From Timer.scheduleAtFixedRate-documentation:

In fixed-rate execution, each execution is scheduled relative to the scheduled execution time of the initial execution. If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up." In the long run, the frequency of execution will be exactly the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate).

If your task takes longer than 250ms to execute, the following task will be executed right after the first task, and the one following that, etc. Maybe you meant to use Timer.schedule instead?

Schedules the specified task for repeated fixed-delay execution, beginning at the specified time. Subsequent executions take place at approximately regular intervals, separated by the specified period.

In fixed-delay execution, each execution is scheduled relative to the actual execution time of the previous execution. If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well.