3

我正在开发一个允许用户扫描条形码的系统。条码扫描器实际上就像一个键盘,以超人的速度“键入”条码的每个数字。为了这个例子,假设连续“击键”之间的大部分时间是 10 毫秒。

我首先在应用EventHandler程序KeyEventWindow. 当 aKeyEvent到达时,处理程序还不知道它是由人输入还是由条形码扫描仪输入的(它将10 毫秒后知道)。不幸的是,我现在必须做出决定,否则有锁定 JavaFX 主线程的风险,所以我自动调用keyEvent.consume()以防止它被处理。

10 毫秒过去后,定时器唤醒并决定是否KeyEvent是条形码的一部分。如果是,则将KeyEvents 连接在一起并由条形码处理逻辑处理。否则,我想让应用程序KeyEvent正常处理。

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

4

1 回答 1

3

这是我对如何做到这一点的看法。

该解决方案的工作原理是过滤应用程序的关键事件,克隆它们并将克隆的事件放入队列中,然后使用过滤器中的原始事件。克隆的事件队列稍后处理。来自条形码阅读器的事件不会重新触发。不是来自条形码阅读器的事件将被重新触发,以便系统可以处理它们。数据结构跟踪事件是否已经被处理,以便系统可以在事件过滤器中知道它是否真的必须拦截和使用事件,或者让它们传递给标准的 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();
  }
}

示例程序输出:

示例程序输出

因为我使用时间轴,所以我的代码中的所有内容都在 FXApplicationThread 上运行,所以我不必担心我的实现中的并发性。在使用真正的条形码阅读器和条形码事件处理器实现时,您可能需要一些额外的并发保护,因为可能会涉及多个线程。此外,您可能不需要我的代码中使用的时间线来模拟条形码系统的延迟处理。

于 2012-08-10T02:06:39.100 回答