使用键绑定打破循环
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 也是单线程的并且不是线程安全的。
这意味着三件事...
- 你永远不应该在事件调度线程的上下文中执行长 运行 或阻塞操作(如无限循环)
- 您不应从 EDT
的上下文之外更新 UI 的状态
- 当某种状态发生变化时,您应该使用某种观察者模式来采取行动。
既然您已经在使用 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();
我正在图像查看器应用程序中构建一个方法,该方法将创建一个幻灯片 - 它会在经过一段时间后按顺序转到下一张图像。我希望这种情况发生无穷大,除非用户按下 '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 也是单线程的并且不是线程安全的。
这意味着三件事...
- 你永远不应该在事件调度线程的上下文中执行长 运行 或阻塞操作(如无限循环)
- 您不应从 EDT 的上下文之外更新 UI 的状态
- 当某种状态发生变化时,您应该使用某种观察者模式来采取行动。
既然您已经在使用 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();