JavaFX 2 TextArea:如何阻止它使用 [Enter] 和 [Tab] [英] JavaFX 2 TextArea: How to stop it from consuming [Enter] and [Tab]

查看:32
本文介绍了JavaFX 2 TextArea:如何阻止它使用 [Enter] 和 [Tab]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 JavaFX TextArea,就好像它与多行 TextField 完全一样.换句话说,当我按下 [Tab] 时,我想循环到窗体上的下一个控件,当我按下 [Enter] 时,我想让 Key.Event 转到 defaultButton 控件(而不是被 TextArea 消耗).

I want to use a JavaFX TextArea as though it were exactly like a multi-line TextField. In other words, when I press [Tab] I want to cycle to the next control on the form and when I press [Enter] I want the Key.Event to go to the defaultButton control (rather than be consumed by the TextArea).

TextArea 的默认行为是 [Tab] 插入到 TextArea 中,而 [Enter] 插入换行符.

The default behavior for TextArea is that [Tab] gets inserted into the TextArea and [Enter] inserts a new-line character.

我知道我需要使用 EventFilters 来获得我想要的行为,但我完全错了.我不希望 TextArea 消耗这些事件......我只是想让它们继续".

I know that I need to use EventFilters to get the behavior that I want, but I'm getting it all wrong. I don't want the TextArea to consume these events ... I just want it to let them "go right on by".

推荐答案

这里的解决方案显示两个文本区域和一个默认按钮.当用户按下 tab 键时,焦点向下移动到下一个控件.当用户按下回车键时,会触发默认按钮.

The solution here displays two text areas and a default button. When the user presses the tab key, the focus moves to the next control down. When the user presses the enter key, the default button is fired.

要实现此行为:

  1. 每个文本区域的回车键按下都会被事件过滤器捕获,复制并定位到文本区域的父节点(其中包含默认的确定按钮).这会导致在表单上的任意位置按下 enter 时触发默认的 OK 按钮.原来的回车键被消耗掉,所以它不会导致新的一行被添加到文本区域的文本中.
  2. 每个文本区域的 Tab 键按下被过滤器捕获,并且父级的焦点可遍历列表被处理以找到下一个可焦点控件,并为该控件请求焦点.原来的 Tab 键按下被消耗,因此它不会导致新的 Tab 间距被添加到文本区域的文本中.

代码使用了 Java 8 中实现的功能,因此需要 Java 8 才能执行它.

The code makes use of features implemented in Java 8, so Java 8 is required to execute it.

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()
        );
      }
    }
  }
}

另一种解决方案是为 TextArea 实现您自己的自定义皮肤,其中包括新的键处理行为.我相信这样的过程会比这里介绍的解决方案更复杂.

An alternate solution would be to implement your own customized skin for TextArea which includes new key handling behavior. I believe that such a process would be more complicated than the solution presented here.

更新

我不太喜欢这个问题的原始解决方案的一点是,一旦使用了 Tab 或 Enter 键,就无法触发它们的默认处理.所以我更新了解决方案,如果用户在按下 Tab 或 Enter 时按住 control 键,将执行默认的 Tab 或 Enter 操作.此更新后的逻辑允许用户通过按 CTRL+Enter 或 CTRL+Tab 将新行或制表符空间插入文本区域.

One thing I didn't really like about my original solution to this problem was that once the Tab or Enter key was consumed, there was no way to trigger their default processing. So I updated the solution such that if the user holds the control key down when pressing Tab or Enter, the default Tab or Enter operation will be performed. This updated logic allows the user to insert a new line or tab space into the text area by pressing CTRL+Enter or CTRL+Tab.

这篇关于JavaFX 2 TextArea:如何阻止它使用 [Enter] 和 [Tab]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆