此處的解決方案顯示兩個文本區域和一個默認按鈕。 當用戶按下標籤鍵時,焦點移動到下一個控件。 當用戶按下回車鍵時,默認按鈕被觸發。
爲了實現這一行爲:
- 每個文本區域正陷入一個事件過濾器,複製和有針對性的對(其中包含了默認的OK按鈕)文本區的父節點的輸入按鍵。這會導致在按下表單上的任何位置時輸入默認的確定按鈕。原始輸入按鍵被消耗,因此它不會將新行添加到文本區域的文本中。
- 每個文本區域的tab鍵被過濾器捕獲,父級的焦點遍歷列表被處理以找到下一個可調焦的控件,併爲該控件請求焦點。原始製表符按鍵被消耗,因此它不會將新制表符間距添加到文本區域的文本中。
該代碼利用了Java 8中實現的功能,因此需要Java 8才能執行它。
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.*;
import javafx.collections.ObservableList;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import static javafx.scene.input.KeyCode.ENTER;
import static javafx.scene.input.KeyCode.TAB;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.*;
public class TextAreaTabAndEnterHandler extends Application {
final Label status = new Label();
public static void main(String[] args) { launch(args); }
@Override public void start(final Stage stage) {
final TextArea textArea1 = new TabAndEnterIgnoringTextArea();
final TextArea textArea2 = new TabAndEnterIgnoringTextArea();
final Button defaultButton = new Button("OK");
defaultButton.setDefaultButton(true);
defaultButton.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
status.setText("Default Button Pressed");
}
});
textArea1.textProperty().addListener(new ClearStatusListener());
textArea2.textProperty().addListener(new ClearStatusListener());
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;");
layout.getChildren().setAll(
textArea1,
textArea2,
defaultButton,
status
);
stage.setScene(
new Scene(layout)
);
stage.show();
}
class ClearStatusListener implements ChangeListener<String> {
@Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
status.setText("");
}
}
class TabAndEnterIgnoringTextArea extends TextArea {
final TextArea myTextArea = this;
TabAndEnterIgnoringTextArea() {
addEventFilter(KeyEvent.KEY_PRESSED, new TabAndEnterHandler());
}
class TabAndEnterHandler implements EventHandler<KeyEvent> {
private KeyEvent recodedEvent;
@Override public void handle(KeyEvent event) {
if (recodedEvent != null) {
recodedEvent = null;
return;
}
Parent parent = myTextArea.getParent();
if (parent != null) {
switch (event.getCode()) {
case ENTER:
if (event.isControlDown()) {
recodedEvent = recodeWithoutControlDown(event);
myTextArea.fireEvent(recodedEvent);
} else {
Event parentEvent = event.copyFor(parent, parent);
myTextArea.getParent().fireEvent(parentEvent);
}
event.consume();
break;
case TAB:
if (event.isControlDown()) {
recodedEvent = recodeWithoutControlDown(event);
myTextArea.fireEvent(recodedEvent);
} else {
ObservableList<Node> children = parent.getChildrenUnmodifiable();
int idx = children.indexOf(myTextArea);
if (idx >= 0) {
for (int i = idx + 1; i < children.size(); i++) {
if (children.get(i).isFocusTraversable()) {
children.get(i).requestFocus();
break;
}
}
for (int i = 0; i < idx; i++) {
if (children.get(i).isFocusTraversable()) {
children.get(i).requestFocus();
break;
}
}
}
}
event.consume();
break;
}
}
}
private KeyEvent recodeWithoutControlDown(KeyEvent event) {
return new KeyEvent(
event.getEventType(),
event.getCharacter(),
event.getText(),
event.getCode(),
event.isShiftDown(),
false,
event.isAltDown(),
event.isMetaDown()
);
}
}
}
}
另一種解決辦法是實施文本區域,其中包括新的關鍵處理行爲自己的自定義皮膚。我相信這樣的過程比這裏提出的解決方案更復雜。
更新
有一件事我真的不喜歡我的原始解決這個問題是,一旦Tab鍵或回車鍵被消耗掉,有沒有辦法來觸發它們的默認處理。所以我更新瞭解決方案,如果用戶按下Tab或Enter時按住Ctrl鍵,將執行默認的Tab或Enter操作。這個更新後的邏輯允許用戶通過按下CTRL + Enter或CTRL + Tab在文本區域中插入新的行或製表符空格。
一個非常優雅的解決方案。感謝您花時間發佈此信息。 – scottb
非常好。在我看來,TextArea API中應該有一個默認的處理屬性。我相信TextAreas處理Enter和Tab作爲默認行爲的方式在許多情況下並不適用。你的解決方案雖然很好,但現在還不能廣泛使用,需要很長的客戶端代碼。 – scottb
@scottb您可能希望在[JavaFX問題跟蹤器](https://javafx-jira.kenai.com)中記錄此行爲的JavaFX平臺功能請求。 – jewelsea