Java Programming Java FX
Java APIs for Building GUIs AWT Swing SWT Eclipse JFace JavaFX Other
Building a GUI Components Components Containers Graphics Layouts Events and Listeners Look and Feel Threads Architectural Models: MV, MVC
Java FX http://docs.oracle.com/javafx/ Included in java. Have to use a specific jar not automatically loaded jfxrt.jar Possible future replacement for Swing
Java FX Application Javafx.application.Application init() start(Stage stage) - abstract stop() Application FX thread : init does not run on thread, start runs on the thread => Stage should be constructed inside the start method
Sample Application public class MyApp extends Application { public void start(Stage stage) { Circle circ = new Circle(40, 40, 30); Group root = new Group(circ); Scene scene = new Scene(root, 400, 300); stage.setTitle("My JavaFX Application"); stage.setScene(scene); stage.show(); }
GUI Components javax.scene.control http://download.oracle.com/otndocs/products/javafx/2.2/samples/Ensemble/index.html#SAMPLES/ Node: base class for everything composing the GUI hierarchy ImageView: contains Image MediaView: plays media WebWiew: display the web content managed by a WebEngine Canvas: an image that can be drawn through a drawing context Chart, Axis Control: base class for all controls Button, CheckBox, RadioButton, ToggleButton ComboBox, ColorPicker, ChoiceBox Label TextField, TextArea, PasswordField ScrollBar, Slider ListView, TableView, TreeView, Cell Menu, Menu Item, MenuBar, Separator
Components Containers Window Stage Scene Region: can be styled with CSS->Chart, Pane TabPane, Tab
Graphics Shape: a type of Node Arc Circle CubicCurve, QuadCurve (Bezier) Ellipse Line Path Polygon Polyline Rectangle Text
GraphicsContext Node ->Canvas Canvas.getGraphicsContext2D Drawing on a Canvas in a buffer Draw arcs, images, curves, rectangles, polygons, etc Also allows for transformations
Layouts javafx.layout.Pane: absolute positioning AnchorPane: anchor children inside the pane (setTop/Bottom/Left/RightAnchor(Node child, Double value) BorderPane: provides 5 positioning areas: setTop/Bottom/Left/Right/Center(Node child) FlowPane: place children in the order they come wrapping at the edge of the pane GridPane: place children in a matrix: setRow/ColumnIndex(Node child, int index) HBox: place children in a single horizontal row StackPane: place children on top of each other TilePane: place children in a grid with equally sized tiles VBox: place children in a single vertical row
Sample GridPane //create the root of the scene GridPane root = new GridPane(); ColumnConstraints col1 = new ColumnConstraints(); col1.setPercentWidth(25); ColumnConstraints col2 = new ColumnConstraints(); col2.setPercentWidth(75); col2.setHgrow(Priority.ALWAYS); root.getColumnConstraints().addAll(col1, col2); MenuBar menuBar = new MenuBar(); GridPane.setRowIndex(menuBar, 0); GridPane.setColumnIndex(menuBar, 0); GridPane.setColumnSpan(menuBar, 2); … root.getChildren().addAll(menuBar, commandsListView, toolBar, blocksPane);
Events and Listeners javax.event.Event ActionEvent javax.scene.input.InputEvent (DragEvent, KeyEvent, MouseEvent, TouchEvent) javax.scene.control.ListView.EditEvent javax.scene.control.TableColumn.CellEditEvent javax.stage.WindowEvent javax.event.EventType: Nodes define their relevant EventType (s) ListView.EDIT_START_EVENT or Events define relevant event types: KeyEvent.KEY_PRESSED javax.event.EventHandler Node.addEventHandler(EventType, EventHandler)
Listening on a Button Button moveActionButton = new Button("Move"); moveActionButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { if (guiController != null){ guiController.moveActionHandler(); } }); //setOnXXX(EventHandler<? extends Event>)
Look and Feel CSS (W3C CSS version 2.1) Scene scene = new Scene(new Group(), 500, 400); scene.getStylesheets().add("path/stylesheet.css"); Scene scene = new Scene(new Group(), 500, 400); scene.getStylesheets().add("path/stylesheet.css"); Scene scene = new Scene(new Group(), 500, 400); scene.getStylesheets().add("path/stylesheet.css"); Scene scene = new Scene(new Group(), 500, 400); scene.getStylesheets().add("path/stylesheet.css"); Scene scene = new Scene(new Group(), 500, 400); scene.getStylesheets().add("path/stylesheet.css"); Button buttonAccept = new Button("Accept"); buttonAccept.getStyleClass().add("button1"); Button buttonAccept = new Button("Accept"); buttonAccept.getStyleClass().add("button1"); //setStyle Button buttonAccept = new Button("Accept"); buttonAccept.getStyleClass().add("button1"); Button buttonAccept = new Button("Accept"); buttonAccept.getStyleClass().add("button1"); Button buttonAccept = new Button("Accept"); buttonAccept.getStyleClass().add("button1"); .root{ -fx-font-size: 16pt; -fx-font-family: "Courier New"; -fx-base: rgb(132, 145, 47); -fx-background: rgb(225, 228, 203); } .root{ -fx-font-size: 16pt; -fx-font-family: "Courier New"; -fx-base: rgb(132, 145, 47); -fx-background: rgb(225, 228, 203); } .root{ -fx-font-size: 16pt; -fx-font-family: "Courier New"; -fx-base: rgb(132, 145, 47); -fx-background: rgb(225, 228, 203); } .root{ -fx-font-size: 16pt; -fx-font-family: "Courier New"; -fx-base: rgb(132, 145, 47); -fx-background: rgb(225, 228, 203); } .button .check-box .scroll-bar .button .check-box .scroll-bar .button .check-box .scroll-bar #my-button #shaded-hbox #my-button #shaded-hbox
Threads Main application thread public class ApplicationController extends Application public static void main(String[] args) { launch(args); } JavaFX application thread: the same with the main application thread Most of the GUI related operations should happen on this thread Background threads: execute larger tasks and communicates progress
Worker(s) Task final MoveCommand mc = new MoveCommand(null, blockToMove, onTopOf); //create a task to perform this command in order not to block the GUI final Task<ObservableList<ICommand>> executingTask = new Task<ObservableList<ICommand>>() { @Override protected ObservableList<ICommand> call() throws Exception { try{ //need to catch any exception here commandsManager.performCommand(mc); }catch(Exception e){ logger.severe(e.toString()); } return commandsHistoryList; };
Task progress Task<Integer> task = new Task<Integer>() { @Override protected Integer call() throws Exception { int iterations; for (iterations = 0; iterations < 10000000; iterations++) { if (isCancelled()) { updateMessage("Cancelled"); break; } updateMessage("Iteration " + iterations); updateProgress(iterations, 10000000); return iterations; }; ProgressBar bar = new ProgressBar(); bar.progressProperty().bind(task.progressProperty()); new Thread(task).start();
Properties ObservableValue<T>: addListener(ChangeListener), T getValue() WritableValue setValue(T) bind (ObservableValue<? Extends T>) bindBidirectional(Property<T>)
Bindings Calculate values depending on one or more sources Binding extends ObservableValue thus having the getValue() operation and listeners on changes ObservableList<?> getDependencies() void invalidate() : mark the binding as invalid Various methods return bindings in order to allow reactions on some value modification events (see next slide)
Sample Binding public class Main { public static void main(String[] args) { IntegerProperty num1 = new SimpleIntegerProperty(1); IntegerProperty num2 = new SimpleIntegerProperty(2); NumberBinding sum = num1.add(num2); System.out.println(sum.getValue()); num1.set(2); }
Invalidations Observable: addListener(InvalidationListener) Lazy behavior: implementations of Observable may be lazy- content not recomputed until requested
MVC http://msdn.microsoft.com/en-us/library/ff649643.aspx
MVC Collaborations http://msdn.microsoft.com/en-us/library/ff649643.aspx
BlocksWorld MVC
Application setup @Override public void init() throws Exception { super.init(); //The logger logger = Logger.getAnonymousLogger(); //The model commandsManager = new CommandsManager(); //The GUI view guiApp = new MainGUI(); //The GUI controller guiController = new GUIController(guiApp, commandsManager, logger); //Link the GUI view with its controller guiApp.setGuiController(guiController); //The Console view cmdApp = new MainCmd(); //The Console controller cmdController = new CmdController(cmdApp, commandsManager, logger); //Link the Cmd view with its controller cmdApp.setCmdController(cmdController); }
Startup and cleanup @Override public void start(Stage primaryStage) { //start the gui part guiApp.initialize(primaryStage); //give a chance to the gui controller to prepare things guiController.initialize(); primaryStage.show(); //start the console part cmdController.initialize(); cmdApp.start(); } public void stop() throws Exception { super.stop(); guiController.cleanup(); cmdController.cleanup();
GUI Controller Initialization public void initialize(){ //relate the commands list in the gui with the commands history commandsHistoryList = FXCollections.observableList( commandsManager.getCommands()); guiView.getCommandsListView().setItems(commandsHistoryList); //register the commands executions listener commandsManager.registerListener(commandsExecutionsListener); // creates the binding for the commands history list in the view //in order to invalidate it on execution updates listBinding = new ListBinding<ICommand>() @Override protected ObservableList<ICommand> computeValue() { //the result is not quite important here return commandsHistoryList; } }; guiView.getCommandsListView().itemsProperty().bind(listBinding);
GUI Updates private class CommandsExecutionsListener implements ICommandsManagerListener{ @Override public void executedCommand(ICommand command) { //run on the FX GUI thread Platform.runLater(new Runnable() { public void run() { //invalidate the list binding in order to enforce //the historical commands list in the GUI to update listBinding.invalidate(); } });