问题
I have a very simple task to accomplish. I just want to press any letter on a button, matches the key code, and move the focus to a text field. I wrote a simple test code as shown. I have no problem to shift the focus. However, I don't want the letter I press shows up in the text field. Seemingly a simple programming solution turns out to be not so simple.
I don't understand why the event consume method doesn't stop the event from propagating down the event chain and have the typed letter shown up at the text field.
It seems like after the requestFocus is called, the text field picks up the letter typed from the button. This happens on Mac. Any help will be greatly appreciated.
package testkeynavigation;
public class TestKeyNavigation extends Application {
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
TextField txt1 = new TextField();
TextField txt2 = new TextField();
VBox vbox = new VBox();
vbox.getChildren().add(btn);
vbox.getChildren().add(txt1);
vbox.getChildren().add(txt2);
root.getChildren().add(vbox);
Scene scene = new Scene(root, 300, 250);
btn.setOnKeyPressed((KeyEvent e) ->{
if (e.getCode() == KeyCode.A) {
e.consume();
System.out.println("e.isConsumed: "+e.isConsumed());
txt2.requestFocus();
}
});
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
btn.requestFocus();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
回答1:
There are three kinds of key event: KEY_PRESSED
, KEY_TYPED
, and KEY_RELEASED
. In a key stroke, an event of each of these types is fired, in that order, to the UI node that has the keyboard focus at the time of the event.
A TextField
has an internal listener for KEY_TYPED
events; so if a KEY_TYPED
event occurs when the text field has focus, a character is entered into the text field (or other actions occur, e.g. deleting characters or moving the caret, depending on the key).
In your code, you listen for the first one of these - KEY_PRESSED
- to occur on the button. If the key press has a code of A
, you consume that event (the KEY_PRESSED
event) then transfer keyboard focus to the text field. At a slightly later moment, the user releases the key, and since the text field now has focus, a KEY_TYPED
event is fired on the text field. Note that this is a new event, so it is not consumed, so the text fields reacts as though a character has been entered. Finally, a KEY_RELEASED
event is fired on the text field.
You can see this in action if you add the debugging code:
txt2.addEventFilter(KeyEvent.ANY, e -> {
System.out.printf("Key event on text field: type=%s, code=%s, character=%s%n",
e.getEventType(), e.getCode(), e.getCharacter());
});
To fix the problem, just listen for the last event in the series of events: the key released event. Note that you don't need to consume the event.
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
TextField txt1 = new TextField();
TextField txt2 = new TextField();
VBox vbox = new VBox();
vbox.getChildren().add(btn);
vbox.getChildren().add(txt1);
vbox.getChildren().add(txt2);
root.getChildren().add(vbox);
Scene scene = new Scene(root, 300, 250);
btn.setOnKeyReleased((KeyEvent e) ->{
if (e.getCode() == KeyCode.A) {
txt2.requestFocus();
}
});
txt2.addEventFilter(KeyEvent.ANY, e -> {
System.out.printf("Key event on text field: type=%s, code=%s, character=%s%n",
e.getEventType(), e.getCode(), e.getCharacter());
});
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
btn.requestFocus();
}
来源:https://stackoverflow.com/questions/49212844/is-this-the-proper-behaviour-for-event-filter-and-requstfocus-in-javafx