Search code examples
javabuttonjavafxincrementdecrement

JavaFX Button Increment and Decrement Event Not Working Correctly


I'm working on a knitting application that counts rows and displays a row in a pattern.

The issue that I am currently having with it is with the counter and how it loops through the numbers. I need a counter to loop through the rows 0-3 and then reset itself at three, and I have the increment side of things done.

However, when I work on the decrement side and then clicking back and forth between the two buttons, the counter takes a bit to get to where it should be.

For example, when the counter is at 3, and I hit the minusButton, it goes back to 0 instead of 2 where it should be. If I was doing minusButton and switched over to the plusButton, then the counter goes back to 0.

I am not sure where I am going wrong with this, but any help is greatly appreciated.

Here is my code

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;


public class KnittingCounterApp extends Application{
    private String [] stitchNames = {"Blackberry Stitch","Pennant Sticht","Andalusian Stitch"};
    private String [] blackBerryRows = {"*(knit in the front, then back, then front again of the same stich, purl 3), repeat from * until end of the row"
                                       ,"*(purl 3 together, knit 3) repeat from * until the end of the row"
                                       ,"*(purl 3, knit in the front, then back, then front again of the same stitch), repeat from * until end of row"
                                       ,"*(knit 3, purl together 3), repeat until the end of the row"};
    private String [] pennantRows = {"knit"
                                    ,"purl 1, *(knit 1, purl 4) repeat from * until the last stitch, purl 1"
                                    ,"knit 1, *(knit 3, purl 2) repeat from * until the last stitch, knit 1"
                                    ,"knit 1, *(knit 3, purl 2) repeat from * until the last stitch, knit 1"};
    private String [] andalusianRows = {"knit"
                                       ,"purl"
                                       ,"knit 1, purl 1 repeast until the end of the row"
                                       ,"purl"};
    //int rowCounter = 0;

    //private String [] allRows = {blackBerryRows, pennantRows, andalusianRows};

    protected Text text = new Text();


    private ComboBox<String> stitchBox = new ComboBox<>();  

    @Override
    public void start(Stage primaryStage) {

        Label stitchLabel = new Label("Select Stich: ");
        RadioButton blackBerry = new RadioButton("Blackberry");
        RadioButton pennant = new RadioButton("Pennant");
        RadioButton andalusian = new RadioButton("Andalusian");
        blackBerry.setSelected(true);
        ToggleGroup stitchGroup = new ToggleGroup();
        blackBerry.setToggleGroup(stitchGroup);
        pennant.setToggleGroup(stitchGroup);
        andalusian.setToggleGroup(stitchGroup);
        VBox stitchBox = new VBox(stitchLabel, blackBerry, pennant,andalusian);
        stitchBox.setSpacing(10);

        Button plusButton = new Button("+");
        Button minusButton = new Button("-");
        HBox buttons = new HBox(20);
        buttons.getChildren().addAll(plusButton,minusButton);
        Label test = new Label();
        TextArea ta = new TextArea();
        AtomicInteger rowCounter = new AtomicInteger(0);
        plusButton.setOnAction(e->{ if(rowCounter.get() /3 == 1) {
                                        ta.setText(Integer.toString(rowCounter.get()));
                                        rowCounter.getAndSet(0);
                                    } else {
                                        ta.setText(Integer.toString(rowCounter.get()));
                                        rowCounter.updateAndGet(n->n+1);
                                    }
                                    });
        minusButton.setOnAction(e->{if(rowCounter.get() == 0) {
                                        ta.setText(Integer.toString(rowCounter.get()));
                                        rowCounter.getAndSet(3);
                                    } else {
                                        ta.setText(Integer.toString(rowCounter.get()));
                                        rowCounter.updateAndGet(n->n-1);
                                    }
                                   });
        VBox buttonBox = new VBox(10);
        buttonBox.getChildren().addAll(buttons,ta);

        BorderPane pane = new BorderPane();
        pane.setCenter(buttonBox);
        pane.setLeft(stitchBox);


        Scene scene = new Scene(pane, 550, 350);
        primaryStage.setTitle("Knit Baby Blanket Counter");
        primaryStage.setScene(scene);
        primaryStage.show();


    }


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

    }

}

Solution

  • There are 2 issues with your code

    1. Assuming you're using the AtomicInteger to do updates from multiple threads (as it is the purpose of this class; you don't seem to do that) you're not doing atomic operations, i.e. other threads could modify the value between you reading the value and a value depending on it being written.
    2. (The actual issue:) You're updating the GUI before you modify the value, i.e. you'll always see the results of the last modification, not the current one. Use the value after the update for the assignment to ta.text.

    The following code fixes both issues, but for simplicity's sake I'd recommend simply storing the value in a field instead:

    plusButton.setOnAction(e->{
        ta.setText(Integer.toString(rowCounter.updateAndGet(i -> i >= 3 ? 0 : i+1)));
    });
    minusButton.setOnAction(e->{
        ta.setText(Integer.toString(rowCounter.updateAndGet(i -> i <= 0 ? 3 : i-1)));
    });
    

    Alternative with a field:

    private int rowCounter = 0;
    
    ...
    
    plusButton.setOnAction(e -> {
        rowCounter = (rowCounter+1) % 4; // alternative using modulo here
        ta.setText(Integer.toString(rowCounter));
    });
    minusButton.setOnAction(e -> {
        rowCounter = (rowCounter + 3) % 4; // alternative using modulo
        ta.setText(Integer.toString(rowCounter));
    });