CompEng
CompEng

Reputation: 55

How to manage multiple buttons with one event handler method in Javafx and Scene Builder

I have 14 pairs of Buttons associated with two groups of textFields that increment and decrement their associated textFields by 2. Each text field displays how many pairs of plates of specific weights are to be loaded onto a barbell. They are stored in two 2-dimensional arrays, one for pounds and the other for kilograms. [increment or decrement][buttons]

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

I'm using Scene Builder and JavaFX, and I'm trying to avoid writing 28 @FXML event handlers. This is the method I've come up with so far to iterate through each of the arrays, but I'm not sure how I can replace the lambda expressions to make this work.

// 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);
                });
            }

        }
    }
}

I then have the event handlers call this method with the relevant indices.

// 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;
    }
}

Upvotes: 1

Views: 3032

Answers (2)

CompEng
CompEng

Reputation: 55

After some experimentation, I came up with a solution. Keep in mind, this is my attempt at sticking to MVC architecture using Scene Builder, FXML, and JavaFX.

There are 14 different plates, 7 in lbs and 7 in kgs. Each plate has a text field to display how many of each are needed to be loaded on a barbell for a given weight. These textFields are stored in ObservableLists and are given listeners elsewhere in the controller.

// 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();

These arrays store the number of plates.

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

These ArrayLists store the increment buttons

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();

Since I used Scene Builder to design the GUI, each button requires @FXML tags to identify them. I don't think there is a way around this, so there are 28 of these in the controller.

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

Then, to avoid writing 28 handlers, I wrote 4 handlers associated with each 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);
}

And one more method to increment or decrement the correct textField.

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;
    }
}

Here's an image of the program so far. Each text field dynamically responds to changes. The barbell on the right displays kilogram plates in response to changes in the textFields.

Barbell Math

Upvotes: 1

c0der
c0der

Reputation: 18812

The following code demonstrates how to have group of (2) buttons incrementing the value of a text field:

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);}
}

Do not hesitate to ask for clarifications as needed. For more help, post MCVE

Upvotes: 0

Related Questions