使用键绑定打破循环

Breaking out of a loop with key bindings

我正在图像查看器应用程序中构建一个方法,该方法将创建一个幻灯片 - 它会在经过一段时间后按顺序转到下一张图像。我希望这种情况发生无穷大,除非用户按下 'esc' 键。键绑定似乎是正确的解决方案,但我无法全神贯注地使用它们。到目前为止,这是我的代码:

private void slideshow(){
    JOptionPane.showMessageDialog(null, "Press 'esc' to stop the slideshow", "Slideshow", JOptionPane.INFORMATION_MESSAGE, slideshow);

    // sets the action listener that the timer triggers
    ActionListener slideshowPerformer = new ActionListener() {
        public void actionPerformed( ActionEvent event )
        {
            //goes to the next image every x seconds
            nextFile();
        }
    };

    Timer slideshowTimer = new Timer(slideshowTime, slideshowPerformer);

    while(true){
        //label is a JLabel
        label.getInputMap().//the key binding code
    }
}

尽管我已经在使用 MouseListener 浏览图像,但我愿意接受其他解决方案。

我认为更好的解决方案是从 0 开始计时,然后进入 while 循环并在每次迭代时将其递增 1。然后用一个数字取模,每次取模等于 0 时,转到下一帧。这是 运行 永远,直到一个动作侦听器正在侦听要按下的 esc 键,您在该键中退出循环。

总而言之,我认为只要使用一些较低层次的想法,就会使您的问题更容易解决。

Swing 与大多数 UI 框架一样,是事件驱动的。与大多数 UI 框架一样,Swing 也是单线程的并且不是线程安全的。

这意味着三件事...

  1. 你永远不应该在事件调度线程的上下文中执行长 运行 或阻塞操作(如无限循环)
  2. 您不应从 EDT
  3. 的上下文之外更新 UI 的状态
  4. 当某种状态发生变化时,您应该使用某种观察者模式来采取行动。

既然您已经在使用 Swing Timer 并且提到了使用键绑定,那么您已经在正确的轨道上,您缺少的 link 是 Action 与键绑定关联应该停止 Timer.

或者,如本例所示,Action 应通知其他相关方(即观察者),Action 已被触发,观察者应采取适当的行动。

import com.sun.glass.events.KeyEvent;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Engine {
        public void stop();
        public void start();
    }

    public class TestPane extends JPanel implements Engine {

        private Timer timer;
        private JLabel label = new JLabel("Waiting");

        public TestPane() {
            setLayout(new GridBagLayout());
            add(label);
            timer = new Timer(1000, new ActionListener() {
                private int counter = 0;
                @Override
                public void actionPerformed(ActionEvent e) {
                    counter++;
                    label.setText(Integer.toString(counter));
                }
            });
            InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = getActionMap();

            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "stop");
            actionMap.put("stop", new StopAction(this));

            start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        public void stop() {
            timer.stop();
            label.setText("Stopped");
        }

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

    }

    public class StopAction extends AbstractAction {

        private Engine engine;

        public StopAction(Engine engine) {
            this.engine = engine;
        }

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

    }

}

感谢大家的帮助!我完全用 while 循环放了个屁,忘记了如何使用 Swing 定时器来度过一分钟。这最终成为我最终的工作代码:

    ActionListener stopAction = new ActionListener(){
        public void actionPerformed(ActionEvent e){
            slideshowTimer.stop();
        }
    };

    // listens for the esc key to stop the slideshow
    label.getRootPane().registerKeyboardAction(stopAction,
        KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
        JComponent.WHEN_IN_FOCUSED_WINDOW);

    slideshowTimer.start();