2014-02-09 74 views
4

我想在我的場景中聽一些KeyEvent,例如KeyCode.ESCAPE(按下時關閉場景)。現在JavaFX KeyEvent傳播順序

scene.addEventHandler(KeyEvent.ANY, event -> { 
      if (event.isConsumed()) 
       return; 
      switch (event.getCode()) { 
      case ESCAPE: 
       stage.hide(); 
       event.consume(); 
       break; 
      default: 
       break; 
      } 
     }); 

,裏面的場景節點可能已經聽過太ESCAPE

// .... 
someOtherNode.addEventHandler(KeyEvent.ANY, e -> { 
     if (e.getCode() == KeyCode.ESCAPE) { 
      // do stuff 
      e.consume(); 
     } 
}); 
// .... 

如何確保從節點消耗KeyEvent而不是場景?

基於從Oracle在圖上,一種解決方法是在偵聽KeyCode小號

enter image description here

但有一個更好的解決方案,像反轉節點等級的末尾添加一個虛設Node傳播路線?

編輯:

用例:

,阻止其他節點需要監聽的ESC鍵或focusProperty(),以便它可以關閉本身就是一個彈出式節點。

+0

我可能很密集,但是這不是過濾器和處理程序的用途嗎?過濾器在捕獲階段停止事件(如果消耗)。事後處理程序可以在冒泡階段觸發。一個是另一個的反面。 – brian

回答

6

有兩種方式可以影響事件:

  1. 使用Node.addEventFilter(...)方法來註冊一個過濾器。過濾器將在事件的捕獲階段執行(因爲窗口越來越具體,確定哪些節點應該得到事件)。

  2. 使用Node.addEventHandler(...)方法註冊處理程序。該處理程序將從捕獲階段中找到的最具體節點開始執行,直至耗盡。

所以在捕獲階段,創建一個堆棧。從窗口(最上面的父窗口)開始,此事件可能執行的每個節點都會添加到堆棧中(以最底層的子窗口結束)。過濾器可以中斷這個過程,或者只是在這個過程中執行一個事件。

在冒泡階段,事件處理程序將從堆棧頂部開始觸發(在捕獲階段創建),直到堆棧爲空或事件消耗完畢。

就你而言,你真的不應該有任何擔心。如果任何節點關心處理「ESC」事件,他們將在冒泡階段這樣做(並且它們應該消耗該事件以防止進一步處理)。您可以在ComboBox中看到此行爲。如果他們不在意,它會冒泡到您的Scene,並且該處理程序將執行。只要確保您創建的處理「ESC」按鈕的自定義代碼也會消耗該事件。

欲瞭解更多信息,有一個解釋和教程在這裏:http://docs.oracle.com/javafx/2/events/jfxpub-events.htm

這裏是一些示例代碼演示了逃生的功能。按下ESC鍵同時關注ComboBox不會導致應用程序關閉,而會關閉其他控件。

import javafx.application.Application; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.FXCollections; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.control.TableColumn.CellDataFeatures; 
import javafx.scene.control.cell.TextFieldTableCell; 
import javafx.scene.input.KeyEvent; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 
import javafx.util.Callback; 
import javafx.util.converter.DefaultStringConverter; 


public class FXEventFiltering extends Application { 

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

    @Override 
    public void start(final Stage stage) throws Exception { 
     //All the controls are added here 
     VBox box = new VBox(); 
     ComboBox<String> dropdown = new ComboBox<>(); 
     TextField field = new TextField(); 
     CheckBox check = new CheckBox("Check"); 
     RadioButton radio = new RadioButton("Radio!"); 
     TextArea area = new TextArea(); 
     TableView<String> table = new TableView<String>(FXCollections.observableArrayList(new String[]{"one","two"})); 
     TableColumn<String, String> tc = new TableColumn<String, String>("Column1"); 
     tc.setEditable(true); 
     tc.setCellFactory(TextFieldTableCell.<String,String>forTableColumn(new DefaultStringConverter())); 
     tc.setCellValueFactory(new Callback<CellDataFeatures<String,String>, ObservableValue<String>>(){ 
      @Override 
      public ObservableValue<String> call(CellDataFeatures<String, String> arg0) { 
       return new SimpleStringProperty(arg0.getValue()); 
      }}); 
     table.getColumns().add(tc); 

     box.getChildren().addAll(dropdown, field, check, radio, area, table); 

     //Setting up your scene 
     Scene scene = new Scene(box); 
     stage.setScene(scene); 
     scene.addEventHandler(KeyEvent.ANY, new EventHandler<KeyEvent>() { 
      @Override 
      public void handle(KeyEvent event) { 
       System.out.println("KEYS!" + event.getEventType().getName()); 
       switch (event.getCode()) { 
       case ESCAPE: 
        System.out.println("Escape!"); 
        stage.hide(); 
        event.consume(); 
        break; 
       default: 
        break; 
       } 
      } 
     }); 

     box.requestFocus(); // Removing default focus 

     stage.show(); 
    } 

} 
+1

非常詳細的解釋!謝謝 – tom91136

0

也許你可以在捕捉場景中的事件後找到哪個節點具有實際焦點,然後遍歷所有節點?那麼你可以調用節點方法關閉?