Search code examples
javajavafxeventhandleractionevent

JavaFX: Help setting event listeners to set variables and print text?


I am writing a program that is a "sandwich ordering app". Think subway. Bread choice, meat choice, cheese choice, etc. I'm trying to use each type of element (radio buttons, combobox, slider, listview, checkbox, and textfield). I am having trouble with some of the listeners. I'm sure there's more than one way to do this, but I am getting nowhere. You'll see in my code that the commented out sections are the sections that are not working for me.

package sandwichorderapp;

import java.util.List;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Slider;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

/**images go in bin folder in files
 *
 * @author toril
 */
public class SandwichOrderApp extends Application {
    
    public static int sandwichSize;
    public static String breadChoice;
    private final static String[] meats = {"Turkey", "Ham", "Steak", 
            "Chicken", "Bacon", "Pastrami"};
    private static String meatChoice;
    private static String cheeseChoice;
    //private static List<String> chosenToppings;
    private final static String[] sauces = {"Mayo", "Mustard", "Barbeque",
            "Ranch", "Buffalo"};
    //private static List<String> saucesChoice;
    //private static String saltPepperChoice;
    
    //method that returns a borderpane
    protected GridPane getPane(){

        //slider for sub size
        Slider sizeSlider = new Slider(6, 18, 12);
        sizeSlider.setShowTickLabels(true);
        sizeSlider.setShowTickMarks(true);
        sizeSlider.setBlockIncrement(6);
        //label the slider
        Label sliderLbl = new Label("Sandwich Size(in):", sizeSlider);
        sliderLbl.setContentDisplay(ContentDisplay.BOTTOM);
        sliderLbl.setUnderline(true);
        
        //listener for slider
        sizeSlider.valueProperty().addListener(e -> 
            sandwichSize = (int)sizeSlider.getValue());

        
        
        //create vbox for radio buttons and label
        VBox breadRadioButtons = new VBox();
        //text to label radio buttons:
        Text breadTxt = new Text("Bread Choice:");
        breadTxt.setUnderline(true);
                
        //set padding
        breadRadioButtons.setPadding(new Insets(5, 5, 5, 5));
        //create RadioButtons
        RadioButton whiteRB = new RadioButton("White");
        RadioButton wheatRB = new RadioButton("Wheat");
        RadioButton ryeRB = new RadioButton("Rye");
        RadioButton sourdRB = new RadioButton("Sourdough");
        //add RadioButtons to vbox
        breadRadioButtons.getChildren().addAll(breadTxt, whiteRB, wheatRB, ryeRB, sourdRB);
        breadRadioButtons.setAlignment(Pos.CENTER);
        
        //group the radio buttons
        final ToggleGroup breadGroup = new ToggleGroup();
        whiteRB.setToggleGroup(breadGroup);
        wheatRB.setToggleGroup(breadGroup);
        ryeRB.setToggleGroup(breadGroup);
        sourdRB.setToggleGroup(breadGroup);
        
        //set event handler for bread radiogroup
        whiteRB.setOnAction(e ->{
            if(whiteRB.isSelected()){
                breadChoice = "white";
            }
        });
        wheatRB.setOnAction(e -> {
            if(wheatRB.isSelected()){
                breadChoice = "wheat";
            }
        });
        ryeRB.setOnAction(e ->{
            if(ryeRB.isSelected()){
                breadChoice = "rye";
            }
        });
        sourdRB.setOnAction(e ->{
            if(sourdRB.isSelected()){
                breadChoice = "sourdough";
            }
        });
        
        
        
        //meat choices, more listview (can choose only one)
        //must call FXCollections because listview wants and arraylist, ot just an array
        ListView<String> meatsLV = new ListView<>(FXCollections.observableArrayList(meats));
        meatsLV.setPrefSize(80, 140);
        meatsLV.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        Label meatsLbl = new Label("Meat Choice:", meatsLV);
        meatsLbl.setContentDisplay(ContentDisplay.BOTTOM);
        meatsLbl.setUnderline(true);
        
        //listener for meats list view
        meatsLV.getSelectionModel().selectedItemProperty().addListener(
                (ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            meatChoice = newValue;
        });
        
        
        
        //choose cheese (only one, so I used comboBox)
        ComboBox<String> cheeseCB = new ComboBox<>();
        cheeseCB.getItems().addAll("Choose", "Cheddar", "Swiss", "Provolone", 
                "Pepper Jack", "American");
        cheeseCB.setValue("Choose");  //DEFAULT VALUE
        Label cheeseLbl = new Label("Cheese Choice:", cheeseCB);
        cheeseLbl.setContentDisplay(ContentDisplay.BOTTOM);
        cheeseLbl.setUnderline(true);

        //event handler for sandwich size combobox
        cheeseCB.getSelectionModel().selectedItemProperty().addListener((options, oldValue, newValue) ->{
            cheeseChoice = newValue;
        });
        
        
        
        //checkboxes for toppings
        VBox toppingsVB = new VBox();
        toppingsVB.setPadding(new Insets(5, 5, 5, 5));
        CheckBox chkLettuce = new CheckBox("Lettuce");
        CheckBox chkTomato = new CheckBox("Tomato");
        CheckBox chkCucumber = new CheckBox("Cucumber");
        toppingsVB.getChildren().addAll(chkLettuce, chkTomato,
                chkCucumber);
        Label toppingsLbl = new Label("Topping Choice:", toppingsVB);
        toppingsLbl.setContentDisplay(ContentDisplay.BOTTOM);
        toppingsLbl.setUnderline(true);
        
        //listeners for toppings
//        EventHandler toppingsHandler = new EventHandler<ActionEvent>() {
//            @Override
//            public void handle(ActionEvent event){
//                if(event.getSource() instanceof CheckBox) {
//                    CheckBox chb = (CheckBox) event.getSource();
//                    chosenToppings.add(chb.getText());
//                }
//            }
//        };
//        chkLettuce.setOnAction(toppingsHandler);
//        chkTomato.setOnAction(toppingsHandler);
//        chkCucumber.setOnAction(toppingsHandler);

        //Sauce choice
        ListView<String> saucesLV = new ListView<>(FXCollections.observableArrayList(sauces));
        saucesLV.setPrefSize(80, 117);
        saucesLV.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        Label saucesLbl = new Label("Sauce Choice:", saucesLV);
        saucesLbl.setContentDisplay(ContentDisplay.BOTTOM);
        saucesLbl.setUnderline(true);
        
        //listener for sauces list view
//        saucesLV.getSelectionModel().selectedItemProperty().addListener(
//                (ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
//            saucesChoice.
//        });
        
        //salt and pepper Y/N
        TextField tfSaltPepper = new TextField();
        Label saltPepperLbl = new Label("Salt and Pepper? (Y/N)", tfSaltPepper);
        saltPepperLbl.setContentDisplay(ContentDisplay.BOTTOM);
        saltPepperLbl.setUnderline(true);
        tfSaltPepper.setEditable(true);
        tfSaltPepper.setPrefWidth(30);
        
//        if("Y".equals(tfSaltPepper.getText()) || "y".equals(tfSaltPepper.getText())){
//        //listener for textfield
//            tfSaltPepper.setOnAction(e -> saltPepperChoice = "add salt and pepper");
//        }
//        else if("N".equals(tfSaltPepper.getText()) || "n".equals(tfSaltPepper.getText())){
//            tfSaltPepper.setOnAction(e -> saltPepperChoice = "no salt and pepper");
//        }
//        
        
        //textview to enter users choices after pressing button
        TextArea editTA = new TextArea();
        editTA.setVisible(false);
        editTA.setWrapText(true);
        
        
        //button to place order
        Button button = new Button("Order");
        
        button.setOnAction(e ->{
            editTA.setVisible(true);
            editTA.setText("Your Order:\nA " + sandwichSize + "in " + 
                    breadChoice + " bread sandwich with " + meatChoice + ", " +
                    cheeseChoice + " cheese, " + "<enter chosen toppings>," + 
                    " <enter chosen sauces>, " + "<enter salt/pepper choice>");
            
        
        });
    
        
        
        
        
        
        GridPane pane = new GridPane();
        //vbox to hold left of borderpane
        VBox leftVB = new VBox();
        //add both nodes to vbox and vbox to gridpane
        leftVB.getChildren().addAll(sliderLbl, breadRadioButtons, meatsLbl);
        VBox.setMargin(sliderLbl, new Insets(30,10,10,10));
        VBox.setMargin(breadRadioButtons, new Insets(10, 10, 10, 5));
        VBox.setMargin(breadTxt, new Insets(0, 0, 5, 0));
        VBox.setMargin(meatsLbl, new Insets(10, 10, 10, 35));
        
        //vbox for other nodes
        VBox rightVB = new VBox();
        //add nodes to vbox, then to gridpane
        rightVB.getChildren().addAll(cheeseLbl, toppingsLbl, saucesLbl, saltPepperLbl, button);
        VBox.setMargin(cheeseLbl, new Insets(30, 10, 10, 10));
        VBox.setMargin(toppingsLbl, new Insets(10, 10, 10, 20));
        VBox.setMargin(saucesLbl, new Insets(10, 10, 10, 20));
        VBox.setMargin(button, new Insets(20, 10, 10, 60));
        
        //HBox to hold nodes in an orderly way
        HBox hbox = new HBox();
        hbox.getChildren().addAll(leftVB, rightVB);
        
        pane.add(hbox, 0, 0);
        pane.add(editTA, 0, 1, 2, 2);
        
        return pane;
    }
    
    
    @Override
    public void start(Stage primaryStage) {
        //variables to hold users choices
        
        Scene scene = new Scene(getPane(), 300, 600);
        
        primaryStage.setTitle("Sandwich Order");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
    
}


Solution

  • I think below are the main problems you are encountering with:

    • Choosing sauces from multi selection : You can add listener to the getSelectedItems() of selection model to build the list.

    • Getting the text for salt & pepper : You can add listener to the textProperty of the textfield to build the text

    • Building list for toppings : What you had is almost correct. You can add or remove the topping based on the checkbox selection.

    Below is the updated code of your example. Please note that there can be many different ways to fix this. This is my attempt to fix this without going much into detail.

    import javafx.application.Application;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ListChangeListener;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Text;
    import javafx.stage.Stage;
    
    import java.util.*;
    
    /**
     * images go in bin folder in files
     *
     * @author toril
     */
    public class SandwichOrderApp extends Application {
    
        public static int sandwichSize;
        public static String breadChoice;
        private final static String[] meats = {"Turkey", "Ham", "Steak", "Chicken", "Bacon", "Pastrami"};
        private static String meatChoice = "no meat";
        private static String cheeseChoice = "no";
        private static Set<String> chosenToppings = new HashSet<>();
        private static List<String> chosenSauces = new ArrayList<>();
        private final static String[] sauces = {"Mayo", "Mustard", "Barbeque", "Ranch", "Buffalo"};
    
        private String saltPepperChoice = "no salt and pepper";
    
        //method that returns a borderpane
        protected GridPane getPane() {
    
            //slider for sub size
            Slider sizeSlider = new Slider(6, 18, 12);
            sizeSlider.setShowTickLabels(true);
            sizeSlider.setShowTickMarks(true);
            sizeSlider.setBlockIncrement(6);
            //label the slider
            Label sliderLbl = new Label("Sandwich Size(in):", sizeSlider);
            sliderLbl.setContentDisplay(ContentDisplay.BOTTOM);
            sliderLbl.setUnderline(true);
    
            //listener for slider
            sizeSlider.valueProperty().addListener(e -> sandwichSize = (int) sizeSlider.getValue());
            sandwichSize = (int) sizeSlider.getValue();
    
            //text to label radio buttons:
            Text breadTxt = new Text("Bread Choice:");
            breadTxt.setUnderline(true);
    
            //create RadioButtons
            RadioButton whiteRB = new RadioButton("White");
            whiteRB.setId("white");
            whiteRB.setSelected(true);
            RadioButton wheatRB = new RadioButton("Wheat");
            wheatRB.setId("wheat");
            RadioButton ryeRB = new RadioButton("Rye");
            ryeRB.setId("rye");
            RadioButton sourdRB = new RadioButton("Sourdough");
            sourdRB.setId("sourdough");
    
            VBox breadRadioButtons = new VBox();
            breadRadioButtons.setPadding(new Insets(5, 5, 5, 5));
            breadRadioButtons.getChildren().addAll(breadTxt, whiteRB, wheatRB, ryeRB, sourdRB);
            breadRadioButtons.setAlignment(Pos.CENTER_LEFT);
    
            //group the radio buttons
            final ToggleGroup breadGroup = new ToggleGroup();
            breadGroup.selectedToggleProperty().addListener((obs, old, val) -> breadChoice = ((Node) val).getId());
            whiteRB.setToggleGroup(breadGroup);
            wheatRB.setToggleGroup(breadGroup);
            ryeRB.setToggleGroup(breadGroup);
            sourdRB.setToggleGroup(breadGroup);
    
            //meat choices, more listview (can choose only one)
            //must call FXCollections because listview wants and arraylist, ot just an array
            ListView<String> meatsLV = new ListView<>(FXCollections.observableArrayList(meats));
            meatsLV.setPrefSize(80, 140);
            meatsLV.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
            Label meatsLbl = new Label("Meat Choice:", meatsLV);
            meatsLbl.setContentDisplay(ContentDisplay.BOTTOM);
            meatsLbl.setUnderline(true);
    
            //listener for meats list view
            meatsLV.getSelectionModel().selectedItemProperty().addListener(
                    (ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
                        meatChoice = newValue != null ? newValue : "no meat";
                    });
    
    
            //choose cheese (only one, so I used comboBox)
            ComboBox<String> cheeseCB = new ComboBox<>();
            cheeseCB.getItems().addAll("Choose", "Cheddar", "Swiss", "Provolone",
                    "Pepper Jack", "American");
            cheeseCB.setValue("Choose");  //DEFAULT VALUE
            Label cheeseLbl = new Label("Cheese Choice:", cheeseCB);
            cheeseLbl.setContentDisplay(ContentDisplay.BOTTOM);
            cheeseLbl.setUnderline(true);
    
            //event handler for sandwich size combobox
            cheeseCB.getSelectionModel().selectedItemProperty().addListener((options, oldValue, newValue) -> cheeseChoice = newValue != null ? newValue : "no");
    
    
            //checkboxes for toppings
            VBox toppingsVB = new VBox();
            toppingsVB.setPadding(new Insets(5, 5, 5, 5));
            CheckBox chkLettuce = new CheckBox("Lettuce");
            CheckBox chkTomato = new CheckBox("Tomato");
            CheckBox chkCucumber = new CheckBox("Cucumber");
            toppingsVB.getChildren().addAll(chkLettuce, chkTomato, chkCucumber);
            Label toppingsLbl = new Label("Topping Choice:", toppingsVB);
            toppingsLbl.setContentDisplay(ContentDisplay.BOTTOM);
            toppingsLbl.setUnderline(true);
    
            //listeners for toppings
            EventHandler<ActionEvent> toppingsHandler = event -> {
                if (event.getSource() instanceof CheckBox) {
                    CheckBox chb = (CheckBox) event.getSource();
                    if (chb.isSelected()) {
                        chosenToppings.add(chb.getText());
                    } else {
                        chosenToppings.remove(chb.getText());
                    }
                }
            };
            chkLettuce.setOnAction(toppingsHandler);
            chkTomato.setOnAction(toppingsHandler);
            chkCucumber.setOnAction(toppingsHandler);
    
            //Sauce choice
            ListView<String> saucesLV = new ListView<>(FXCollections.observableArrayList(sauces));
            saucesLV.setPrefSize(80, 117);
            saucesLV.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
            Label saucesLbl = new Label("Sauce Choice:", saucesLV);
            saucesLbl.setContentDisplay(ContentDisplay.BOTTOM);
            saucesLbl.setUnderline(true);
    
            //listener for sauces list view
            saucesLV.getSelectionModel().getSelectedItems().addListener((ListChangeListener<? super String>) p -> {
                chosenSauces.clear();
                chosenSauces.addAll(saucesLV.getSelectionModel().getSelectedItems());
            });
    
            //salt and pepper Y/N
            TextField tfSaltPepper = new TextField();
            tfSaltPepper.textProperty().addListener((obs, old, val) -> {
                if ("y".equalsIgnoreCase(val)) {
                    saltPepperChoice = "add salt and pepper";
                } else {
                    saltPepperChoice = "no salt and pepper";
                }
            });
            Label saltPepperLbl = new Label("Salt and Pepper? (Y/N)", tfSaltPepper);
            saltPepperLbl.setContentDisplay(ContentDisplay.BOTTOM);
            saltPepperLbl.setUnderline(true);
            tfSaltPepper.setEditable(true);
            tfSaltPepper.setPrefWidth(30);
    
            //textview to enter users choices after pressing button
            TextArea editTA = new TextArea();
            editTA.setVisible(false);
            editTA.setWrapText(true);
    
    
            //button to place order
            Button button = new Button("Order");
    
            button.setOnAction(e -> {
                editTA.setVisible(true);
                editTA.setText("Your Order:\nA " + sandwichSize + "in " +
                        breadChoice + " bread sandwich with " + meatChoice + ", " +
                        cheeseChoice + " cheese, " + buildString(chosenToppings) + " toppings, " + buildString(chosenSauces) +
                        " sauces, " + saltPepperChoice);
            });
    
    
            GridPane pane = new GridPane();
            //vbox to hold left of borderpane
            VBox leftVB = new VBox();
            //add both nodes to vbox and vbox to gridpane
            leftVB.getChildren().addAll(sliderLbl, breadRadioButtons, meatsLbl);
            VBox.setMargin(sliderLbl, new Insets(30, 10, 10, 10));
            VBox.setMargin(breadRadioButtons, new Insets(10, 10, 10, 5));
            VBox.setMargin(breadTxt, new Insets(0, 0, 5, 0));
            VBox.setMargin(meatsLbl, new Insets(10, 10, 10, 35));
    
            //vbox for other nodes
            VBox rightVB = new VBox();
            //add nodes to vbox, then to gridpane
            rightVB.getChildren().addAll(cheeseLbl, toppingsLbl, saucesLbl, saltPepperLbl, button);
            VBox.setMargin(cheeseLbl, new Insets(30, 10, 10, 10));
            VBox.setMargin(toppingsLbl, new Insets(10, 10, 10, 20));
            VBox.setMargin(saucesLbl, new Insets(10, 10, 10, 20));
            VBox.setMargin(button, new Insets(20, 10, 10, 60));
    
            //HBox to hold nodes in an orderly way
            HBox hbox = new HBox();
            hbox.getChildren().addAll(leftVB, rightVB);
    
            pane.add(hbox, 0, 0);
            pane.add(editTA, 0, 1, 2, 2);
    
            return pane;
        }
    
        private String buildString(Collection<String> collection) {
            String str = "";
            if (collection.isEmpty()) {
                str = "no ";
            } else {
                for (String top : collection) {
                    str = str + top + ",";
                }
                str = str.substring(0, str.length() - 1) + " ";
            }
            return str;
        }
    
        @Override
        public void start(Stage primaryStage) {
            Scene scene = new Scene(getPane(), 300, 600);
            primaryStage.setTitle("Sandwich Order");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            launch(args);
        }
    }