Reflection 将现有方法用作JavaFX事件处理程序

Reflection 将现有方法用作JavaFX事件处理程序,reflection,event-handling,javafx-2,Reflection,Event Handling,Javafx 2,在我能找到的几乎所有示例中,JavaFX事件处理程序都是作为匿名内部类创建的, 像这样: button.setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (event.getClickCount()>1) { System.out.println("double cli

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

button.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        if (event.getClickCount()>1) {
            System.out.println("double clicked!");    
        }
    }
});
button.setOnMouseClicked(新建EventHandler()){
@凌驾
公共无效句柄(MouseeEvent事件){
如果(event.getClickCount()>1){
System.out.println(“双击!”);
}
}
});
但是,我真的很讨厌匿名内部类(丑陋!!!),我也不想为每个事件处理程序创建单独的类。我希望像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;
    }
}
public最终静态事件处理程序createEventHandler(
最终类eventClass、最终对象handlerInstance、最终字符串handlerMethod){
试一试{
final-Method=handlerInstance.getClass().getMethod(handlerMethod,eventClass);
返回新的EventHandler(){
@凌驾
公共无效句柄(E事件){
试一试{
调用(handlerInstance,事件);
}捕获(IllegalAccessException | IllegalArgumentException
|调用目标异常(e){
e、 printStackTrace();
}
}
};
}catch(NoSuchMethodException | SecurityException e){
返回null;
}
}

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

建议的方法

使用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); }    
}
根据问题和评论部分的要点进行进一步讨论

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

使用时,它们并不那么丑陋:

button.setOnMouseClicked(event -> {
    if (event.getClickCount() > 1) {
        System.out.println("double clicked!");    
    }
});
我想使用现有的方法作为事件处理程序。正如FXMLLoader所做的那样

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

此外,如果您的UI是在FXML中定义的,那么您只需使用FXMLLoader中的现有功能即可获得所需的行为。(我猜您正在寻找一种不涉及FXML的解决方案)

有人有更好的主意吗

保留参考资料

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

private final EventHandler<MouseEvent> mouseHandler = event -> {
    if (event.getClickCount() > 1) {
        System.out.println("double clicked!");
    }
};
. . .
button.setOnMouseClicked(mouseHandler);
private final EventHandler mouseHandler=event->{
如果(event.getClickCount()>1){
System.out.println(“双击!”);
}
};
. . .
按钮。设置鼠标点击(鼠标手柄);
保留对处理程序的本地引用的一个优点是,它为您提供了一个引用,以便以后调用以前使用注册的处理程序

使用其他语言


如果您对其他语言持开放态度,那么在动态语言中实现动态方法调用会稍微干净一些。您可以看到如何处理FXML文档中的事件处理程序分配问题(我不认为它依赖于FXMLLoader,但相信它有自己的纯Ruby替换实现)。当然,您使用的是一种动态语言,这在您的情况下可能是可取的,也可能是不可取的。

我想补充一点,在JDK8中,您可以使用方法引用,因此将该方法命名为“myEventHandler(MouseeEvent)”,您可以编写按钮,但它基本上告诉我,我不能比我现有的解决方案做得更好。FXMLLoader显然做了同样的事情(获取方法的句柄并通过反射调用它)。顺便说一句,我不明白为什么很多人推荐JDK8和JavaFX8,它们还不是官方的,也不是测试以外的任何东西的候选。至于为什么要使用java8/JavaFX8,尽管它还没有正式发布……它增加了许多人们期待已久的特性。我选择javafx8是因为尽管它是beta/early access,但它比swing更容易使用,而且lambda和streams的引入太诱人了。实际的java8版本定于3月份发布,所以实际上运行得很好,只有一些小错误,主要是在第三方工具集中。@jewelsea:由于没有其他答案,我将接受你的答案。它提供了对该问题的全面评估。