Обратные вызовы в GUI Дизайн в JavaFX - PullRequest
1 голос
/ 27 февраля 2020

Я пытаюсь создать набор связанных классов в JavaFX.

У меня есть объект класса Board, который создает некоторые объекты класса Box. Внутри класса Box я создаю несколько объектов класса Button.

Когда я нажимаю кнопку, я хочу выполнить триггер метода Box.

Внутри метода триггера Box, Я хочу, чтобы выполнение метода вычисления Board.

Если метод расчета возвращает значение true, тогда кнопка меняет свой цвет, иначе поле, содержащее кнопку, меняет свой цвет.

Первая идея, которую мне пришлось реализовать, - это использовать какой-то обратный вызов, передаваемый с платы на ящик и с ящика на кнопку.

Затем мне понадобилась еще одна необходимость: повторить этот механизм для другой функции ( если кнопка нажата правой кнопкой, если метод board_shape для Board возвращает 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;
        }
    }
}

1 Ответ

3 голосов
/ 27 февраля 2020

Эти подклассы действительно кажутся избыточными. Вместо

    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 (ваши интерфейсы Triggerable и Calculable являются просто копиями IntPredicate и даже тогда ваш пример на самом деле не демонстрирует необходимости его использования).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...