简体   繁体   English

两个JButton使用相同的actionListener来启动/停止计时器

[英]Two JButtons using the same actionListener to Start/Stop Timer

I'm fairly new to Java and could do with some help. 我是Java的新手,可以提供一些帮助。 I trying to make a Timer countdown from a set time to 0. I have this functionality working fine, but I want to add functionality to allow me to stop the Timer as it's counting down. 我试图将计时器的倒计时从设置的时间设置为0。我的功能正常运行,但是我想添加一些功能,以便在计时器倒计时时停止计时器。

Here is my code (I'm trying to achieve this using MVC) 这是我的代码(我正在尝试使用MVC来实现)

This is the control part: 这是控制部分:

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

public class StartButton extends JButton implements ActionListener
{
    private TimerModel model;
    private Timer timer;
    private boolean isStarted;

    public StartButton(String buttonText, TimerModel model)
    {
        super(buttonText);
        addActionListener(this);
        this.model = model;
        isStarted = false;
    }

    public void actionPerformed(ActionEvent evt)
    {
        if(!isStarted) 
        {
            timer = new Timer(1000, this);
            timer.start();
            isStarted = true;
        }

        model.timerCountdown();
    }

    public void stopTimer()
    {
        timer.stop();
    }
}

I have looked at some other similar issues online and I tried this within the constructor (Note: I didn't use implements ActionListener, and removed the actionPerformed method I had above): 我在线上查看了其他一些类似的问题,并在构造函数中进行了尝试(注意:我没有使用实现ActionListener,而是删除了上面的actionPerformed方法):

if(buttonText.equals("Start"))
    {
        addActionListener(new ActionListener() 
        {
            public void actionPerformed(ActionEvent e)
            {
               if(!isStarted) 
               {
                   timer = new Timer(1000, this);
                   timer.start();
                   isStarted = true;
               }

               model.timerCountdown(); 
            }
        });
    }

    if(buttonText.equals("Stop"))
    {
        addActionListener(new ActionListener() 
        {
            public void actionPerformed(ActionEvent e)
            {
               timer.stop(); 
            }
        });
    }

Now this part handles the counting down ok, but when I click the stop button it shows an exception( See stack trace here ), and it continues to count down. 现在,此部分处理倒计时正常,但是当我单击“停止”按钮时,它会显示一个异常( 请参阅此处的堆栈跟踪 ),并且它将继续倒计时。

I have limited knowledge, but I guess it's something to do with the way I'm trying to stop the Timer. 我的知识有限,但是我想这与我试图停止计时器的方式有关。

I would appreciate it if someone could point me in the right direction, or at least explain to me why this happens. 如果有人可以指出正确的方向,或者至少向我解释为什么会发生,我将不胜感激。

Again, if you're not changing the basic behavior of the JButton itself, such as how it is drawn, but instead are only changing the button's title and behavior when pressed, then don't extend JButton. 同样,如果您不更改JButton本身的基本行为(例如其绘制方式),而是仅在按下时更改按钮的标题和行为,则不要扩展JButton。 Instead give each button its own Action, an object from a class that extends from AbstractAction. 而是给每个按钮自己的Action,这是从AbstractAction继承的类中的对象。 Consider these guys as similar to ActionListeners on steroids. 考虑这些家伙类似于类固醇上的ActionListeners。 They have the same abilities as ActionListeners and then some since they can easily change the button's title, whether it is enabled or not, its mnemonic, icon,... 它们具有与ActionListeners相同的功能,因此具有某些功能,因为它们可以轻松更改按钮的标题(无论是否启用),其助记符,图标,...

For example: 例如:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.*;

import javax.swing.*;

@SuppressWarnings("serial")
public class MyTimerGui {
   private static final String TITLE = "Flashing Label";
   private static final int TIMER_DELAY = 200;
   private static final int GAP = 3;
   private static final float LABEL_POINTS = 32F;
   private JPanel mainPanel = new JPanel();
   private JLabel flashyLabel = new JLabel(TITLE, SwingConstants.CENTER);
   private Timer timer = new Timer(TIMER_DELAY, new TimerListener());

   public MyTimerGui() {
      Font font = flashyLabel.getFont();
      font = font.deriveFont(LABEL_POINTS);
      flashyLabel.setFont(font);
      flashyLabel.setOpaque(true);

      JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
      buttonPanel.add(new JButton(new StartAction(this, "Start", KeyEvent.VK_S)));
      buttonPanel.add(new JButton(new StopAction(this, "Stop", KeyEvent.VK_T)));
      buttonPanel.add(new JButton(new ExitAction(this, "Exit", KeyEvent.VK_X)));

      mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
      mainPanel.setLayout(new BorderLayout());
      mainPanel.add(flashyLabel, BorderLayout.CENTER);
      mainPanel.add(buttonPanel, BorderLayout.PAGE_END);
   }

   public JComponent getMainComponent() {
      return mainPanel;
   }

   public void start() {
      timer.start();
   }

   public void stop() {
      timer.stop();
      flashyLabel.setForeground(null);
      flashyLabel.setBackground(null);
   }

   public void exit() {
      timer.stop();
      Window win = SwingUtilities.getWindowAncestor(mainPanel);
      win.dispose();
   }

   private class TimerListener implements ActionListener {
      private final Color foreground1 = Color.green;
      private final Color background1 = Color.red;

      @Override
      public void actionPerformed(ActionEvent aEvt) {
         Color fg = flashyLabel.getForeground();
         if (foreground1.equals(fg)) {
            flashyLabel.setForeground(null);
            flashyLabel.setBackground(null);
         } else {
            flashyLabel.setForeground(foreground1);
            flashyLabel.setBackground(background1);
         }
      }
   }

   private class StartAction extends AbstractAction {
      private MyTimerGui myTimerGui;

      public StartAction(MyTimerGui myTimerGui, String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
         this.myTimerGui = myTimerGui;
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         myTimerGui.start();
      }
   }

   private class StopAction extends AbstractAction {
      private MyTimerGui myTimerGui;

      public StopAction(MyTimerGui myTimerGui, String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
         this.myTimerGui = myTimerGui;
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         myTimerGui.stop();
      }
   }

   private class ExitAction extends AbstractAction {
      private MyTimerGui myTimerGui;

      public ExitAction(MyTimerGui myTimerGui, String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
         this.myTimerGui = myTimerGui;
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         myTimerGui.exit();
      }
   }

   private static void createAndShowGui() {
      MyTimerGui myTimerGui = new MyTimerGui();

      JFrame frame = new JFrame("MyTimer");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(myTimerGui.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

I agree with the comments that say you shouldn't be extending a JButton . 我同意那些不应该扩展JButton Maybe the logic should be carried out in the main class of your application, the same class that class that deals with the creation and storage of the components. 逻辑应该在应用程序的主类中执行,该主类与处理组件的创建和存储的类相同。

But I digress. 但是我离题了。 To answer your question, I think there are really two ways to approach this. 要回答您的问题,我认为确实有两种方法可以解决此问题。 Either (A) have the actionListener stored within your class as you did in your code or (B) write an actionListener outside of the object itself. (A)就像在代码中一样将actionListener存储在类中,或者(B)在对象本身之外编写一个actionListener

Was the constructor you tried to implement this in the main class constructor? 您是否试图在主类构造函数中实现此构造函数?

I think you need something like the following (this, again, is in the main class): 我认为您需要类似以下内容的内容(同样在主类中):

StartButton start = new JButton("Start");
StopButton stop = new JButton("Stop");

start.addActionListener(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
        // called when the button is pressed
        buttonPressed();
    }
});

stop.addActionListener(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
        // called when the button is pressed
        buttonPressed();
    }
});

Then you would write this method in the same class: 然后,您可以在同一类中编写此方法:

private void buttonPressed() {
    System.out.println("Button pressed!");
}

I just whipped up a quick test of this, so I can confirm that this method works. 我只是对此进行了快速测试,因此可以确认该方法有效。

PS: I would also suggest having the button contain a boolean state instead of checking for the text of the button, if you do intend to keep using the StartButton and associated classes. PS:如果您确实打算继续使用StartButton和关联的类,我还建议让按钮包含boolean state而不是检查按钮的文本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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