【问题标题】:Use existing method as JavaFX event handler使用现有方法作为 JavaFX 事件处理程序
【发布时间】:2013-12-09 17:48:38
【问题描述】:

在我能找到的几乎所有示例中,JavaFX 事件处理程序都是作为匿名内部类创建的, 像这样:

button.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        if (event.getClickCount()>1) {
            System.out.println("double clicked!");    
        }
    }
});

但是,我真的很讨厌匿名内部类(丑陋!!!),而且我也不想为每个事件处理程序创建单独的类。我想像 FXMLLoader 那样使用现有方法作为事件处理程序。我的第一个想法是使用反射和泛型,这就是我想出的:

public final static <E extends Event> EventHandler<E> createEventHandler(
        final Class<E> eventClass, final Object handlerInstance, final String handlerMethod) {
    try {
        final Method method = handlerInstance.getClass().getMethod(handlerMethod, eventClass);
        return new EventHandler<E>() {
            @Override
            public void handle(E event) {
                try {
                    method.invoke(handlerInstance, event);
                } catch (IllegalAccessException | IllegalArgumentException
                        | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        };
    } catch (NoSuchMethodException | SecurityException e) {
        return null;
    }
}

您传递了所需的事件类、处理程序实例和将处理事件的方法名称,并返回所需的 EventHandler。它有效,但看起来不是很优雅。有人有更好的主意吗?

【问题讨论】:

    标签: reflection event-handling javafx-2


    【解决方案1】:

    建议的方法

    使用 Tom 关于 Java 8 方法引用的想法:

    public void countClick(MouseEvent event) {
        nClickProperty.set(nClickProperty.get() + 1);
    }
    . . . 
    label.setOnMouseClicked(this::countClick);
    

    可执行示例

    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.*;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.input.MouseEvent;
    import javafx.stage.Stage;
    
    public class ClickCounter extends Application {    
    
        private final IntegerProperty nClickProperty = new SimpleIntegerProperty(0);
    
        public void countClick(MouseEvent event) {
            nClickProperty.set(nClickProperty.get() + 1);
        }
    
        @Override
        public void start(Stage stage) throws Exception {
            Label label = new Label();
            label.setPrefSize(100, 50);
            label.setAlignment(Pos.CENTER);
    
            label.textProperty().bind(
                    Bindings.concat("Num clicks: ", nClickProperty.asString())
            );
    
            label.setOnMouseClicked(this::countClick);
    
            stage.setScene(new Scene(label));
            stage.show();
        }
    
        public static void main(String[] args) { launch(args); }    
    }
    

    根据问题和评论部分的观点进一步讨论

    我真的很讨厌匿名内部类(丑陋!!!)

    使用Java 8 lambdas instead,它们没有那么丑:

    button.setOnMouseClicked(event -> {
        if (event.getClickCount() > 1) {
            System.out.println("double clicked!");    
        }
    });
    

    我想使用现有方法作为事件处理程序。就像 FXMLLoader 一样。

    对于通用机制,您可以查看FXMLLoader 源代码并将事件处理机制提取到通用库中。

    另外,如果您的 UI 是在 FXML 中定义的,那么您可以只使用 FXMLLoader 中的现有功能来获得您想要的行为。 (我猜您正在寻找不涉及 FXML 的解决方案)。

    有人有更好的主意吗?

    保留引用

    将事件处理程序分配给引用。这种方法在某些情况下有用,但不一定美观。

    private final EventHandler<MouseEvent> mouseHandler = event -> {
        if (event.getClickCount() > 1) {
            System.out.println("double clicked!");
        }
    };
    . . .
    button.setOnMouseClicked(mouseHandler);
    

    保留对处理程序的本地引用的一个优点是,它为您提供了一个引用,以便稍后为以前使用 node.addEventHandler 注册的处理程序调用 node.removeEventHandler

    使用其他语言

    如果您对替代语言持开放态度,那么在动态语言中实现动态方法调用会更简洁一些。您可以看到JRubyFX 如何处理来自 FXML 文档的事件处理程序分配问题(我不认为它依赖于 FXMLLoader,但相信它有自己的纯 Ruby 替代实现)。当然,那么您使用的动态语言可能会或可能不适合您的情况。

    【讨论】:

    • 我想补充一点,在 JDK8 中你可以使用方法引用,所以给方法命名为“myEventHandler(MouseEvent)”你可以写 button.setOnMouseClicked(this::myEventHandler)
    • 信息量很大,但它基本上告诉我,我不能比我现有的解决方案做得更好。 FXMLLoader 显然做同样的事情(获取方法的句柄并通过反射调用它)。附带说明一下,我不明白为什么很多人推荐 JDK8 和 JavaFX8,它们还不是官方的,除了测试之外,它们并不是真正的候选对象。
    • 至于为什么要使用java8/javafx8,虽然它还没有正式发布……它增加了许多人们期待已久的特性。我选择了 javafx8,尽管它是 beta/early-access,但它比 swing 更容易使用,并且 lambda 和流的引入太诱人了。实际的 java8 版本定于 3 月发布,因此实际上运行良好,只有一些小错误,主要是在 3rd 方工具集中。
    • @jewelsea:由于显然没有其他答案,我会接受你的答案。它提供了对问题的全面评估。
    猜你喜欢
    • 1970-01-01
    • 2013-06-26
    • 2018-10-06
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-06
    • 1970-01-01
    相关资源
    最近更新 更多