2014-02-09 5 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, Обходной бы добавление фиктивной Node в конце иерархии узла, который слушает KeyCode с

enter image description here

но есть лучшее решение, как инвертирующий маршрут распространения?

EDIT:

Прецедент:

Всплывающее подобный узел, который блокирует другие узлы должны были бы прислушивается к клавише 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

А вот некоторые примеры кода демонстрирует функциональность Escape. Нажатие 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

Возможно, вы могли бы перебрать все узлы после того, как поймали событие в сцене, чтобы узнать, какой узел имеет фактический фокус? Тогда вы можете вызвать метод node для закрытия?

Смежные вопросы