2013-05-20 38 views
6

我想使用JavaFX TextArea,就像它完全像多行TextField一樣。換句話說,當我按[Tab]時,我想循環到窗體上的下一個控件,當我按[Enter]時,我希望Key.Event轉到defaultButton控件(而不是由TextArea使用)。JavaFX 2 TextArea:如何阻止它消耗[Enter]和[Tab]

TextArea的默認行爲是將[Tab]插入到TextArea中,[Enter]插入換行符。

我知道我需要使用EventFilters來獲得我想要的行爲,但我得到的都是錯誤的。我不希望TextArea消耗這些事件......我只是想讓它們「繼續前進」。

回答

8

此處的解決方案顯示兩個文本區域和一個默認按鈕。 當用戶按下標籤鍵時,焦點移動到下一個控件。 當用戶按下回車鍵時,默認按鈕被觸發。

爲了實現這一行爲:

  1. 每個文本區域正陷入一個事件過濾器,複製和有針對性的對(其中包含了默認的OK按鈕)文本區的父節點的輸入按鍵。這會導致在按下表單上的任何位置時輸入默認的確定按鈕。原始輸入按鍵被消耗,因此它不會將新行添加到文本區域的文本中。
  2. 每個文本區域的tab鍵被過濾器捕獲,父級的焦點遍歷列表被處理以找到下一個可調焦的控件,併爲該控件請求焦點。原始製表符按鍵被消耗,因此它不會將新制表符間距添加到文本區域的文本中。

該代碼利用了Java 8中實現的功能,因此需要Java 8才能執行它。

textareahandler

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在文本區域中插入新的行或製表符空格。

+0

一個非常優雅的解決方案。感謝您花時間發佈此信息。 – scottb

+1

非常好。在我看來,TextArea API中應該有一個默認的處理屬性。我相信TextAreas處理Enter和Tab作爲默認行爲的方式在許多情況下並不適用。你的解決方案雖然很好,但現在還不能廣泛使用,需要很長的客戶端代碼。 – scottb

+0

@scottb您可能希望在[JavaFX問題跟蹤器](https://javafx-jira.kenai.com)中記錄此行爲的JavaFX平臺功能請求。 – jewelsea