Как управлять несколькими кнопками с помощью одного метода обработчика событий в Javafx и Scene Builder

У меня есть 14 пар кнопок, связанных с двумя группами текстовых полей, которые увеличивают и уменьшают связанные с ними текстовые поля на 2. В каждом текстовом поле отображается, сколько пар блинов определенного веса должно быть загружено на штангу. Они хранятся в двух двумерных массивах, один для фунтов, а другой для килограммов. [увеличить или уменьшить][кнопки]

final Button[][] poundIncrementDecrementButton = new Button[2][7];
final Button[][] kilogramIncrementDecrementButton = new Button[2][7];

Я использую Scene Builder и JavaFX и стараюсь не писать 28 обработчиков событий @FXML. Это метод, который я придумал до сих пор для перебора каждого из массивов, но я не уверен, как я могу заменить лямбда-выражения, чтобы это работало.

// assigns event handlers to increment and decrement buttons.
private void incrementDrecimentButtonEventHandlers() {

    // p=1 increment buttons, p=2 decrement buttons
    for (int p = 0; p < poundIncrementDecrementButton.length; p++) {
        // loops through buttons
        for (int j = 0; j < poundIncrementDecrementButton[p].length; j++) {
            Button incrementButton = poundIncrementDecrementButton[p][j];
            final int incrementDecriment = p;
            final int textField = j;
            incrementButton.setOnAction((ActionEvent event) -> {
                incrementDecrementPlate("pounds", incrementDecriment, textField);
            });
        }

        // k=1 increment buttons, k=2 decrement buttons
        for (int k = 0; k < kilogramIncrementDecrementButton.length; k++) {
            // loops through buttons
            for (int j = 0; j < kilogramIncrementDecrementButton[k].length; j++) {
                Button incrementButton = kilogramIncrementDecrementButton[k][j];
                final int incrementDecriment = k;
                final int textField = j;
                incrementButton.setOnAction((ActionEvent event) -> {
                    incrementDecrementPlate("kilograms", incrementDecriment, textField);
                });
            }

        }
    }
}

Затем я заставляю обработчики событий вызывать этот метод с соответствующими индексами.

// increments or decrements the plates
private void incrementDecrementPlate(String unit, int incrementDecrement, int textField) {

    Double oldValue = Double.parseDouble(poundTextFieldList.get(textField).getText());
    String incremented;
    String decremented;

    if (oldValue % 2 != 0) {
        incremented = Double.toString(oldValue + 1);
    } else {
        incremented = Double.toString(oldValue + 2);
    }

    if (oldValue % 2 != 0) {
        decremented = Double.toString(oldValue - 1);
    } else if (oldValue != 0) {
        decremented = Double.toString(oldValue - 2);
    } else {
        decremented = Double.toString(oldValue);
    }

    switch (unit) {
        case "pounds":
            if (incrementDecrement == 0) {
                poundTextFieldList.get(textField).setText(incremented);
            } else {
                poundTextFieldList.get(textField).setText(decremented);
            }
            break;
        case "kilograms":
            if (incrementDecrement == 0) {
                kilogramTextFieldList.get(textField).setText(incremented);
            } else {
                kilogramTextFieldList.get(textField).setText(decremented);
            }
            break;
    }
}

person CompEng    schedule 19.04.2018    source источник
comment
Это кажется большим количеством кода, чтобы сделать что-то, что, вероятно, можно было бы написать в меньшем количестве кода.   -  person Sedrick    schedule 19.04.2018
comment
Кажется, что было бы намного проще сделать эту часть макета пользовательского интерфейса и обработки событий полностью на Java - это очень мало дополнительного кода к коду, который вы уже опубликовали, чем иметь файл FXML с 28 (или 56?) <Button> элементов и 14 элементов текстового поля...   -  person James_D    schedule 19.04.2018
comment
Похоже, вы делаете это вручную, что возможно, но подход JavaFX будет лучшим в долгосрочной перспективе. Создавайте элементы управления пользовательским интерфейсом в коде, используйте контроллер, кнопки для установки свойств, текстовые поля для наблюдения за свойствами. Базовые вещи - посмотрите учебник по Oracle JavaFX   -  person Siraj K    schedule 19.04.2018
comment
@Siraj Это именно то, что я делаю. Весь этот код находится в моем классе контроллера. Я использовал Scene Builder для разработки пользовательского интерфейса. Я стараюсь максимально придерживаться MVC.   -  person CompEng    schedule 19.04.2018


Ответы (2)


После некоторых экспериментов я нашел решение. Имейте в виду, что это моя попытка придерживаться архитектуры MVC с использованием Scene Builder, FXML и JavaFX.

Есть 14 различных тарелок, 7 в фунтах и ​​7 в килограммах. Каждая пластина имеет текстовое поле для отображения того, сколько пластин необходимо загрузить на штангу для заданного веса. Эти текстовые поля хранятся в списках ObservableList и прослушиваются в другом месте контроллера.

// textFields associated with 45, 35, 25, 15, 10, 5, 2.5lb plates
ObservableList<TextField> poundTextFieldList = FXCollections.observableArrayList();
// textFields associated with 25, 20, 15, 10, 5, 2.5, 1.25kg plates
ObservableList<TextField> kilogramTextFieldList = FXCollections.observableArrayList();

Эти массивы хранят количество тарелок.

int[] poundPlatesToLoad = calculator.getPoundPlatesToLoad();
int[] kilogramPlatesToLoad = calculator.getKilogramPlatesToLoad();

Эти ArrayLists хранят кнопки увеличения

private final ArrayList<Button> poundIncrementButtons = new ArrayList();
private final ArrayList<Button> poundDecrementButtons = new ArrayList();
private final ArrayList<Button> kilogramIncrementButtons = new ArrayList();
private final ArrayList<Button> kilogramDecrementButtons = new ArrayList();

Поскольку я использовал Scene Builder для разработки графического интерфейса, каждая кнопка требует тегов @FXML для их идентификации. Я не думаю, что есть способ обойти это, поэтому в контроллере их 28.

@FXML
private Button poundIncrement45Button;
@FXML
private Button poundDecrement45Button;

Затем, чтобы не писать 28 обработчиков, я написал 4 обработчика, связанных с каждым ArrayList.

public void poundIncrementButtonPressed(ActionEvent event) {
    System.out.println("pound increment button pressed");
    Button button = (Button) event.getSource();
    // identifies which plate is to be incremented
    int plate = poundIncrementButtons.indexOf(button);
    incrementDecrementPlate(0, 0, plate);
}

И еще один способ увеличить или уменьшить правильное текстовое поле.

private void incrementDecrementPlate(int unit, int incrementDecrement, int textField) {

    int oldValue;
    if(unit == 0){
        oldValue = (int) poundPlatesToLoad[textField];
    } else {
        oldValue = (int) kilogramPlatesToLoad[textField];
    }

    String incremented;
    String decremented;

    if (oldValue % 2 != 0) {
        incremented = Integer.toString(oldValue + 1);
    } else {
        incremented = Integer.toString(oldValue + 2);
    }

    if (oldValue % 2 != 0) {
        decremented = Integer.toString(oldValue - 1);
    } else if (oldValue != 0) {
        decremented = Integer.toString(oldValue - 2);
    } else {
        decremented = Integer.toString(oldValue);
    }

    switch (unit) {
        case 0:
            if (incrementDecrement == 0) {
                poundTextFieldList.get(textField).setText(incremented);
            } else {
                poundTextFieldList.get(textField).setText(decremented);
            }
            break;
        case 1:
            if (incrementDecrement == 0) {
                kilogramTextFieldList.get(textField).setText(incremented);
            } else {
                kilogramTextFieldList.get(textField).setText(decremented);
            }
            break;
    }
}

Вот изображение программы на данный момент. Каждое текстовое поле динамически реагирует на изменения. Штанга справа отображает килограммовые пластины в ответ на изменения в текстовых полях.

Математика со штангой

person CompEng    schedule 19.04.2018
comment
SceneBuilder — это просто инструмент для создания файлов FXML, аналогичный написанию HTML или использованию визуального редактора. Вы можете установить в нем контроллер, чтобы он автоматически добавлял для вас поля @FXML. Кстати, ваш новый код можно сжать, но пока он работает... - person Siraj K; 20.04.2018
comment
Scene Builder пишет в файл FXML, но, насколько я понимаю, чтобы связать элементы из файла FXML с классом контроллера, нужно использовать теги @FXML в классе контроллера, чтобы работать с ними (кнопками, текстовыми полями) программно . И да, я согласен, я буду работать над его уплотнением. - person CompEng; 20.04.2018
comment
Я также знаю, что Scene Builder может генерировать скелетный класс контроллера, но он просто генерирует 28 отдельных обработчиков для кнопок, чего я и пытался избежать. - person CompEng; 20.04.2018

Следующий код демонстрирует, как иметь группу из (2) кнопок, увеличивающих значение текстового поля:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class FxMain extends Application {

    private Button[] increment, decrement;
    private TextField[] txt;
    private static final int numberOfGroups = 3;

    @Override
    public void start(Stage stage) {

        initComponents(numberOfGroups);
        GridPane root = new GridPane();
        for(int row =0; row < numberOfGroups; row++){ //add all components 
            root.addRow(row, decrement[row], txt[row],increment[row]);
        }
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    //create and initialize components 
    private void initComponents(int size) {
        increment = new Button[size]; decrement = new Button[size];
        txt = new TextField[size];

        for(int group = 0; group < size; group++) {
            final int i = group;
            txt[i] = new TextField(String.valueOf(0));
            increment[i] = new Button("+");
            increment[i].setOnAction(a-> updateTxtField(txt[i], 1));//assign handle 
            decrement[i] = new Button("-");
            decrement[i].setOnAction(a-> updateTxtField(txt[i],-1));
        }
    }
    //increment text filed value
    private void updateTxtField(TextField textField, int i) {

        int newValue = Integer.valueOf(textField.getText()) + i;
        textField.setText(String.valueOf(newValue));
    }

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

Не стесняйтесь обращаться за разъяснениями по мере необходимости. Для получения дополнительной помощи опубликуйте MCVE.

person c0der    schedule 19.04.2018
comment
Спасибо за ответ. Поскольку я использовал Scene Builder для создания графического интерфейса, я не уверен, как правильно связать обработчики событий с кнопками без тегов @FXML в моем классе контроллера? - person CompEng; 19.04.2018
comment
Для получения дополнительной помощи опубликуйте минимально воспроизводимый пример - person c0der; 19.04.2018