【问题标题】:Callbacks in GUI Design in JavaFXJavaFX 中 GUI 设计中的回调
【发布时间】:2020-02-28 02:48:44
【问题描述】:

我正在尝试在 JavaFX 中设计一组相关的类。

我有一个 Board 类的对象,它创建了 Box 类的一些对象。 在 Box 类中,我创建了一些 Button 类的对象。

当我点击一个Button时,我想要执行Box的方法触发器。

在Box的触发方法里面,我想要Board的方法计算的执行。

如果计算方法返回为真,则按钮改变颜色,否则包含按钮的Box改变颜色。

我必须实现的第一个想法是使用某种从 Board 传递到 Box 以及从 Box 传递到 Button 的回调。

然后我有另一个需求:对另一个函数重复这个机制(如果按钮是用右键按下的,如果Board的calculation_shape方法返回true,则按钮变成圆形,否则Box变成圆形)。 所以我添加了另一组回调。

无论如何,这在我看来是一种代码味道,因为要添加第二个功能,我修改了所有接口和所有类。

在 GUI 领域还有其他方法吗?

谢谢

这里是 MRE。这种行为很愚蠢,但我的问题是:

1) 这是在 GUI 元素之间进行通信的正确方式吗?

2)如果我需要在孩子和父母之间增加更多的交流接口怎么办?我会有很长的构造函数,有很多接口......在我看来就像代码味道......

public interface Triggerable {
    boolean trigger(int size);
}


public class MyButton extends Button {

    Triggerable method;
    int buttonSize;
    String buttonName;

    public MyButton(String name, int size, Triggerable t) {
        super(name);
        buttonName = name;
        this.setMinSize(100, 30);
        this.setOnMouseClicked(e -> MouseClickedAction(e));
        buttonSize = size;
        method = t;
    }

    void MouseClickedAction(MouseEvent e) {
        if(method.trigger(buttonSize) == true ) {
            System.out.println(buttonName + " triggered.");
        }
    }
}

public interface Calculable {
    boolean calculate(int totalSize);
}


public class Box extends VBox {

    String boxName;
    int numberOfButtons = 3;
    int boxSize;
    Calculable method;

    public Box (String name, int size, Calculable m) {
        boxName = name;
        for ( int i = 0; i < numberOfButtons; i++ ) {
            this.getChildren().add(new MyButton("Button" + i  + boxName , i, block -> trigger(block)));
        }
        boxSize = size;
        method = m;

    }

    public boolean trigger(int buttonSize) {
        if(method.calculate(boxSize+buttonSize) == true) {
            System.out.println( boxName + " triggered.");
            return false;
        } else {
            return true;
        }

    }
}

public class Board extends HBox {

    String boardName;
    int numberOfBoxes = 3;
    int boardThreshold = 2;

    public Board (String name) {
        boardName = name;
        for ( int i = 0; i < numberOfBoxes; i++ ) {
            this.getChildren().add(new Box("Box" + i , i, block -> calculate(block) ));
        }
    }

    public boolean calculate(int totalSize) {
        if(totalSize > boardThreshold) {
            return true;
        } else {
            return false;
        }
    }
}

【问题讨论】:

  • 修改了帖子以添加最小的可重现示例。

标签: user-interface javafx


【解决方案1】:

这些子类看起来真的是多余的。而不是

    for ( int i = 0; i < numberOfButtons; i++ ) {
        this.getChildren().add(new MyButton("Button" + i  + boxName , i, block -> trigger(block)));
    }

你可以这样做,例如

    for (int i = 0 ; i < numberOfButtons ; i++) {
        final int size = i ;
        Button button = new Button("Button" + i + boxName);
        button.setOnAction(e -> trigger(size));
        this.getChildren().add(button);
    }

您的触发方法可以接收按钮作为参数,以更改其颜色,或者您可以在某处创建一个可观察的属性,并观察它以更改按钮的颜色。在现实生活中,您的可观察属性将成为您的数据模型的一部分,但在概念上类似于:

    for (int i = 0 ; i < numberOfButtons ; i++) {
        this.getChildren().add(createButton(i));
    }

private Button createButton(int size) {
    Button button = new Button("Button " + i + boxName);
    BooleanProperty someState = new SimpleBooleanProperty();
    someState.addListener((obs, wasInSomeState, isNowInSomeState) -> {
        if (isNowInSomeState) {
            button.setStyle("-fx-background-color: red;");
        } else {
            button.setStyle("");
        }
    );
    button.setOnAction(e -> someState.set(trigger(size)));
    return button ;
}

如果您更全局地需要此类按钮,只需将该创建方法移至工厂类,而不是继承 Button

一般来说,这只是看起来过度设计。尝试 1. 避免过度继承(在适当的情况下对对象使用聚合或工厂方法),以及 2. 不要复制标准 API(您的 TriggerableCalculable 接口只是 IntPredicate 的复制品,即便如此,您的示例并没有真正证明需要使用它)。

【讨论】:

  • 第一件事:非常感谢您的帮助,我明白了您的意思。通过使用 observables,我可以更好地意识到我的行为。另一点是:我如何从创建者类 (Box) 中观察已创建类 (Button) 的属性。所以你建议我使用工厂。我的目标是更高层次的层次结构需要观察创建对象的一些属性。为什么你更喜欢工厂方法而不是子类化?
  • @Pich 您的子类似乎只是为了将属性(包括侦听器和观察者)设置为特定值而存在。子类通常应该添加或专门化行为,恕我直言。这是我的观点和我喜欢的风格,其他人可能不同意,但我的一般建议是明智地进行子类化。
  • 好的,谢谢。另一个问题是深入探讨创建者类所需的已创建类的可观察性问题:正确的方法是使用工厂?
  • @Pich 在不了解项目的更多细节的情况下很难明确回答。我总是遵循某种 MVC 设计,所以总是有一个可观察的数据模型。然后在我要创建任何类型的视图的任何地方,模型都是可用的,我可以用它注册侦听器以确保视图与模型同步。如果我需要从应用程序的不同部分创建相同的视图组件,我会考虑使用工厂来执行此操作,但这只可能发生在相当大规模的应用程序中。
猜你喜欢
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
  • 2011-07-25
  • 1970-01-01
  • 2011-06-18
相关资源
最近更新 更多