在JavaFX中重新触发已消费事件

在JavaFX中重新触发已消费事件,java,event-handling,javafx-2,barcode-scanner,Java,Event Handling,Javafx 2,Barcode Scanner,我正在开发一个允许用户扫描条形码的系统。条形码扫描器的作用就像一个键盘,以超人的速度“输入”条形码的每个数字。在本例中,假设连续“按键”之间的大部分时间间隔为10毫秒 我首先实现了一个EventHandler,它在应用程序的窗口上侦听numericKeyEvents。当KeyEvent到达时,处理程序还不知道它是由人输入的还是由条形码扫描仪输入的(从现在起10毫秒后它就会知道)。不幸的是,我现在必须做出决定,否则就有可能锁定JavaFX的主线程,所以我会自动调用keyEvent.consume(

我正在开发一个允许用户扫描条形码的系统。条形码扫描器的作用就像一个键盘,以超人的速度“输入”条形码的每个数字。在本例中,假设连续“按键”之间的大部分时间间隔为10毫秒

我首先实现了一个
EventHandler
,它在应用程序的
窗口上侦听numeric
KeyEvent
s。当
KeyEvent
到达时,处理程序还不知道它是由人输入的还是由条形码扫描仪输入的(从现在起10毫秒后它就会知道)。不幸的是,我现在必须做出决定,否则就有可能锁定JavaFX的主线程,所以我会自动调用
keyEvent.consume()
,以防止它被处理

10毫秒后,计时器将唤醒并确定
KeyEvent
是否为条形码的一部分。如果是,则将
KeyEvent
s串联在一起,并由条形码处理逻辑处理。否则,我想让应用程序正常处理
KeyEvent


在我已经调用了
KeyEvent.consume()
之后,如何强制应用程序处理
KeyEvent

解决方案的工作原理是过滤应用程序的关键事件,克隆它们并将克隆的事件放入队列中,然后使用过滤器中的原始事件。稍后将处理克隆的事件队列。条形码读取器中的事件不会重新刷新。不来自条形码读取器的事件将被重新记录,以便系统能够处理它们。数据结构跟踪事件是否已经被处理,这样系统就可以在事件过滤器中知道它是否真的必须截获和使用事件,或者让它们传递给标准的JavaFX事件处理程序

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

// delays event key press handling so that some events can be intercepted
// and routed to a bar code a reader and others can be processed by the app.
public class EventRefire extends Application {
  public static void main(String[] args) { launch(args); }
  @Override public void start(final Stage stage) throws Exception {
    // create the scene.
    final VBox layout = new VBox();
    final Scene scene = new Scene(layout);

    // create a queue to hold delayed events which have not yet been processed.
    final List<KeyEvent> unprocessedEventQueue = new ArrayList();
    // create a queue to hold delayed events which have already been processed.
    final List<KeyEvent> processedEventQueue = new ArrayList();

    // create some controls for the app.
    final TextField splitterField1 = new TextField(); splitterField1.setId("f1");
    final TextField splitterField2 = new TextField(); splitterField2.setId("f2");
    final Label forBarCode   = new Label();
    final Label forTextField = new Label();

    // filter key events on the textfield and don't process them straight away.
    stage.addEventFilter(KeyEvent.ANY, new EventHandler<KeyEvent>() {
      @Override public void handle(KeyEvent event) {
        if (event.getTarget() instanceof Node) {
          if (!processedEventQueue.contains(event)) {
            unprocessedEventQueue.add((KeyEvent) event.clone());
            event.consume();
          } else {
            processedEventQueue.remove(event);
          }  
        }  
      }
    });

    // set up a timeline to simulate handling delayed event processing from 
    // the barcode scanner.
    Timeline timeline = new Timeline(
      new KeyFrame(
        Duration.seconds(1), 
        new EventHandler() {
          @Override public void handle(Event timeEvent) {
            // process the unprocessed events, routing them to the barcode reader
            // or scheduling the for refiring as approriate.
            final Iterator<KeyEvent> uei = unprocessedEventQueue.iterator();
            final List<KeyEvent> refireEvents = new ArrayList();
            while (uei.hasNext()) {
              KeyEvent event = uei.next();
              String keychar = event.getCharacter();
              if ("barcode".contains(keychar)) {
                forBarCode.setText(forBarCode.getText() + keychar);
              } else {
                forTextField.setText(forTextField.getText() + keychar);
                refireEvents.add(event);
              }
            }

            // all events have now been processed - clear the unprocessed event queue.
            unprocessedEventQueue.clear();

            // refire all of the events scheduled to refire.
            final Iterator<KeyEvent> rei = refireEvents.iterator();
            while (rei.hasNext()) {
              KeyEvent event = rei.next();
              processedEventQueue.add(event);
              if (event.getTarget() instanceof Node) {
                ((Node) event.getTarget()).fireEvent(event);
              }
            }
          }
        }
      )
    );
    timeline.setCycleCount(Timeline.INDEFINITE);
    timeline.play();

    // layout the scene.
    final GridPane grid = new GridPane();
    grid.addRow(0, new Label("Input Field 1:"), splitterField1);
    grid.addRow(1, new Label("Input Field 2:"), splitterField2);
    grid.addRow(2, new Label("For App:"),       forTextField);
    grid.addRow(3, new Label("For BarCode:"),   forBarCode);
    grid.setStyle("-fx-padding: 10; -fx-vgap: 10; -fx-hgap: 10; -fx-background-color: cornsilk;");

    Label instructions = new Label("Type letters - key events which generate the lowercase letters b, a, r, c, o, d, e will be routed to the barcode input processor, other key events will be routed back to the app and processed normally.");
    instructions.setWrapText(true);
    layout.getChildren().addAll(grid, instructions);
    layout.setStyle("-fx-padding: 10; -fx-vgap: 10; -fx-background-color: cornsilk;");
    layout.setPrefWidth(300);
    stage.setScene(scene);
    stage.show();
  }
}
import java.util.ArrayList;
导入java.util.Iterator;
导入java.util.List;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.event.event;
导入javafx.event.EventHandler;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.control.Label;
导入javafx.scene.control.TextField;
导入javafx.scene.input.KeyEvent;
导入javafx.scene.layout.GridPane;
导入javafx.scene.layout.VBox;
导入javafx.stage.stage;
导入javafx.util.Duration;
//延迟事件按键处理,以便拦截某些事件
//并发送到条形码阅读器和其他应用程序可以处理。
公共类EventRefire扩展应用程序{
公共静态void main(字符串[]args){launch(args);}
@覆盖公共无效开始(最终阶段)引发异常{
//创建场景。
最终VBox布局=新VBox();
最终场景=新场景(布局);
//创建队列以保存尚未处理的延迟事件。
最终列表未处理ventqueue=new ArrayList();
//创建队列以保存已处理的延迟事件。
最终列表processedEventQueue=new ArrayList();
//为应用程序创建一些控件。
final TextField splitterField1=new TextField();splitterField1.setId(“f1”);
final TextField splitterField2=new TextField();splitterField2.setId(“f2”);
条形码的最终标签=新标签();
最终标签forTextField=新标签();
//过滤文本字段上的键事件,不要立即处理它们。
stage.addEventFilter(KeyEvent.ANY,neweventhandler()){
@覆盖公共无效句柄(KeyEvent事件){
if(event.getTarget()instanceof Node){
如果(!processedEventQueue.contains(事件)){
未处理的ventqueue.add((KeyEvent)event.clone());
event.consume();
}否则{
processedEventQueue.remove(事件);
}  
}  
}
});
//设置时间线,以模拟处理来自的延迟事件处理
//条形码扫描器。
时间线=新时间线(
新关键帧(
持续时间。秒(1),
新的EventHandler(){
@重写公共无效句柄(事件时间事件){
//处理未处理的事件,并将其发送到条形码读取器
//或安排适当的重新充装时间。
final Iterator uei=unprocessedventqueue.Iterator();
最终列表refreevents=new ArrayList();
while(uei.hasNext()){
KeyEvent事件=uei.next();
字符串keychar=event.getCharacter();
如果(“条形码”。包含(keychar)){
forBarCode.setText(forBarCode.getText()+keychar);
}否则{
setText(forTextField.getText()+keychar);
refreevents.add(事件);
}
}
//现在已处理所有事件-清除未处理的事件队列。
未处理的proventqueue.clear();
//重新刷新计划要重新刷新的所有事件。
final迭代器rei=refireEvents.Iterator();
while(rei.hasNext()){
KeyEvent事件=rei.next();
processedEventQueue.add(事件);
if(event.getTarget()instanceof Node){
((节点)event.getTarget()).fireEvent(事件);
}
}
}
}
)
);
timeline.setCycleCount(timeline.unfinite);
timeline.play();
//布置场景。
最终网格窗格网格=新网格窗格();
addRow(0,新标签(“输入字段1:”),splitterField1);
addRow(1,新标签(“输入字段2:”),splitterField2);
addRow(2,新标签(“For App:”),forTextField);
grid.addRow(3,新标签(“用于条码:”),用于条码);
grid.setStyle(“-fx填充:10;-fx vgap:10;-fx hgap:10;-fx背景