2016-04-14 82 views
1

我有多个控制器,每个控制器都关联到一个不同的FXML文件。在一个节点中有一个事件需要跨其他节点进行同步,因此我决定在另一个事件中执行此操作,并在各个控制器文件中执行事件处理程序。有没有从静态方法调用类方法的“最佳”方式?

要注册的事件处理程序需要事件处理方法是静态的(即addEventHandler(SomeEvent, ClassName::MethodName)

因此,控制器看起来像......

public class MyController { 
    private static MyController selfRef = null; 

    public MyController() { 
     selfRef = this; 
    } 

    public static void someEventHandler(Event event) { 
     if (selfRef != null) { 
      selfRef.doSomethingUseful(); 
     } 
    } 

    private void doSomethingUseful() { /* synch the nodes */ } 
} 

这工作,但似乎有点是否有一个首选的机制来实现相同的最终结果?

+0

它看起来python-y,但嘿,如果它的工作原理 – Norsk

+0

猜你只需要使单身人士 –

+1

你有没有考虑过Google Guava EventBus?它看起来很适合你的需求。 – vl4d1m1r4

回答

2

你可能有更多的灵活性,这一点,如果你摆脱所有静态的东西,使事件处理您的控制器类的成员,如下所示。

样品实现无静态成员

import javafx.event.*; 
import javafx.fxml.*; 
import javafx.scene.Scene; 
import javafx.scene.control.Label; 
import javafx.stage.*; 

import java.io.IOException; 

class CustomerDialogController { 
    @FXML 
    private Label customerName; 

    private EventHandler<Event> customEventHandler = event -> { 
     // handle the event... 
    }; 

    void initData(Customer customer) { 
     customerName.setText(customer.getName()); 
    } 

    public EventHandler<Event> getCustomEventHandler() { 
     return customEventHandler; 
    } 
} 

public class EventHandling { 
    public Stage showCustomerDialog(Customer customer) throws IOException { 
     FXMLLoader loader = new FXMLLoader(getClass().getResource("customerDialog.fxml")); 

     Stage stage = new Stage(StageStyle.DECORATED); 
     stage.setScene(new Scene(loader.load())); 

     CustomerDialogController controller = loader.getController(); 
     controller.initData(customer); 

     stage.addEventHandler(Event.ANY, controller.getCustomEventHandler()); 
     stage.show(); 

     return stage; 
    }  
} 

class Customer { 
    private String name; 

    Customer(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 
} 

注意事项实施方案

在事件处理程序已被添加到舞台上的例子,但它同样可以被添加到任何场景或节点或任何能够处理事件的功能。

如果需要,还可以为事件处理程序添加setter以允许从外部更改事件处理逻辑。

除了上面的设置,您可能希望让控制器在其初始化方法中自行注册事件处理程序。无论您是否这样做,取决于您是否希望能够注册暴露在控制器之外的事件处理程序,或者是否希望使用封装来隐藏控制器本地的所有事件处理逻辑。上(也许是上级)的替代品

作为一种替代方法,而不是使用事件中的JavaFX处理系统为您定制的方法

注意,你可以使用第三方系统如Google Guava Event Bus

您还应该考虑为什么您需要将自定义事件处理添加到您的应用程序。 JavaFX支持非常灵活的绑定和观察者模式。通过将模型对象的属性公开为可观察的,通常不需要定制事件。通常,视图控制器可以观察对相关模型对象的任何更改,并根据UI交互来修改模型对象的内部状态。如果您引入基于依赖注入的系统以将模型注入您的控制器,例如Guice,Spring,afterburner.fxGluon Ignite,则尤其如此。

1

也许你可以使用某种注册表,它负责同步。下面是一个快速和肮脏的例子:

public class Synchronizer { 

      private ObservableList<Node> nodes; 
      private boolean    isSyncing; 

      public Synchronizer() { 
       nodes = FXCollections.observableArrayList(); 
      } 

      public void addNode(Node node) { 
       nodes.add(node); 
      } 

      public void sync(Node sourceNode, Event event) { 
       if (isSyncing) { 
        return; 
       } 

       isSyncing = true; 
       for (Node node : nodes) { 
        if (node != sourceNode) { 
         node.fireEvent(event); 
        } 
       } 
       isSyncing = false; 
      } 
     } 

在您的控制器中,您可以将您想要同步的事件添加到同步器,并在eventListener中调用sync()

public class Controller { 

     private StackPane root; 
     private Button button; 

     public Controller(Synchronizer synchronizer) { 
      button = new Button(); 
      button.setOnAction(evt -> { 
       synchronizer.sync(button, evt); 
       //action 
      }); 
      synchronizer.addNode(button); 

      root = new StackPane(button); 
     } 
} 

编辑:

这应该为一个更清洁的版本:

public class Starter extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     ViewController controller1 = new ViewController(); 
     ViewController controller2 = new ViewController(); 

     Synchronizer synchronizer = new Synchronizer(); 
     synchronizer.add(controller1); 
     synchronizer.add(controller2); 

     VBox box = new VBox(controller1.root, controller2.root); 
     primaryStage.setScene(new Scene(box)); 
     primaryStage.show(); 
    } 

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

    public interface SyncTarget { 

     Node getSyncNode(); 

     void triggerAction(); 

    } 

public class Synchronizer { 

    private ObservableList<SyncTarget> syncTargets; 

    private EventHandler<Event>  eventHandler; 

    public Synchronizer() { 
     syncTargets = FXCollections.observableArrayList(); 
     eventHandler = e -> sync(); 

    } 

    public void add(SyncTarget target) { 
     syncTargets.add(target); 
     target.getSyncNode().addEventHandler(ActionEvent.ANY, eventHandler); 
    } 

    public void remove(SyncTarget target) { 
     syncTargets.remove(target); 
     target.getSyncNode().removeEventHandler(ActionEvent.ANY, eventHandler); 
    } 

    public void sync() { 
     for (SyncTarget target : syncTargets) { 
      target.triggerAction(); 
     } 
    } 
} 


    public class ViewController implements SyncTarget { 

     private StackPane root; 
     private Button button; 

     public ViewController() { 
      button = new Button(); 
      root = new StackPane(button); 
     } 

     @Override 
     public Node getSyncNode() { 
      return button; 
     } 


     @Override 
     public void triggerAction() { 
      //action 
     } 
    } 
} 
+0

“...通过将模型对象的属性公开为可观察的,通常不需要定制事件。通常,视图控制器可以观察对相关模型对象的任何更改,并根据UI交互来修改模型对象的内部状态。“ 我喜欢这个选项。我不是一个真正的图形用户界面的人,这很适合我所熟悉的开发风格。也许如果我做更多的GUI,那么我会深入JavaFX,但我喜欢从底层模型驱动所有控制器行为的概念。 – SoCal

相关问题