2012-08-09 69 views
2

我正在開發一個系統,允許用戶掃描條形碼。條形碼掃描器的行爲就像一個鍵盤,以超人類速度「打印」條形碼的每個數字。爲了這個例子,假設連續「擊鍵」之間的大部分時間是10毫秒。重新點亮所消耗的事件在JavaFX的

我首先執行EventHandler,它在應用程序的Window上偵聽數字KeyEvent。當KeyEvent到達時,處理器還不知道它是否是由人或由條形碼掃描器(它知道10毫秒從現在開始)進入。不幸的是,我必須做出決定現在或風險的JavaFX的主線程鎖定了,所以我自動調用keyEvent.consume(),以防止它被處理。

10毫秒後已經過去了,計時器醒來並決定KeyEvent是否是條形碼的一部分。如果是這樣,則KeyEvent被連接在一起並由條形碼處理邏輯處理。否則,我想讓應用程序正常處理KeyEvent

我怎麼能強制應用程序處理KeyEvent後我已經調用keyEvent.consume()呢?

回答

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(); 
    } 
} 

示例程序輸出:

Sample program output

因爲我使用了一個時間軸一切都在我的代碼在運行FXApplicationThread,所以我不擔心我在執行併發。在使用真正的條形碼閱讀器和條形碼事件處理器實施時,可能需要一些增加的併發保護,因爲可能涉及多個線程。您也可能不需要我的代碼中使用的時間線來模擬條碼系統的延遲處理。

+0

非常感謝!我使用'Timeline'來保存JavaFX線程中的所有內容。我沒有意識到'Event'有一個'clone()'方法。 – 2012-08-10 12:40:40