JavaFX

  • 1996: Java 1.0, med AWT
  • 1998: Java 1.2, Swing lansert
  • 2008: JavaFX 1.0, JavaFX Script
  • 2011: JavaFX 2.0, JavaFX Script og JavaFX Mobile droppet.
  • 2012: JavaFX 2.1, støtte for Mac, JavaFX 2.2, støtte for Linux
  • 2014: JavaFX 8, JavaFX integrert i Java SE
  • 2015: JavaFX får omsider egne dialogbokser...

Java GUI kort historie

Nylig oppdatert til JavaFX 8, så vi må ha Java 8. Sjekk i terminalen:

$ java -version
java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

Versjonen bør minimum være 1.8.0_40

import javax.swing.*;
import javax.swing.border.*;

public class HelloSwing {
    public static void start() {
        JFrame frame = new JFrame("myApp");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JLabel label = new JLabel("Hello World!");
        label.setBorder(new EmptyBorder(50, 50, 50, 50));
        frame.add(label);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(HelloSwing::start);
    }
}

Hello World i Swing

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.layout.BorderPane;
import javafx.geometry.Insets;

public class HelloWorld extends Application {
    public void start(Stage stage) {
        Text text = new Text("Hello World!");
        BorderPane pane = new BorderPane(text);
        pane.setPadding(new Insets(50));
        Scene scene = new Scene(pane);

        stage.setTitle("myApp");
        stage.setScene(scene);
        stage.show();
    }

    // trengs vanligvis ikke:
    public static void main(String[] args) {
        launch(args);
    }
}

Hello World i JavaFX

javafx.application.Application

Dokumentasjon

  • Grunnklassen for programmer i JavaFX
  • Har en ferdigdefinert main-metode (som vi ikke bruker)
  • Har tre metoder vi i stedet bruker:
  • ​public void init()
  • public void start(Stage primaryStage)
  • public void stop()

IMPLEMENTASJON MÅ VÆRE PUBLIC!

Finne dokumentasjon til JavaFX

Google, søk med:

javafx 8 api klassenavn

public class ClickyApp extends Application {
    @Override
    public void start(Stage stage) {
        Button button = new Button("Click me!");
        button.setOnAction(this::handleButtonClick);
        BorderPane pane = new BorderPane(button);
        pane.setPadding(new Insets(50));
        Scene scene = new Scene(pane);

        stage.setTitle("Clicky app");
        stage.setScene(scene);
        stage.show();
    }

    public void handleButtonClick(ActionEvent event) {
        new Alert(Alert.AlertType.INFORMATION,
            "You clicked the button!").showAndWait();
    }
}

Reagere på knappetrykk

* imports utelatt fra nå av, full kode her:

https://github.com/evestera/1010-pusle

SceneBuilder

SceneBuilder

Lager GUI som lagres som FXML-kode. Denne FXML-koden kan du kan laste med Java-kode.

 

Ikke lenger støttet av Oracle (som lager Java), men firmaet Gluon har tatt over ansvaret.

 

Nedlasting:

http://gluonhq.com/products/downloads/

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>

<BorderPane xmlns="http://javafx.com/javafx/8.0.40"
    xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="FxmlExample">
   <center>
      <Button text="Click me!" onAction="#handleButtonClick" />
   </center>
   <padding>
      <Insets bottom="50.0" left="50.0" right="50.0" top="50.0" />
   </padding>
</BorderPane>

FXML-kode, kan enten skrives for hånd eller genereres med SceneBuilder:

* basert på ClickyApp-eksempelet

public class FxmlExample extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        URL fxml = FxmlExample.class.getResource("FxmlExample.fxml");
        Parent pane = FXMLLoader.load(fxml);
        Scene scene = new Scene(pane);

        stage.setTitle("Clicky app");
        stage.setScene(scene);
        stage.show();
    }

    public void handleButtonClick(ActionEvent event) {
        new Alert(Alert.AlertType.INFORMATION,
            "You clicked the button!").showAndWait();
    }
}

Så laster vi koden på denne måten med FXMLLoader.load():

Properties

De aller fleste elementer i JavaFX har en spesiell type variabler som er av klassen javafx.beans.property.Property

Properties kan vi legge lyttere på, så vi kan gjøre ting når de endrer seg.

Vi kan også koble Properties sammen med bind(), slik at når den ene endrer seg blir den andre oppdatert.

public class PropertyExample extends Application {
    public void start(Stage stage) {
        TextField field = new TextField();
        Text text = new Text();

        text.textProperty().bind(field.textProperty());

        VBox box = new VBox(10, field, text);
        box.setPadding(new Insets(20));

        Scene scene = new Scene(box);

        stage.setTitle("Property Example");
        stage.setScene(scene);
        stage.show();
    }
}

Eksempel på kobling med properties

Å gjøre ting i bakgrunnen

Hvis vi vil gjøre ting i bakgrunnen i en JavaFX-applikasjon kan vi bruke javafx.concurrent.Task

Task har Properties som progress og value vi enkelt kan oppdatere fra innsiden.

Disse kan vi lytte til og bruke med bind() fra utsiden til å oppdatere GUI når det trengs.

public class HelloTask extends Application {
    Button button;
    ProgressBar progress;

    @Override
    public void start(Stage stage) {
        GridPane grid = new GridPane();
        grid.setAlignment(Pos.CENTER);
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(25, 25, 25, 25));

        button = new Button("Click me!");
        button.setOnAction(this::handleClick);
        grid.add(button, 0, 0);

        progress = new ProgressBar();
        progress.setProgress(0);
        grid.add(progress, 0, 1);

        Scene scene = new Scene(grid);

        stage.setTitle("Hello Task!");
        stage.setScene(scene);
        stage.show();
    }
// ...
// ...
    ExecutorService executor = Executors.newCachedThreadPool();

    @Override
    public void stop() {
        executor.shutdown();
    }
// ...

Vi må selv sette opp en bakgrunnsarbeider som kan ta imot oppgavene våre.

 

Det gjør vi enkelt med java.util.concurrent.Executors

 

Når programmet lukkes må vi også stoppe arbeideren.

// ...
    public void handleClick(ActionEvent event) {
        Task<String> task = new Task<String>() {
            @Override
            protected String call() throws Exception {
                updateValue("Working...");

                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return "Interrupted!";
                    }
                    updateProgress(i + 1, 10);
                }
                return "Done!";
            }
        };
        progress.progressProperty().bind(task.progressProperty());
        button.textProperty().bind(task.valueProperty());

        executor.submit(task);
    }
}

Denne presentasjonen:

https://slides.com/evestera/javafx/

Kode fra presentasjonen:

https://github.com/evestera/1010-pusle

JavaFX

By Erik Vesteraas

JavaFX

  • 2,167