JavaFx:不触发TextField KeyEvent

我正在实现一个cusom组件,用户可以在其中键入TextField,然后弹出TableView,然后用户可以在该表中查找项目。

我对KeyEvent standard TextField类有疑问,例如 Ctrl + A Home 。弹出带有TableView的窗格后,这些关键事件不再起作用。 我检查了TextField是否失去了焦点,但没有失去焦点,如果我设置了EventFilter以查看正在发生的情况,则表明这些事件已触发,但对UI没有影响。甚至弹出窗口的setHideOnescape也无法正常工作。

这是验证它的简单代码:

fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="textField.Controller">
    <TextField fx:id="textField" prefWidth="400"/>
</AnchorPane>

控制器:

public class Controller implements Initializable {

    @FXML
    private TextField textField;


    @Override
    public void initialize(URL location,ResourceBundle resources) {

        Popup popUp = new Popup();
        TableView<Object> table = new TableView<>();

        table.prefWidthProperty().bind(textField.widthProperty());
        popUp.getcontent().add(table);
        popUp.setHideOnescape(true);
        popUp.setautoHide(true);


        // To see if the KeyEvent is triggered
        textField.addEventFilter(KeyEvent.ANY,System.out::println);

        textField.setOnKeyTyped(event -> {
            if(!popUp.isShowing()){
                popUp.show(
                        textField.getScene().getWindow(),textField.getScene().getWindow().getX()
                                + textField.localToScene(0,0).getX()
                                + textField.getScene().getX(),textField.getScene().getWindow().getY()
                                + textField.localToScene(0,0).getY()
                                + textField.getScene().getY()
                                + textField.getHeight() - 1);
            }
        });
    }
}

主要:

public class Main extends Application {

    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getclass().getResource("View.fxml"));
        AnchorPane pane = loader.load();
        primaryStage.setScene(new Scene(pane,800,600));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这是控制台输出

KeyEvent [source = TextField[id=textField,styleclass=text-input text-field],target = TextField[id=textField,eventType = KEY_pressed,consumed = false,character =,text = a,code = A]
KeyEvent [source = TextField[id=textField,eventType = KEY_TYPED,character = a,text =,code = UNDEFINED]
KeyEvent [source = TextField[id=textField,eventType = KEY_RELEASED,code = CONTROL,controlDown,shortcutDown]
KeyEvent [source = TextField[id=textField,code = UNDEFINED,code = A,code = CONTROL]

有什么主意,为什么说那些事件没有被消耗掉呢?

pqadbc 回答:JavaFx:不触发TextField KeyEvent

textField不对某些/所有“标准功能键”做出反应的原因是,它们的KEY_PRESSED类型永远不会到达它-它们被重定向到弹出窗口中的表,并且大多数/所有表都被表使用。

天真的第一近似方法是将表的focusTraversable属性设置为false:这有效地防止了将所有键传递给它。现实世界中的需求可能不太简单,因为其中一些需求应该到达表,而其他需求应该冒充到textField。

这可以通过自定义EventDispatcher(在表上)来实现,该事件检查器检查所有keyEvent并确定要传递/不传递给表的原始调度程序。一个代码片段,其中拦截器是用于决策的谓词(为方便起见,最后有完整的工作示例):

private BasicEventDispatcher original;
private Predicate<Event> interceptor;

@Override
public Event dispatchEvent(Event event,EventDispatchChain tail) {
    if (!interceptor.test(event)) {
        event = original.dispatchCapturingEvent(event);
        if (event.isConsumed()) {
            return null;
        }
    }
    event = tail.dispatchEvent(event);
    if (event != null && !interceptor.test(event)) {
        event = original.dispatchBubblingEvent(event);
        if (event.isConsumed()) {
            return null;
        }
    }
    return event;
}

用法:如果是f.i.我们希望将LEFT和RIGT冒泡到该textField,而所有其他字段应由表正常处理

List<KeyCode> toIntercept = List.of(KeyCode.LEFT,KeyCode.RIGHT);
Predicate<Event> interceptor = e -> {
    if (e instanceof KeyEvent) {
        return toIntercept.contains(((KeyEvent) e).getCode());
    }
    return false;
};
table.setEventDispatcher(new InterceptingEventDispatcher(
        (BasicEventDispatcher) table.getEventDispatcher(),interceptor));

完整示例:

public class ViewPopupApplication extends Application {

    public static class InterceptingEventDispatcher implements EventDispatcher {
        private BasicEventDispatcher original;
        private Predicate<Event> interceptor;

        public InterceptingEventDispatcher(BasicEventDispatcher original,Predicate<Event> interceptor) {
            this.original = original;
            this.interceptor = interceptor;
        }

        @Override
        public Event dispatchEvent(Event event,EventDispatchChain tail) {
            if (!interceptor.test(event)) {
                event = original.dispatchCapturingEvent(event);
                if (event.isConsumed()) {
                    return null;
                }
            }
            event = tail.dispatchEvent(event);
            if (event != null && !interceptor.test(event)) {
                event = original.dispatchBubblingEvent(event);
                if (event.isConsumed()) {
                    return null;
                }
            }
            return event;
        }

    }

    private Parent createContent() {
        TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(Locale.getAvailableLocales()));
        // just to see that right/left are intercepted while up/down are handled
        table.getSelectionModel().setCellSelectionEnabled(true);

        TableColumn<Locale,String> country = new TableColumn<>("Country");
        country.setCellValueFactory(new PropertyValueFactory<>("displayCountry"));
        TableColumn<Locale,String> language = new TableColumn<>("Language");
        language.setCellValueFactory(new PropertyValueFactory<>("displayLanguage"));
        table.getColumns().addAll(country,language);
        // disables default focus traversal
        //  table.setFocusTraversable(false);

        // decide which keys to intercept
        List<KeyCode> toIntercept = List.of(KeyCode.LEFT,KeyCode.RIGHT);
        Predicate<Event> interceptor = e -> {
            if (e instanceof KeyEvent) {
                return toIntercept.contains(((KeyEvent) e).getCode());
            }
            return false;
        };
        table.setEventDispatcher(new InterceptingEventDispatcher(
                (BasicEventDispatcher) table.getEventDispatcher(),interceptor));

        TextField textField = new TextField("something to show");
        textField.setPrefColumnCount(20);
        textField.setText("something to see");

        table.prefWidthProperty().bind(textField.widthProperty());
        Popup popUp = new Popup();
        popUp.getContent().add(table);

        textField.setOnKeyTyped(event -> {
            if(!popUp.isShowing()){
                popUp.show(
                        textField.getScene().getWindow(),textField.getScene().getWindow().getX()
                                + textField.localToScene(0,0).getX()
                                + textField.getScene().getX(),textField.getScene().getWindow().getY()
                                + textField.localToScene(0,0).getY()
                                + textField.getScene().getY()
                                + textField.getHeight() - 1);
            }
        });

        BorderPane content = new BorderPane(textField);
        return content;
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
本文链接:https://www.f2er.com/3058775.html

大家都在问