2013-12-09 3 views
4

Практически во всех примерах, которые я мог бы найти, обработчики JavaFX событий создаются как анонимные внутренние классы, так:Использовать существующий метод в качестве обработчика событий 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; 
    } 
} 

Вы пройти необходимый класс Event, экземпляр обработчика и имя метода, который будет обрабатывать событие, и требуется EventHandler возвращается. Он работает, но выглядит не очень элегантно. Кто-нибудь имеет лучшую идею?

ответ

8

Предлагаемый подход

идея Используйте Тома о 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); }  
} 

Futher обсуждение, основанное на точках вопроса и комментарии разделы

Я действительно ненавижу анонимные внутренние классы (некрасиво !!!)

Использование Java 8 lambdas instead, они не являются столь же некрасиво:

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

Я хотел бы использовать существующие методы как обработчики событий. как это делает FXMLLoader.

Для механизма общего назначения вы можете ознакомиться с исходным кодом FXMLLoader и извлечь только механизм обработки событий из общей библиотеки.

Кроме того, если ваш пользовательский интерфейс определен в FXML, вы можете просто использовать существующую функциональность в FXMLLoader, чтобы получить нужное поведение. (Я предполагаю, что вы ищете решение, которое не связано с FXML).

У кого-нибудь есть идея?

Ведение литературы

Назначение обработчика событий для справки. Этот подход хорош с точки зрения полезности в некоторых случаях, а не для красоты.

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

Одним из преимуществ сохранения локальной ссылки на обработчик является то, что он предоставляет вам ссылку позже вызывать node.removeEventHandler для обработчика, который ранее был зарегистрирован с помощью node.addEventHandler.

Использование альтернативных языков

Если вы открыты для альтернативных языков, то реализации динамического метода призванию немного чище в динамическом языке. Вы можете видеть, как JRubyFX рассматривает проблему присвоения обработчика события из документов FXML (я не думаю, что он полагается на FXMLLoader, но полагает, что он имеет чистую реализацию замены Ruby своей собственной). Конечно, тогда вы используете динамический язык, который может или не желателен в вашем случае.

+2

Я хотел бы добавить, что в JDK8 вы можете использовать ссылки на методы, поэтому дать метод называется «myEventHandler (MouseEvent)», вы можете написать button.setOnMouseClicked (this :: myEventHandler) – tomsontom

+0

Очень информативно, но в основном это говорит мне что я не могу сделать намного лучше, чем мое существующее решение. FXMLLoader, по-видимому, делает то же самое (получает дескриптор метода и вызывает его через отражение). В качестве побочного примечания я не понимаю, почему многие люди рекомендуют JDK8 и JavaFX8, которые пока не являются официальными и не являются кандидатами на что-либо, кроме тестирования. –

+1

Что касается использования java8/javafx8, хотя он еще официально не выпущен ... он добавляет много функций, которые люди долго ждали. Я выбрал javafx8 так, как будто это бета/ранний доступ, было гораздо проще в использовании, чем качели, а введение лямбда и потоков было слишком заманчивым. Фактический выпуск java8 намечен на март, поэтому на самом деле все работает очень хорошо с незначительными ошибками в основном на сторонних инструментах. – chooks

Смежные вопросы