TEMPLATE The main steps in developing a GUI application in JavaFX include: 1. Create the nodes needed 2. Organize these nodes into a pane (provides control over node position, and size) 3. Create a scene with the pane 4. Set the scene on a stage 5. Display the stage public class AppName extends Application{ public void start(Stage primaryStage){ // Create a scene and place components on scene // Place scene on stage // Display stage } // Only necessary in IDEs with limited support for JavaFX public static void main(String[] args){ Application.launch(args); } } The diagrams below show some of the classes that play a key role in creating a GUI using JavaFX and how they are related. STAGE 1. setScene(Scene scene) 2. setTitle(String txt) 3. show() 4. hide() 5. close() PARENT Base class for all nodes with the capability to contain other nodes. Calling getChildren() through it returns a list (ObservableList<Class> from the Collection class) through which the following methods can then be invoked: 1. void add(Node node) 2. void addAll(Node… node) 3. void remove(Node node) 4. void clear(): removes all child nodes 5. int size(): returns the number of child nodes. 6. Class get(int index): returns the node at the specified index NODE Base class (abstract) for all objects (nodes => visual components) that can be added to a scene. Some important methods of the Node class: 1. Parent getParent() 2. String getId() 3. void setId(String id): ID has to be unique within the scene graph 4. Node lookup(String id): searches the Node’s children for the one with id matching the argument. The argument, id, must start with #. 5. String getStyle() 6. void setStyle(String style) Note Root node: the node that starts a scene graph Branch node: node that has at least one child node Leaf node: node that has no child node Control: base class for label, button, check box, radio button, text field, text area etc. This class provides the method: void setTooltip(Tooltip value). Where value = new Tooltip(“hint to display”); Shape: refers to a text, line, circle, ellipse, rectangle, arc, polygon, polyline, and so on. Other types of node include: • Camera • Canvas • LightBase (Abstract) • MadiaView • Shape3D • Subscene • SwingNode: to allow the use of GUI components from javax.swing SCENE 1. Scene(Parent rootNode) 2. Scene(Parent rootNode, double width, double height) // stage size = width x height 3. void setRoot(Parent rootNode) Note Scene graph: a tree structure that contains all the nodes that make up a user interface. IMAGE VIEW ImageView imageName = new ImageView(new Image(“Image URL”)); LABEL 1. Label() 2. Label(String txt) 3. Label(String txt, Node image) 4. void setAlignment(Pos alignment) alignment = Pos.BASELINE_CENTER, BASELINE_LEFT, BASELINE_RIGHT, BOTTOM_CENTER, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER, CENTER_LEFT, CENTER_RIGHT, TOP_CENTER, TOP_LEFT, TOP_RIGHT where Pos is an enumeration constant. 5. void setUnderLine(boolean status) BUTTON 1. Button() 2. Button(String txt) 3. void setText(String txt, Node image) 4. void setAlignment(Pos alignment) // alignment static values from the class Pos. 5. void setUnderLine(boolean status) RADIO BUTTON 1. RadioButton() 2. RadioButton(String text) 3. void setSelected(boolean status) 4. void setToggleGroup(ToggleGroup grp): add radio button to a mutually exclusive group (grp). - grp = new ToggleGroup() - grp also provides getToggles() which returns ObservableList<Node>, thus providing the opportunity add all mutually exclusive radio buttons to the group through the method addAll(Node… radioButtons). 5. void setAlignment(Pos alignment) 6. void setUnderLine(boolean status): underline the label 7. boolean isSelected() 8. void setSelected(boolean status) 9. void setText(String text) 10. String getText() PROGRAM FOR ILLUSTRATION import javafx.application.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.stage.*; public class ClickMe extends Application{ Button btn; RadioButton eco, bus, fst; @Override public void start(Stage stage){ btn = new Button("SUBMIT YOUR FLIGHT CLASS"); btn.setOnAction(e -> buttonClick()); eco = new RadioButton("Economy Class"); eco.setSelected(true); bus = new RadioButton("Business Class"); fst = new RadioButton("First Class"); // Make buttons mutually exclusive ToggleGroup grp = new ToggleGroup(); grp.getToggles().addAll(eco, bus, fst); // Alternative approach to making them mutually exclusive eco.setToggleGroup(grp); bus.setToggleGroup(grp); fst.setToggleGroup(grp); // // // FlowPane pane = new FlowPane(); pane.getChildren().addAll(btn, eco, bus, fst); Scene scene = new Scene(pane, 500, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args){ Application.launch(args); } private void buttonClick() { String choice = "Flight class chosen: "; if(eco.isSelected()) choice += eco.getText(); else if(bus.isSelected()) choice += bus.getText(); else choice += fst.getText(); System.out.println(choice); } } OUTPUT 1. Run program 2. Click button 3. Select “Business Class” and click button 4. Select “First Class” and click button 5. Close window CHECK BOX 1. CheckBox() 2. CheckBox(String text) 3. String getText(): returns the label of the text 4. boolean isSelected(): returns the state of the check box i.e. true selected, false unselected 5. void setSelected(boolean status) 6. void setDisabled(Boolean status): true to disable, and false to enable. PROGRAM TO ILLUSTRATE import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application{ Button btn; CheckBox golf = new CheckBox("Golf"), chess = new CheckBox("Chess"), football = new CheckBox("Football"), boxing = new CheckBox("Boxing"); @Override public void start(Stage stage){ btn = new Button("SUBMIT FAVOURITE SPORT(S)"); btn.setOnAction(e -> buttonClick()); FlowPane pane = new FlowPane(); pane.getChildren().addAll(btn, golf, chess, football, boxing); Scene scene = new Scene(pane, 500, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args){ Application.launch(args); } private void buttonClick() { String choice = "Favourite Sports Selected\n"; choice += golf.isSelected() ? golf.getText() + "\n" : ""; choice += chess.isSelected() ? chess.getText() + "\n" : ""; choice += football.isSelected() ? football.getText() + "\n" : ""; choice += boxing.isSelected() ? boxing.getText() + "\n" : ""; System.out.println(choice); } } OUTPUT TEXT FIELD 1. TextField() 2. TextField(String txt) 3. void setText(String text) 4. void setPromptText(String prompt) 5. String getText() 6. void setEditable(boolean status) 7. void setPrefColumnCount(int cols) PASSWORD FIELD PasswordField is the class to use if a text field is for a password. PasswordField extends TextField. PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.Scene; javafx.scene.control.*; javafx.scene.layout.FlowPane; javafx.stage.*; public class ClickMe extends Application { Button btn; TextField username; PasswordField password; @Override public void start(Stage stage) { btn = new Button("CLICK ME"); btn.setOnAction(e -> btnClick()); username = new TextField(); password = new PasswordField(); FlowPane pane = new FlowPane(); pane.getChildren().addAll( btn, new Label("Username: "), username, new Label("Password: "), password ); Scene scene = new Scene(pane, 500, 50); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void btnClick() { System.out.println("DETAILS ENTERED\nUsername: " + username.getText() + "\nPassword: " + password.getText()); } } OUTPUT 1. Run the program 2. Provide username and password 3. Click the button 4. Close the window TEXT AREA 1. TextArea() 2. TextArea(String text) 3. void setWrapText(Boolean status): default value is false i.e. it doesn’t wrap. 4. void setPrefColumnCount(int cols): controls the width of the area 5. void setPrefRowCount(int rows): number of rows of text that should be visible at the same time. 6. void setText(String text) 7. void setPromptText(String prompt) 8. String getText(): returns the text in the text area. 9. void appendText(String txt): Appends txt to the text already in the text area 10. void setEditable(boolean status) PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.Scene; javafx.scene.control.*; javafx.scene.layout.FlowPane; javafx.stage.*; public class ClickMe extends Application { Button btn; TextArea txtArea; @Override public void start(Stage stage) { btn = new Button("CLICK ME"); btn.setOnAction(e -> btnClick()); txtArea = new TextArea(); txtArea.setPromptText("Enter text here ..."); txtArea.setPrefColumnCount(15); txtArea.setPrefRowCount(3); txtArea.setWrapText(true); FlowPane pane = new FlowPane(); pane.getChildren().addAll(btn, txtArea); Scene scene = new Scene(pane, 200, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void btnClick() { System.out.println("TEXT ENTERED\n" + txtArea.getText()); } } OUTPUT 1. Run program 2. Type the 4 lines of text into the text area 3. Click button 4. Close window COMBO BOX 1. ComboBox<Class>() name = new ComboBox<>() 2. ComboBox<Class> name = new ComboBox<>(ObservableList<Class> list) Note Creating list is as shown below: ObservableList<Class> list = FXCollections.observableArrayList(obj, obj, …, obj); Where “Class” represents any class in Java. 3. ObservableList<Class> getItems(): get list of items excluding the prompt. 4. void setItems(ObservableList<Class> items) 5. void setEditable(boolean value) 6. void setVisibleRowCount(int value): sets the number of items that should be visible at the same time when the list of items is displayed. 7. void setPromptText(String text) 8. Class getValue(): returns the selected item and null if no item is selected. 9. void show(): shows the list of items 10. void hide(): hides the list of items 11. boolean isShowing(): tells whether the list of items is showing. 12. SingleSelectionModel<class> getSelectionModel(): returns the selection model of the combo box and from this object, other very important methods can be obtained: a. void clearAndSelect(int index): it clears the previous selection sets the selection to the selected index. Note that the index value of 0 does not correspond to the prompt text. b. void clearSelection(): sets selection to the prompt text. c. void clearSelection(int index): clears the selection if the selected index matches the argument. d. int getSelectedIndex(): returns the index of the selected item e. class getselectedItem(): returns the selected item. f. string toString(): returns the selected item in string form. g. boolean isSelected(int index): returns true if the provided index is the selected one, and false otherwise. h. void select(int index): sets the selected index to the argument. i. void select(class item): sets the selected item to the argument. j. void selectfirst() k. void selectnext() l. void selectlast() PROGRAM FOR ILLUSTRATION import import import import import import javafx.application.*; javafx.collections.*; javafx.scene.Scene; javafx.scene.control.*; javafx.scene.layout.FlowPane; javafx.stage.*; public class ClickMe extends Application { Button btn1, btn2, btn3; ComboBox<String> cmb; @Override public void start(Stage stage) { btn1 = new Button("SHOW/HIDE"); btn2 = new Button("GET ITEMS"); btn3 = new Button("GET SELECTED ITEM"); btn1.setOnAction(e -> btn1Click()); btn2.setOnAction(e -> btn2Click()); btn3.setOnAction(e -> btn3Click()); cmb = new ComboBox<>(FXCollections.observableArrayList( "Sausage", "Pepperoni", "Linguica", "Salame", "Olives", "Mushrooms" )); cmb.setPromptText("Make a choice"); cmb.setVisibleRowCount(4); FlowPane pane = new FlowPane(); pane.getChildren().addAll(btn1, btn2, btn3, cmb); Scene scene = new Scene(pane, 300, 200); stage.setScene(scene); stage.setTitle("The Click Me App"); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void btn1Click() { if (cmb.isShowing()) { cmb.hide(); } else { cmb.show(); } } private void btn2Click() { String lst ="\nITEMS ON LIST\n"; ObservableList<String> items = cmb.getItems(); for(String i : items) lst += i + "\n"; System.out.println(lst); } private void btn3Click() { System.out.println("Selected item: " + cmb.getValue()); } } OUTPUT 1. Run program 2. Click “SHOW/HIDE” 3. Click “GET ITEMS” 4. Click “GET SELECTED ITEM” 5. Select an item 6. Click “GET SELECTED ITEM” 7. Select another item 8. Click “GET SELECTED ITEM” 9. Close window LIST VIEW 1. ListView<Class>() 2. ListView<Class>(ObservableList<Class> items) 3. ObservableList<Class> getItems() 4. void setItems(ObervableList<Class> items) 5. void setOrientation(Orientation orien) orien = Oritentation.HORIZONTAL or Orientation.VERTICAL 6. MultipleSelectionModel<Class> getSelectionModel(): object returned provides following important methods: a. setSelectionMode(SelectionMode value) value = SelectionMode.MULTIPLE, or SelectionMode.SINGLE b. ObervableList<Class> getSelectedItems() c. Class getSelectedItem() PROGRAM FOR ILLUSTRATION import import import import import import javafx.application.*; javafx.collections.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application{ Button btn; ListView list; @Override public void start(Stage stage){ btn = new Button("Click me please!"); btn.setOnAction(e -> buttonClick()); list = new ListView(); list.getItems().addAll("Sausage", "Pepperoni", "Linguica", "Salame", "Olives", "Mushrooms", "Onions", "Peppers", "Pineapple", "Spinach", "Canadian Bacon", "Tomatoes", "Kiwi", "Anchovies", "Gummy Bears"); FlowPane pane = new FlowPane(); list.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); pane.getChildren().addAll(btn, list); Scene scene = new Scene(pane, 300, 400); stage.setScene(scene); stage.setTitle("The Click Me App"); stage.show(); } public static void main(String[] args){ Application.launch(args); } private void buttonClick() { String selection = ""; ObservableList<String> sels = list.getSelectionModel().getSelectedItems(); int sn = 0; for(String sel: sels){ the selection += ++sn + ": " + sel + "\n"; } // ALTERNATIVE //for(int i = 0; i < sels.size(); i++){ // selection += (i + 1) + ": " + sels.get(i) + "\n"; // } System.out.println(selection); } } OUTPUT 1. Run program 2. Select “Sausage” 3. Press “Ctlr” button and select “Salame”, “Olives”, “Onions”, “Pineapple”, “Spinach”, and “Kiwi”. 4. Click the button 5. Close the window HORIZONTAL BOX Places the nodes in a single row. This pane is represented by the class HBox 1. HBox() 2. HBox(double spacing) 3. HBox(Node… children) 4. HBox(double spacing, Node… children) 5. ObservableList<Node> getChildren() 6. static void setAlignment(Pos alignment) 7. static void setHgrow(Node child, Priority priority): Applied to a spacer element (preferably a node from the class Region) meant to take extra space arising from increasing the width of the window. 8. 9. static void setMargin(Node child, Insets margin) - margin = new Insets(double value) // value margin for all 4 sides - margin = new Insets(double top, double right, double bottom, double left) void setPadding(Insets space) 10. void setSpacing(double space) PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application{ Button addNode; HBox pane = new HBox(20); int n = 0; @Override public void start(Stage stage){ addNode = new Button("ADD NODE"); addNode.setOnAction(e -> buttonClick()); pane.getChildren().add(addNode); Scene scene = new Scene(pane, 500, 50); stage.setScene(scene); stage.show(); } public static void main(String[] args){ Application.launch(args); } private void buttonClick() { n++; pane.getChildren().add(new Button("Button " + n)); } } OUTPUT 1. Click “ADD NODE” 5 times 2. Close window VERTICAL BOX Places the nodes in a single column. 1. VBox() 2. VBox(double spacing) 3. VBox(Node… children) 4. VBox(double spacing, Node… children) 5. ObservableList<Node> getChildren() 6. static void setAlignment(Pos alignment) 7. static void setMargin(Node child, Insets margin) 8. void setPadding(Insets space) 9. void setSpacing(double space) 10. static void setVgrow(Node child, Priority priority): similar to setHgrow(…) but is related to increase in the height of the window. PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { Button addNode; VBox pane = new VBox(20); int n = 0; @Override public void start(Stage stage) { addNode = new Button("ADD NODE"); addNode.setOnAction(e -> buttonClick()); pane.getChildren().addAll(addNode); Scene scene = new Scene(pane, 100, 300); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void buttonClick() { n++; pane.getChildren().add(new Button("Button " + n)); } } OUTPUT 1. Run program 2. Click “ADD NODE” 6 times 3. Close window FLOW PANE Places the nodes row-by-row horizontally or column-by-column vertically. A node is moved to a new row or column when there is no more room. 1. FlowPane() 2. FlowPane(double hgap, double vgap) - vgap vertical gap between rows of nodes - hgap horizontal gap between columns of nodes 3. FlowPane(double hgap, double vgap, Node… children) 4. FlowPane(Node… children) 5. FlowPane(Orientation orientation): pane with nodes arranged in the specified orientation. - Orientation = Orientation.HORIZONTAL (default) or Orientation.VERTICAL 6. FlowPane(Orientation orientation, double hgap, double vgap) 7. FlowPane(Orientation orientation, double hgap, double vgap, Node... children) 8. FlowPane(Orientation orientation, Node... children) 9. ObservableList<Node> getChildren() 10. void setAlignment(Pos alignment): sets how nodes are aligned within the pane. 11. void setColumnAlignment(Pos alignment) 12. void setHgap(double value) 13. static void setMargin(Node child, Insets value) 14. void setOrientation(Orientation orientation) 15. void setPadding(Insets value) 16. void setRowAlignment(Pos alignment) 17. void setSpacing(double value) 18. void setVgap(double value) PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { Button addNode; FlowPane pane = new FlowPane(); long n = 1; @Override public void start(Stage stage) { addNode = new Button("ADD NODE!!!"); addNode.setOnAction(e -> buttonClick()); pane.getChildren().addAll(addNode); Scene scene = new Scene(pane, 300, 300); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void buttonClick() { n = n * 10; Button btn = new Button("Button " + n); pane.getChildren().add(btn); } } OUTPUT 1. Run program 2. Click “CLICK ME!!!” 15 times 3. Close window TILE PANE Similar to the flow pane. The difference is that all its nodes are positioned in cells of the same size with the width coming from the widest node, and the height coming from the tallest node within the pane. 1. TilePane() 2. TilePane(Node… children) 3. TilePane(double hgap, double vgap) 4. TilePane(Orientation orientation) 5. TilePane(double hgap, double vgap, Node… children) 6. TilePane(Orientation orientation, double hgap, double vgap) 7. TilePane(Orientation orientation, Node… children) 8. TilePane(Orientation orientation, double hgap, double vgap, Node… children) 9. ObservableList<Node> getChildren() 10. void setHgap(double value) 11. void setVgap(double value) 12. void setOrientation(Orientation orientation) 13. static void setOrientation(Node node, Orientation orientation): sets the orientation of the node within its cell. 14. void setPrefTileWidth(double value) 15. void setPrefTileHeight(double value) 16. static void setMargin(Node node, Insets value) 17. void setPadding(Insets value) PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { Button addNode; TilePane pane = new TilePane(); long n = 1; @Override public void start(Stage stage) { addNode = new Button("ADD NODE!!!"); addNode.setOnAction(e -> buttonClick()); pane.getChildren().addAll(addNode); Scene scene = new Scene(pane, 320, 210); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void buttonClick() { n = n * 10; Button btn = new Button("Button " + n); pane.getChildren().add(btn); } } OUTPUT BORDER PANE Places the nodes in the top, right, bottom, left, and center regions. Each region can accommodate only one node. Important facts to note include: ➢ A region without a node is not rendered ➢ Increasing the height of the window causes heights of the left, center, and right regions to increase. ➢ Increasing the width of the window causes the width of the top, center and bottom to increase. 1. BorderPane() 2. BorderPane(Node center) 3. BorderPane (Node center, Node top, Node right, Node bottom, Node left) 4. void setCenter(Node node) 5. void setTop(Node node) 6. void setRight(Node node) 7. void setBottom(Node node) 8. void setLeft(Node node) 9. void setAlignment(Pos alignment) 10. void setPadding(Insets value) 11. static void setMargin(Node child, Insets value) Methods 4, 5, 6, 7, and 8 positions the provided nodes in the specified regions. PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; import javafx.stage.*; public class ClickMe extends Application { @Override public void start(Stage stage) { Button top = new Button("TOP REGION"); Button left = new Button("LEFT REGION"); Button bottom = new Button("BOTTOM REGION"); Button right = new Button("RIGHT REGION"); Button center = new Button("REGION REGION"); // ENSURE NODES FILL-UP THE AVAILABLE SPACE (WIDTH, AND HEIGHT) top.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); bottom.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); center.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); left.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); right.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); BorderPane pane = new BorderPane(center, top, right, bottom, left); pane.setPadding(new Insets(10)); Scene scene = new Scene(pane, 400, 200); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } } OUTPUT SCROLL PANE This is a node used with panes contrary to what its name suggests. It is used with panes to display make invisible sections of a pane -bigger than the window- visible with the aid of scroll bars (horizontal, and vertical). ScrollPane() ScrollPane(Node node) void getContent(Node node): sets the node for a scroll pane. void setPannable(Boolean value): If true, the user can pan the contents of the scroll pane using the mouse. The default is false. void setPadding(Insets value) PROGRAM FOR ILLUSTRATION import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { Button addNode; TilePane pane = new TilePane(); @Override public void start(Stage stage) { addNode = new Button("CLICK TO SHRINK PANE"); addNode.setOnAction(e -> buttonClick()); pane.getChildren().addAll(addNode); ScrollPane scrlPane = new ScrollPane(pane); scrlPane.setPannable(true); Scene scene = new Scene(scrlPane, 320, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void buttonClick() { pane.setPrefWidth(318); } } OUTPUT 1. Run program 2. With cursor on window, hold down left mouse button and drag left and right 3. Click the button 4. Repeat 2 GRID PANE Places the nodes in the cells in a grid of rows and columns. Important facts to note include: ➢ The cell for a node can be specified, and its (node smaller than its cell) alignment within it can be specified. ➢ Nodes can be made to span multiple rows or columns ➢ Nodes smaller than their cells, can be stretch to fill their cells horizontally, vertically, or both. ➢ Placing components on this pane requires careful planning which involves dividing the interface into cells and noting the index of rows, and columns for each node. This also involves noting if nodes will span rows or columns and how many will be spanned. 1. GridPane() 2. void add(Node node, int col, int row) 3. void add(Node node, int col, int row, int colspan, int rowspan) 4. void addColumn(int col, Node... nodes): adds an entire column of nodes 5. void addRow(int row, Node... nodes): adds an entire row of columns 6. static void setColumnSpan(Node node, int colspan) 7. void setRowSpan(Node node, int colspan) 8. void setHalignment(Node node, HPos value) 9. value = HPos.LEFT, CENTER, or RIGHT 10. void setValignment(Node node, VPos value) 11. value = VPos.BOTTOM, CENTER, or TOP 12. void setHgap(double value) 13. void setVgap(double value) 14. static void setMargin(Node node, Insets value) 15. void setPadding(Insets value) PROGRAM FOR ILLUSTRATION Target interface below: Columns index 0 1 2 3 4 0 Row index 1 2 import import import import import javafx.application.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { GridPane pane = new GridPane(); @Override public void start(Stage stage) { pane.addRow(0, new Button("4"), new Button("5"), new Button("6"), new Button("*"), new Button("1/x")); pane.addRow(1, new Button("1"), new Button("2"), new Button("3"), new Button("-")); pane.add(new Button("="), 4, 1, 1, 2); pane.add(new Button("0"), 0, 2, 2, 1); pane.add(new Button("."), 2, 2); pane.add(new Button("+"), 3, 2); Scene scene = new Scene(pane, 130, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } } OUTPUT GRID PANE CONSTRAINTS Controlling the size of row and columns require methods from two classes (ColumnConstraints and RowConstraints). From ColumnConstraints 1. ColumnConstraints() 2. ColumnConstraints(double size) 3. ColumnConstraints(double min, double pref, double max) 4. void setMinWidth(double value) 5. void setMaxWidth(double value) 6. void setPrefWidth(double value) 7. void setPercentWidth(double value) 8. void setHgrow(Priority value) 9. void setHalignment(HPos value) From RowConstraints 1. RowConstraints() 2. RowConstraints(double height) 3. RowConstraints(double min, double pref, double max) 4. void setMinHeight(double value) 5. void setMaxHeight(double value) 6. void setPrefHeight(double value) 7. void setPercentHeight(double value) 8. void setVgrow(Priority value) 9. void setValignment(VPos value) USING CREATED CONSTRAINTS Doing this requires using the methods below with the constraint objects 1. getColumnConstraints().addAll(const, const, …, const) 2. getRowConstraints().addAll(const, const, …, const) PROGRAM FOR ILLUSTRATION import import import import import import import javafx.application.*; javafx.collections.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { GridPane pane = new GridPane(); @Override public void start(Stage stage) { pane.addRow(0, new Button("4"), new Button("5"), new Button("6"), new Button("*"), new Button("1/x")); pane.addRow(1, new Button("1"), new Button("2"), new Button("3"), new Button("-")); pane.add(new pane.add(new pane.add(new pane.add(new Button("="), Button("0"), Button("."), Button("+"), 4, 0, 2, 3, 1, 1, 2); 2, 2, 1); 2); 2); // GET ALL BUTTONS AND MAKE THEM FILL AVAILABLE SPACES ObservableList<Node> children = pane.getChildren(); for(int i = 0; i < children.size(); i++){ ((Control)children.get(i)).setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); } ColumnConstraints cc = new ColumnConstraints(); cc.setPercentWidth(20); pane.getColumnConstraints().addAll(cc, cc, cc, cc, cc); RowConstraints rc = new RowConstraints(); rc.setPercentHeight(100/3.0); pane.getRowConstraints().addAll(rc, rc, rc); pane.setHgap(10); pane.setVgap(10); pane.setPadding(new Insets(10)); Scene scene = new Scene(pane, 250, 150); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } } OUTPUT 1. Run program 2. Close window STACK PANE Places the nodes on top of each other in the center of the pane. 1. StackPane() 2. StackPane(Node… children) 3. ObservableList<Node> getChildren() 4. static void setAlignment(Pos alignment) 5. static void setMargin(Node child, Insets value) 6. void setPadding(Insets value) For the sake of illustrating how to use a stack pane, the following points are of importance: - new Rectangle(double width, double height): returns a rectangle of the specified width and height - setFill(Color color): fills the rectangle with the specified color. A stack pane will be used to draw two rectangles stacked with the smaller on top the bigger. PROGRAMM FOR ILLUSTRATION import import import import import import javafx.application.*; javafx.scene.*; javafx.scene.layout.*; javafx.scene.paint.Color; javafx.scene.shape.*; javafx.stage.*; public class ClickMe extends Application { @Override public void start(Stage stage) { Rectangle r1 = new Rectangle(300, 200); r1.setFill(Color.GREEN); Rectangle r2 = new Rectangle(100, 200); r2.setFill(Color.WHITE); StackPane pane = new StackPane(r1, r2); Scene scene = new Scene(pane, 310, 210); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } } OUTPUT PROGRAMMING EXERCISES 1. Write a program to produce the interface below. Specifications that are not very obvious are presented below: a. Interface dimension = 460 x 461 b. Distance between buttons = 40 c. Button size = 150 x 40 d. Padding for bottom pane = 30, 40, 20, 40 e. Space between radio buttons = 10 f. Space between check boxes = 10 g. Combo box for LGA is disabled CODE import import import import import import javafx.application.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { TextField txtMat = new TextField(); TextField txtFullName = new TextField(); TextField txtMobile = new TextField(); RadioButton radMale = new RadioButton("Male"); RadioButton radFemale = new RadioButton("Female"); ComboBox cmbState = new ComboBox(); ComboBox cmbLGA = new ComboBox(); CheckBox chkWAEC = new CheckBox("WAEC"); CheckBox chkNECO = new CheckBox("NECO"); CheckBox chkNABTEB = new CheckBox("NABTEB"); Button btnSubmit = new Button("SUBMIT"); Button btnReset = new Button("RESET"); @Override public void start(Stage stage) { radMale.setSelected(true); ToggleGroup tg = new ToggleGroup(); tg.getToggles().addAll(radMale, radFemale); cmbState.setPromptText("Choose a state ..."); cmbState.setMaxWidth(Double.MAX_VALUE); cmbLGA.setMaxWidth(Double.MAX_VALUE); cmbLGA.setDisable(true); HBox paneGender = new HBox(radMale, radFemale); paneGender.setSpacing(10); paneGender.setAlignment(Pos.CENTER_LEFT); HBox paneResult = new HBox(chkWAEC, chkNECO, chkNABTEB); paneResult.setAlignment(Pos.CENTER_LEFT); paneResult.setSpacing(10); HBox paneBottom = new HBox(40, btnSubmit, btnReset); btnSubmit.setPrefSize(150, 40); btnReset.setPrefSize(150, 40); paneBottom.setPadding(new Insets(30, 40, 20, 40)); paneBottom.setAlignment(Pos.CENTER); GridPane paneCenter = new GridPane(); paneCenter.addColumn(0, new Label("Matriculation Number: "), new Label("Full Name: "), new Label("Mobile: "), new Label("Gender: "), new Label("State: "), new Label("LGA: "), new Label("O'Level Result(s)- at most 2: ")); paneCenter.addColumn(1, txtMat, txtFullName, txtMobile, paneGender, cmbState, cmbLGA, paneResult); paneCenter.setHgap(10); paneCenter.setVgap(10); ColumnConstraints cc1 = new ColumnConstraints(); cc1.setPercentWidth(50); cc1.setHalignment(HPos.RIGHT); ColumnConstraints cc2 = new ColumnConstraints(); cc2.setPercentWidth(50); // 100/2.0 paneCenter.getColumnConstraints().addAll(cc1, cc2); RowConstraints rc = new RowConstraints(); rc.setPercentHeight(14.28); // 100/7.0 paneCenter.getRowConstraints().addAll(rc, rc, rc, rc, rc, rc, rc); BorderPane paneMain = new BorderPane(); paneMain.setCenter(paneCenter); paneMain.setBottom(paneBottom); paneMain.setPadding(new Insets(5)); Scene scene = new Scene(paneMain, 460, 461); stage.setScene(scene); stage.setTitle("SUBMIT STUDENT DETAILS"); stage.show(); } public static void main(String[] args) { Application.launch(args); } } 2. Write a program to produce the interface below, with the provided specifications: a. Horizontal and vertical gap separating all components is 10px. b. The grid has a padding of 10px all round. c. Text filed for phone number has a maximum width of 200px. d. Both buttons have a minimum width of 80px. e. Stage has a minimum width of 500px and a maximum width of 600px. CODE import import import import import import javafx.application.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { TextField txtName, txtPhone, txtAddress; RadioButton rdoSmall, rdoMedium, rdoLarge, rdoThin, rdoThick; CheckBox chkPepperoni, chkMushrooms, chkAnchovies; @Override public void start(Stage stage) { txtName = new TextField(); txtPhone = new TextField(); txtAddress = new TextField(); rdoSmall = new rdoLarge = new rdoThick = new chkPepperoni = chkAnchovies = RadioButton("Small"); rdoMedium = new RadioButton("Medium"); RadioButton("Large"); rdoThin = new RadioButton("Thin"); RadioButton("Thick"); new CheckBox("Pepperoni"); chkMushrooms = new CheckBox("Mushrooms"); new CheckBox("Anchovies"); txtName.setPromptText("Enter the name here"); txtPhone.setPromptText("Enter the phone number here"); txtPhone.setMaxWidth(200); txtAddress.setPromptText("Enter the address here"); rdoMedium.setSelected(true); rdoThin.setSelected(true); ToggleGroup groupSize = new ToggleGroup(); groupSize.getToggles().addAll(rdoSmall, rdoMedium, rdoLarge); ToggleGroup groupCrust = new ToggleGroup(); groupCrust.getToggles().addAll(rdoThin, rdoThick); Button btnOK = new Button("OK"), btnCancel = new Button("Cancel"); btnOK.setMinWidth(80); btnCancel.setMinWidth(80); VBox paneSize = new VBox(10, new Label("Size"), rdoSmall, rdoMedium, rdoLarge); VBox paneCrust = new VBox(10, new Label("Crust"), rdoThin, rdoThick); VBox paneToppings = new VBox(10, new Label("Topping"), chkPepperoni, chkMushrooms, chkAnchovies); HBox paneButtons = new HBox(10, btnOK, btnCancel); GridPane grid = new GridPane(); grid.setPadding(new Insets(10)); grid.setHgap(10); grid.setVgap(10); grid.addRow(0, new Label("Name: "), txtName); grid.addRow(1, new Label("Phone number: "), txtPhone); grid.addRow(2, new Label("Address: "), txtAddress); grid.addRow(3, paneSize, paneCrust, paneToppings); grid.add(paneButtons, 2, 4); GridPane.setColumnSpan(txtName, 2); GridPane.setColumnSpan(txtPhone, 2); GridPane.setColumnSpan(txtAddress, 2); ColumnConstraints c1 = new ColumnConstraints(); c1.setPercentWidth(33); c1.setHalignment(HPos.RIGHT); ColumnConstraints c2 = new ColumnConstraints(); c2.setPercentWidth(33); grid.getColumnConstraints().addAll(c1, c2, c2); Scene scene = new Scene(grid); stage.setScene(scene); stage.setMinWidth(500); stage.setMaxWidth(600); stage.setTitle("Pizza Order"); stage.show(); } public static void main(String[] args) { Application.launch(args); } } 3. Write a program that switches the panes at a center in response to a button clicked. The interface specifications are as show below: a. The interface is 250 x 200. b. Buttons are separated by 10px. c. The pane with the buttons has a right margin of 10px. d. Write separate functions to handle the expected switching for each button clicked. CODE import import import import import import javafx.application.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class ClickMe extends Application { VBox pane1; VBox pane2; VBox pane3; StackPane paneStack; @Override public void start(Stage stage) { pane1 = new VBox(new Label("PANE 1 IN THE STACK")); pane2 = new VBox(new Label("PANE 2 IN THE STACK")); pane3 = new VBox(new Label("PANE 3 IN THE STACK")); paneStack = new StackPane(pane1); Button btn1 = new Button("SHOW PANE 1"); Button btn2 = new Button("SHOW PANE 2"); Button btn3 = new Button("SHOW PANE 3"); btn1.setOnAction(e -> btn1Click()); btn2.setOnAction(e -> btn2Click()); btn3.setOnAction(e -> btn3Click()); VBox paneButtons = new VBox(10, btn1, btn2, btn3); BorderPane paneBorder = new BorderPane(); paneBorder.setLeft(paneButtons); paneBorder.setCenter(paneStack); paneBorder.setPadding(new Insets(10)); BorderPane.setMargin(paneButtons, new Insets(0, 10, 0, 0)); Scene scene = new Scene(paneBorder, 250, 200); stage.setScene(scene); stage.show(); } public static void main(String[] args) { Application.launch(args); } private void btn1Click(){ paneStack.getChildren().clear(); paneStack.getChildren().add(pane1); } private void btn2Click(){ paneStack.getChildren().clear(); paneStack.getChildren().add(pane2); } private void btn3Click(){ paneStack.getChildren().clear(); paneStack.getChildren().add(pane3); } } EVENT-DRIVEN PROGRAMMING EVENT: an object generated (automatically) when a user does something noteworthy with one of the components constituting a user interface. Event objects are represented by the class javafx.event.Event and its subclasses (listed below). The table below shows some classes of events and the actions of a user that can generate them. Event Class User’s Action ActionEvent - Click a button - Press Enter in a text input field - Check or uncheck radio button - Check or uncheck a check box - Select a new item from a combo box - Mouse pressed - Mouse released - Mouse clicked - Mouse entered - Mouse exited - Mouse moved - Mouse dragged - Key pressed - Key released - Key typed - Open a window MouseEvent KeyEvent WindowEvent - Close a window SOURCE: the component interacted with on the user interface. LISTENER/HANDLER: an object of the handler interface (javafx.event.EventHandler) in which is provided statements that must be executed in response to the event. These statements are provided in the abstract method handle(…). This method takes the event object generated as its argument i.e. handle(EventClass event) where EventClass represents the class Event or any of its subclasses. The main things done in event-driven programming are: 1. Create an event source (done when building the stage). 2. Create an event handler or listener. 3. Register the event handler with the event source (link source and handler) CREATING AN EVENT HANDLER 1. Using the Application The Application representing the stage (frame/window) is made to implement the EventHandler and as a result the abstract method handle(…) is implemented within it. // IMPORT NECESSARY PACKAGES public class Experiments extends Application implements EventHandler { : : @Override public void handle(Event t) { // execute when event occurs } } 2. Use an Inner Class Use an inner class defined (usually private) within Application to implement EventHandler. // IMPORT NECESSARY PACKAGES public class Experiments extends Application { @Override public void start(Stage stage) { : : } public static void main(String[] args) { Application.launch(args); } private class HandlerClass implements EventHandler{ @Override public void handle(Event e) { // execute when event occurs } } } 3. Use an Anonymous Inner Class Unassigned constructor + method body/implementation Anonymous class EventHandler. The anonymous class is defined within the Application. new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { // execute when event occurs } } 4. Using Lambda Expression This approach is derived from 3 (using anonymous inner class). In this case, the name of the interface and the abstract method to implement are replaced by a variable name and the arrow operator. This is because EventHandler interface is said to be a functional interface because it has only one abstract method. e -> { // execute when event occurs } It is important to note that, the body of the method in the above syntax can be replaced with a call to a method that has already been implemented. This is as shown below: e -> executeMethod(...) LINKING EVENT HANDLER AND EVENT SOURCE Doing this requires invoking/calling a method (I will like to call it the linking method) through the event source and supplying the instance of the listener as argument to the invoked method. The name of the method invoked and class of the handler depend on the type of event. Some of these methods are listed below: 1. setOnAction(EventHandler<ActionEvent>) 2. setOnMousePressed(EventHandler<MouseEvent>) 3. setOnMouseReleased(EventHandler<MouseEvent>) 4. setOnMouseClicked(EventHandler<MouseEvent>) 5. setOnMouseEntered(EventHandler<MouseEvent>) 6. setOnMouseExited(EventHandler<MouseEvent>) 7. setOnMouseMoved(EventHandler<MouseEvent>) 8. setOnMouseDragged(EventHandler<MouseEvent>) 9. setOnKeyPressed(EventHandler<KeyEvent>) 10. setOnKeyReleased(EventHandler<KeyEvent>) 11. setOnKeyTyped(EventHandler<KeyEvent>) Registration is done using the format below: sourceObject.linkingMethod(handlerObject); The object of the event handler can be made available through: a. an instance of the Application b. an instance of the inner class within Application c. an anonymous inner class d. an anonymous inner class in lambda expression e. an anonymous inner class in lambda expression with a helper method Linking in cases (c), (d), and (e) above are done directly with the event source of interest. This ensures that the handler object can only be linked with the source of interest. Examples will now be presented using each of the above approaches for the handlerObject. The program below uses the 3 sides of a triangle to compute and display its area. (a) CODE WITH APPLICATION AS HANDLER import import import import import import import javafx.application.*; javafx.event.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class EventIssues extends Application implements EventHandler { TextField txtA, txtB, txtC, txtAns; Button btnSolve; @Override public void start(Stage stage) throws Exception { txtA = new TextField(); txtB = new TextField(); txtC = new TextField(); txtAns = new TextField(); txtAns.setEditable(false); btnSolve = new Button("SOLVE"); btnSolve.setPrefSize(230, 150); GridPane paneGrid = new GridPane(); paneGrid.addColumn(0, new Label("Side A: "), new Label("Side B: "), new Label("Side C: "), new Label("Answer: ")); paneGrid.addColumn(1, txtA, txtB, txtC, txtAns); paneGrid.add(btnSolve, 0, 4); GridPane.setColumnSpan(btnSolve, 2); paneGrid.setHgap(10); paneGrid.setVgap(10); paneGrid.setPadding(new Insets(10)); Scene scene = new Scene(paneGrid, 230, 200); stage.setScene(scene); stage.setTitle("TRIANGLE"); stage.show(); // LINK EVENT SOURCE WITH HANDLER (EventIssues ==> Application class) btnSolve.setOnAction(this); } public static void main(String[] args) { Application.launch(args); } @Override public void handle(Event t) { // Get entered sides and convert them to numbers double a, b, c; a = Double.parseDouble(txtA.getText()); b = Double.parseDouble(txtB.getText()); c = Double.parseDouble(txtC.getText()); // Calculate area double s = (a + b + c) / 2; double area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); // Print area on interface as a String txtAns.setText(area + ""); } } (b) CODE WITH OBJECT OF INNER CLASS AS HANDLER import import import import import import import javafx.application.*; javafx.event.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class EventIssues extends Application { TextField txtA, txtB, txtC, txtAns; Button btnSolve; @Override public void start(Stage stage) throws Exception { txtA = new TextField(); txtB = new TextField(); txtC = new TextField(); txtAns = new TextField(); txtAns.setEditable(false); btnSolve = new Button("SOLVE"); btnSolve.setPrefSize(230, 150); GridPane paneGrid = new GridPane(); paneGrid.addColumn(0, new Label("Side A: "), new Label("Side B: "), new Label("Side C: "), new Label("Answer: ")); paneGrid.addColumn(1, txtA, txtB, txtC, txtAns); paneGrid.add(btnSolve, 0, 4); GridPane.setColumnSpan(btnSolve, 2); paneGrid.setHgap(10); paneGrid.setVgap(10); paneGrid.setPadding(new Insets(10)); Scene scene = new Scene(paneGrid, 230, 200); stage.setScene(scene); stage.setTitle("TRIANGLE"); stage.show(); // LINK EVENT SOURCE WITH HANDLER (Handler class below) btnSolve.setOnAction(new Handler()); } public static void main(String[] args) { Application.launch(args); } // PRIVATE INNER CLASS IMPLEMENTING EVENTHANDLER private class Handler implements EventHandler { @Override public void handle(Event t) { // Get entered sides and convert them to numbers double a, b, c; a = Double.parseDouble(txtA.getText()); b = Double.parseDouble(txtB.getText()); c = Double.parseDouble(txtC.getText()); // Calculate area double s = (a + b + c) / 2; double area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); // Print area on interface as a String txtAns.setText(area + ""); } } } (c) CODE WITH OBJECT OF AN ANONYMOUS INNER CLASS AS HANDLER import import import import import import import javafx.application.*; javafx.event.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class EventIssues extends Application { TextField txtA, txtB, txtC, txtAns; Button btnSolve; @Override public void start(Stage stage) throws Exception { txtA = new TextField(); txtB = new TextField(); txtC = new TextField(); txtAns = new TextField(); txtAns.setEditable(false); btnSolve = new Button("SOLVE"); btnSolve.setPrefSize(230, 150); GridPane paneGrid = new GridPane(); paneGrid.addColumn(0, new Label("Side A: "), new Label("Side B: "), new Label("Side C: "), new Label("Answer: ")); paneGrid.addColumn(1, txtA, txtB, txtC, txtAns); paneGrid.add(btnSolve, 0, 4); GridPane.setColumnSpan(btnSolve, 2); paneGrid.setHgap(10); paneGrid.setVgap(10); paneGrid.setPadding(new Insets(10)); Scene scene = new Scene(paneGrid, 230, 200); stage.setScene(scene); stage.setTitle("TRIANGLE"); stage.show(); // LINK EVENT SOURCE WITH HANDLER (Anonymous inner class) btnSolve.setOnAction(new EventHandler() { @Override public void handle(Event t) { // Get entered sides and convert them to numbers double a, b, c; a = Double.parseDouble(txtA.getText()); b = Double.parseDouble(txtB.getText()); c = Double.parseDouble(txtC.getText()); // Calculate area double s = (a + b + c) / 2; double area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); // Print area on interface as a String txtAns.setText(area + ""); } }); } public static void main(String[] args) { Application.launch(args); } } (d) CODE WITH HANDLER OBJECT AS LAMBDA EXPRESSION import import import import import import javafx.application.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class EventIssues extends Application { TextField txtA, txtB, txtC, txtAns; Button btnSolve; @Override public void start(Stage stage) throws Exception { txtA = new TextField(); txtB = new TextField(); txtC = new TextField(); txtAns = new TextField(); txtAns.setEditable(false); btnSolve = new Button("SOLVE"); btnSolve.setPrefSize(230, 150); GridPane paneGrid = new GridPane(); paneGrid.addColumn(0, new Label("Side A: "), new Label("Side B: "), new Label("Side C: "), new Label("Answer: ")); paneGrid.addColumn(1, txtA, txtB, txtC, txtAns); paneGrid.add(btnSolve, 0, 4); GridPane.setColumnSpan(btnSolve, 2); paneGrid.setHgap(10); paneGrid.setVgap(10); paneGrid.setPadding(new Insets(10)); Scene scene = new Scene(paneGrid, 230, 200); stage.setScene(scene); stage.setTitle("TRIANGLE"); stage.show(); // LINK EVENT SOURCE WITH HANDLER (Anonymous inner class) btnSolve.setOnAction( t -> { double a, b, c; a = Double.parseDouble(txtA.getText()); b = Double.parseDouble(txtB.getText()); c = Double.parseDouble(txtC.getText()); // Calculate area double s = (a + b + c) / 2; double area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); // Print area on interface as a String txtAns.setText(area + ""); }); } public static void main(String[] args) { Application.launch(args); } } (e) CODE WITH HANDLER OBJECT AS LAMBDA EXPRESSION TIED TO A HELPER METHOD import import import import import import javafx.application.*; javafx.geometry.*; javafx.scene.*; javafx.scene.control.*; javafx.scene.layout.*; javafx.stage.*; public class EventIssues extends Application { TextField txtA, txtB, txtC, txtAns; Button btnSolve; @Override public void start(Stage stage) throws Exception { txtA = new TextField(); txtB = new TextField(); txtC = new TextField(); txtAns = new TextField(); txtAns.setEditable(false); btnSolve = new Button("SOLVE"); btnSolve.setPrefSize(230, 150); GridPane paneGrid = new GridPane(); paneGrid.addColumn(0, new Label("Side A: "), new Label("Side B: "), new Label("Side C: "), new Label("Answer: ")); paneGrid.addColumn(1, txtA, txtB, txtC, txtAns); paneGrid.add(btnSolve, 0, 4); GridPane.setColumnSpan(btnSolve, 2); paneGrid.setHgap(10); paneGrid.setVgap(10); paneGrid.setPadding(new Insets(10)); Scene scene = new Scene(paneGrid, 230, 200); stage.setScene(scene); stage.setTitle("TRIANGLE"); stage.show(); // LINK EVENT SOURCE WITH HANDLER (Lambda expression with helper method) btnSolve.setOnAction(t -> computeArea()); } public static void main(String[] args) { Application.launch(args); } private void computeArea() { double a, b, c; a = Double.parseDouble(txtA.getText()); b = Double.parseDouble(txtB.getText()); c = Double.parseDouble(txtC.getText()); // Calculate area double s = (a + b + c) / 2; double area = Math.sqrt(s * (s - a) * (s - b) * (s - c)); // Print area on interface as a String txtAns.setText(area + ""); } } OUTPUT FROM EACH PROGRAM ABOVE FORMATTING WITH CSS SELECTORS Selector .styleClassName or #idValue (for a unique component) JavaFX has default style class names derived from the class name. This is done by writing the class names in lower case and using hyphen (-) between words, if the class name has more than one word e.g. CheckBox check-box, RadioButton radio-button, Button button etc. Some subclasses of the Node class don’t have default style class names. For example, layout panes such as HBox, VBox etc. as well as shapes such as Rectangle, Circle etc. Style class names have to be created for classes of this sort. Syntax is given below guiComponent.getStyleClass().add(“created_style_class_name”). STYLE RULE SYNTAX Style sheet contains one or more style rules. Syntax for a style rule is as shown below: Selector{ property: value; property: value; : property: value; } Style rules can be administered through: 1. Inline (directly on the GUI component) guiComponent.setStyle(“property: value”); 2. External style sheet in same directory as .class file (preferably on a scene) scene.getStyleSheets().add(“sheetName.css”); 3. External style sheet in another directory scene.getStylesheets().add(getClass().getResource(“sheetName.css”).toExternalForm()); STYLE PROPERTIES Properties used in the style rule are majorly CSS properties preceded by -fx-. For example, background-color -fx-background-color, font -fx-font. Below are some important style properties: 1. -fx-font-family: serif, sans-serif, cursive, fantasy, or monospace 2. -fx-font-size: number ending in pt (point) or px (pixel) 3. -fx-font-style: normal, italic, or oblique 4. -fx-font-weight: normal, bold, bolder, lighter, 100, 200, 300, 400, 500,…,800, or 900 5. -fx-font: (combination of one or more of the above values) 6. -fx-background-color: color name (e.g. cornsilk, thistle, papayawhip, aliceblue, red, blue, green etc.), #RRGGBB (e.g. #f5f5f5) 7. -fx-border-width: number in pixels 8. -fx-border-style: none, solid, dotted, or dashed 9. -fx-border-color Program for Illustration Java File import javafx.application.*; import javafx.geometry.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.stage.Stage; public class Experiment extends Application { Button btn; Label fontLbl; int n; @Override public void start(Stage stage) { n = 0; String[] typeface = {"serif", "sans-serif", "cursive", "fantasy", "monospace"}; fontLbl = new Label(typeface[n].toUpperCase()); fontLbl.setStyle("-fx-font: 20px " + typeface[n] + ";"); btn = new Button("SWITCH FONTS"); VBox pane = new VBox(10, btn, fontLbl); pane.setPadding(new Insets(10)); // CREATE A STYLE CLASS FOR THE PANE WHICH HAS NONE pane.getStyleClass().add("pane"); // GIVE THE LABEL AN ID fontLbl.setId("label"); Scene scene = new Scene(pane, 200, 100); // LINK SCENE TO EXTERNAL STYLE SHEET scene.getStylesheets().add(getClass().getResource("CSS.css").toExternalForm()); stage.setScene(scene); stage.show(); btn.setOnAction(e -> { n++; if (n == 5) { n = 0; } fontLbl.setText(typeface[n].toUpperCase()); fontLbl.setStyle("-fx-font: 20px " + typeface[n] + ";"); }); } public static void main(String[] args) { Application.launch(args); } } CSS File .pane{ -fx-background-color:cornsilk; } .pane, .button, .label{ -fx-border-style: solid; -fx-border-width: 2px; } .button{ -fx-background-color: yellow; -fx-font-weight: bolder; } OUTPUT (Click button “SWITCH FONTS” 5 times) EXCEPTION HANDLING The occurrence of an exception (error) will prevent further execution of a program. Preventing this requires dealing with it. One way of doing this is by using a try-catch block of codes. Statements after the one that generated the exception are not executed. Try-catch syntax is given below: try{ Statement(s) likely to generate exception stay here } catch(ExceptionClass var){ Statement(s) executed when the exception occurs } finally{ Represents statement(s) executed whether or not exception occurs, if the block is present } EXCEPTION UNHANDLED public class Experiment2 { public static void main(String[] args) { int a = Integer.parseInt("6"); System.out.println("a = " + a); int b = Integer.parseInt("b"); System.out.println("b = " + b); System.out.println("END"); } } SAMPLE OUTPUT a = 6 Exception in thread "main" java.lang.NumberFormatException: For input string: "b" EXCEPTION HANDLED public class Experiment2 { public static void main(String[] args) { try { int a = Integer.parseInt("6"); System.out.println("a = " + a); int b = Integer.parseInt("b"); System.out.println("b = " + b); System.out.println("END"); } catch (Exception err) { System.out.println("\nERROR!!1 String is not a numeric."); } } } SAMPLE OUTPUT a = 6 ERROR!!1 String is not a numeric. EXCEPTION HANDLED WITH FINAL BLOCK public class Experiment2 { public static void main(String[] args) { try { int a = Integer.parseInt("6"); System.out.println("a = " + a); int b = Integer.parseInt("b"); System.out.println("b = " + b); } catch (Exception err) { System.out.println("\nERROR!!1 String is not a numeric."); } finally { System.out.println("END"); } } } SAMPLE OUTPUT a = 6 ERROR!!1 String is not a numeric. END FILE PROCESSING File names are either absolute (when they start with a drive letter) or relative (when they don’t). When giving slashes in file paths it is either \\ (for windows) or / (all OS). 1. File(pathname: String) – creates a File object for a file or directory. For example: a. new File(“c:\\book”) or new File(“c:/book”) Directory b. new File(“c:\\book\\test.dat”) or new File(“c:/book/test.dat”) 2. File(parent: String, child: String) – creates a File object for a subdirectory/file in the parent directory 3. File(parent: File, child: String) – similar to (2) except that the parent is represented by a file object. 4. createNewFile(): boolean – creates the associated file (all directories in the path must have been created) and returns true if the file wasn’t existing and false it already exist. 5. exists(): boolean – returns true if the file or directory represented by the file object exists. 6. canRead(): boolean – returns true if the associated file can be read i.e. data can be obtained from it. 7. canWrite(): boolean – returns true if associated file can be written to. 8. isDirectory(): boolean – returns true if file object represents a directory. 9. isFile(): boolean – returns true if file object represents a file. 10. isAbsolute(): boolean – returns true if the file object was created with an absolute path. 11. isHidden(): boolean – returns true if the file or directory is hidden. 12. getAbsolutePath(): String – returns the absolute path of a directory or file. 13. getName(): String – returns the last name and extension of the associated file. 14. getPath(): String – returns the complete directory and file name of the file object. 15. getParent(): String – gets a string of all directories above the associated directory or file. null is returned if there is no directory in the name used for creating the file object. 16. lastModified(): long – returns the time (timestamp) that the file was last modified. 17. length(): long – returns the size of the file or 0 if it doesn’t exist or is a directory. 18. listFile(): File[] – returns an array of file objects representing the files contained in the directory represented by the file object. 19. delete(): boolean – deletes the associated file or directory and returns true if successful. 20. renameTo(dest: File): boolean – renames the associated file or directory to the name of the File object provided as argument, returning true if successful. 21. mkdir(): boolean – creates the last directory in a file name if it is not existing and returns true if the process is successful. It is important to ensure the file name has only directories and only the last is non-existent in the system. 22. mkdirs(): boolean – creates all non-existent directories in a file name. The file name has to have only directory names. PROCESSING TEXT FILES WRITING WITH PRINTWRITER 1. PrintWriter(file: File): file is linked to the file to be processed. 2. PrintWriter(filename: String) - The file is created if it wasn’t existing before construction, and overwritten if it was. Methods to use through the PrintWriter object include: 3. print(data) 4. println(data) 5. printf(data) 6. close(): must be invoked after the write operation has been completed otherwise the file will end up corrupted. data String, char, char[], int, long, float, double, or Boolean CODE import java.io.*; public class Experiment3 { public static void main(String[] args){ try{ File f = new File("Text File.txt"); PrintWriter pw = new PrintWriter(f); pw.println(5); pw.println(234l); pw.println(2.4e-5); pw.println(2.5f); pw.println(true); pw.println('#'); pw.println("String"); char[] charArray = {'c','h','a','r',' ','a','r','r','a','y'}; pw.println(charArray); pw.close(); } catch(Exception err){ System.out.println("Error opening file."); } } } FILE CONTENT READING FROM FILE WITH SCANNER 1. Scanner(file: File): creates a Scanner object using the File object created using a text file. 2. hasNext(): boolean – returns true if there is still data (String) to read from the file. There are similar methods for other data types in Java e.g. hasNextLine() 3. useDelimiter(pattern: String): Scanner – changes the default delimiter (white space) to the pattern. 4. close(): void – close the file after reading to prevent corruption. Note: - The file to be read from must be available to prevent the FileNotFoundException. - The methods already known for reading through Scanner from the keyboard, are the ones used when reading from file. CODE READING FROM EARLIER FILE import java.io.*; import java.util.*; public class Experiment3 { public static void main(String[] args){ try{ File file = new File("Text File.txt"); Scanner sourceFile = new Scanner(file); while(sourceFile.hasNext()) System.out.println(sourceFile.next()); sourceFile.close(); } catch(Exception err){ System.out.println("Error opening file."); } } } OUTPUT 5 234 2.4E-5 2.5 true # String char array When \n is used as delimiter i.e. sourceFile.useDelimiter("\n"); the output will be as given below (pay attention to char array) 5 234 2.4E-5 2.5 true # String char array READING FROM FILE WITH FILEREADER 1. FileReader(file: File) 2. FileReader(filename: String) 3. read(): int – reads and returns the code of a character from the opened file. It returns -1 (EOF End of File code) when there is no character left to read. 4. readLine 5. close(): void – close the opened file at the end of reading. CODE package javafxexample1; import java.io.*; public class Experiment3 { public static void main(String[] args){ try{ File file = new File("Text File.txt"); FileReader sourceFile = new FileReader(file); // read first character code int c = sourceFile.read(); while(c != -1){ System.out.print((char)c); c = sourceFile.read(); } sourceFile.close(); } catch(Exception err){ System.out.println("Error opening file."); } } } OUTPUT 5 234 2.4E-5 2.5 true # String char array PROCESSING BINARY FILES WRITING WITH FileOutputStream This class is a subclass of the abstract class OutputStream and it is that supplies the additional methods listed. 1. FileOutputStream(file: File) 2. FileOutputStream(fileURL: String) 3. FileOutputStream(file: File, append: boolean) 4. FileOutputStream(fileURL: String, append: boolean) Note: a. 1 and 2 will create and open a new file (that overwrites a previously existing one if available) b. 3 and 4 will create and open a new file if it wasn’t existing, and just open a previously existing one for appending new bytes of data. 5. write(data: int) 6. close(): void – closes the stream CODE package javafxexample1; import java.io.*; public class Experiment3 { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream("Test.txt"); for(int i = 'A'; i <= 'Z'; i++) fos.write(i); fos.write('\n'); fos.close(); } catch (Exception err) { } } } STATE OF FOLDER BEFORE EXECUTION STATE OF FOLDER AFTER EXECUTION Test.txt OPENED CODE TO APPEND CONTENT TO AN EXISTING FILE package javafxexample1; import java.io.*; public class Experiment3 { public static void main(String[] args) { try { FileOutputStream fos = new FileOutputStream("Test.txt", true); for(int i = 'a'; i <= 'z'; i++) fos.write(i); fos.write('\n'); fos.close(); } catch (Exception err) { } } } Test.txt OPENED READING WITH FileInputStream This class is a subclass of the abstract class InputStream and it is that supplies the additional methods listed. 1. FileInputStream(file: File) 2. FileInputStream(fileURL: String) 3. read(): int – returns the next byte of data to be read and -1 if there is nothing left. 4. available(): int – returns the number of bytes of data available for reading. 5. close(): void – closes the stream CODE TO READE THE CONTENT OF Test.txt package javafxexample1; import java.io.*; public class Experiment3 { public static void main(String[] args) { try { FileInputStream fis = new FileInputStream("Test.txt"); System.out.println("Available data = " + fis.available()); System.out.println("\nCONTENT OF Test.txt"); int d = fis.read(); while(d != -1){ System.out.print((char)d); d = fis.read(); } } catch (Exception err) { } } } OUTPUT WRITING WITH DataOutputStream The class is a subclass of OutputStream and implements DataOutput which provides methods to write various types of data. 1. DataOutputStream(outStream: OutputStream) – the argument can also be an instance of FileOutputStream 2. writeBoolean(data: boolean): void 3. writeByte(data: int): void – writes the 8 lower bits of data (16 bits) to the stream i.e. the first 8 bits of v. For example, 259, will be written as 3 to the stream. 4. writeBytes(s: String): void – writes the lower byte of the characters in a String to the stream. This implies a character that is represented by more than 1 byte, will be written to the stream as a different character. For example, under normal circumstance (char)359 = ŧ, if written to the stream becomes g. 5. writeChar(c: char): void – writes a character (2 bytes) to the stream 6. writeChars(s: String) – writes every character in s to the stream 7. writeFloat(v: float): void 8. writeDouble(v: double): void 9. writeInt(v: int): void 10. writeLong(v: long): void 11. writeShort(v: short): void 12. writeUTF(s: String): void - The UTF format uses different number of bits to code the characters in a string, unlike what obtains in ASCII and Unicode – 8bits if code <= 0x7F, 16bits if code > 0x7F but <= 0x7FF, and 24bits if code > 0x7FF. READING WITH DataInputStream This class is a subclass of InputStream and implements the interface, DataInput, which provides methods to read (get) native data types (not just integers). 1. DataInputStream(in: InputStream) – InputStream can be replaced with FileInputStream, as argument. 2. readBoolean(): Boolean 3. readByte(): byte 4. readChar(): char 5. readFloat(): float 6. readDouble(): double 7. readInt(): int 8. readLong(): long 9. readShort(): short 10. readLine(): String 11. readUTF(): String When reading multiple data form a binary file the end of the file is indicated by the generation of an EOFExcepton. If all data in a file are to be read using the methods in this class, then the loop that should be used is one with a condition that is always true, and will only stop when the EOFException has been generated. USING FILE CHOOSER DIALOG Using a file dialog window provides a means of choosing the file that will be processed. Using a file chooser requires: 1. FileChooser() - creates a FileChooser object which represents a file chooser dialog. 2. setTitle(title: String): void – sets the title of the dialog 3. showOpenDialog(window: Window): File – shows the dialog to allow the choosing of a file, with window stage or null. Clicking the Open-button after selecting a file, closes the dialog and return a file object representing the file selected for opening, while clicking the Cancel-button will return a null. 4. FileChooser.ExensionFilter(description: String, sample: String) – used to provide options in a combo box in the dialog on the type of file to be chosen. 5. getExtensionFilters(): ObservableList<ExtensionFilter> - returns among other things a means of adding extension filters to the dialog to enable the choosing of certain file types.