Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

JavaFX har støtte for egne såkalte GUI-definisjonsfiler av typen FXML, som er XML-filer som beskriver innholdet i et GUI. Den hierarkiske strukturen av XML-elementer (tags) tilsvarer den hierarkiske strukturen av GUI-objekter, bestående av vinduer, paneler og enklere interaktive elementer som lister, tekstfelt og knapper. Ved oppstart av JavaFX-applikasjonen lastes FXML-fila inn og alle GUI-objektene opprettes. Fordelen er at FXML-fila er lettere å skrive og vedlikeholde enn Java-koden som trengs for å opprette den tilsvarende strukturen. Under vises et GUI med kun et tekstfelt og en knapp, FXML-fila som beskriver strukturen og java-koden for å laste inn FXML-fila.

<?xml version="1.0" encoding="UTF-8"?> <?emfs /fxcontroller/.fxml; ?> <?import javafx.scene.control.TextField?> <?import javafx.scenecontrol.Button?> <?import javafx.scene.layout.HBox?> <HBox xmlns:fx="http://javafx.com/fxml"> <TextField text="Type something here!"/> <Button text="Click me!"/> </HBox>
Code Block
Example1.fxml
languagejavafxjava
title
Example1
.
Code Block
languagejavafx
titleExample1.java
package fxcontroller;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Example1 extends Application {
    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load(this.getClass().getResource("Example1.fxml"));
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}

Kobling mellom FXML-filer og kontroller-logikk

...

 

 

Code Block
languagejavafx
titleExample2Example1.fxmllinenumberstrue
<?xml version="1.0" encoding="UTF-8"?>
<?emfs /fxcontroller/Example2Example1.fxml; ?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>

<HBox xmlns:fx="http://javafx.com/fxml">
    <TextField fx:id="textField" text="Type something here!"/>
    <Button text="Click me!" onAction="#handleUpcaseAction"/>
</HBox>

 

 

Kobling mellom FXML

...

-filer og kontroller-logikk

Det er praktisk at GUI-strukturen automatisk bygges, men det blir samtidig upraktisk å få tak i (referanser) til de interaktive GUI-elementene, så en kan få lagt til lyttere og implementert kontroller-logikk. Dersom en f.eks. skal reagere på trykk på "Click me!"-knappen, så må en lete gjennom children-lista til root-elementet og finne knappen, for så å bruke cast og legge til lytteren. Heldigvis har FXML støtte for nettopp å koble elementer i GUI-strukturen til kontroller-logikk, som vist under:

Code Block
languagejavafx
titleExample2.java
linenumberstrue
package fxcontroller;

import java.io.IOException;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class Example2 extends Application {
    @Override
    public void start(Stage primaryStage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setController(this);
        Parent root = (Parent) fxmlLoader.load(this.getClass().getResourceAsStream("Example2.fxml"));
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
    @FXML
    private TextField textField;

	@FXML
    public void handleUpcaseAction(ActionEvent event) {
        textField.setText(textField.getText().toUpperCase());
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

 

FXML-fil for eksemplet. XML-elementer (tags) svarer stort sett til GUI-elementer, hvor tag-navnet svarer til GUI-klassen.

Code Block
languagejavafx
titleExample2.fxml
linenumberstrue
<?xml version="1.0" encoding="UTF-8"?>
<?emfs /fxcontroller/Example2.fxml; ?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns:fx="http://javafx.com/fxml">
    <TextField fx:id="textField" text="Type something here!"/>
    <Button text="Click me!" onAction="#handleUpcaseAction"/>
</HBox>

 

TIl venstre: Kode for innlasting av FXML-fil. Koden this.getClass().getResourceAsStream("Example2.fxml") brukes for å lese inn en fil som ligger i samme mappe/pakke som klassen.

Teknikken er basert på at en FXMLLoader-instans leser inn FXML-fila og underveis/i etterkant kobler elementer i FXML-fila til felt og metoder i et kontroller-objekt:

  • I linje 5 17 i Example2.java registreres applikasjonsobjektet som kontrolleren for GUI-elementene som bygges basert på FXML-fila.
  • I linje 8 7 i Example2.fxml så brukes fx:id for å knytte en id til TextField-objektet. Merk at "fx" først må være registrert som XML-namespace i en xmlns-deklarasjon, slik det gjøres i linje 7.
  • I linje 11 22 og 12 23 i Example2.java deklareres et felt med en @FXML-annotasjon. Annotasjonen forteller FXMLLoader-en at feltet skal settes til GUI-elementet med tilsvarende id.
  • I linje 9 8 i Example2.fxml så brukes onAction-attributtet for å angi handleUpcaseAction som kontroller-metoden for action-hendelsen til knappen. Merk #-tegnet foran metodenavnet.
  • I linje 14 25 og 15 26 i Example2.java så deklareres en metode med en @FXML-annotasjon for å håndtere action-hendelsen. Merk at vi egentlig kan utelate parameteret, hvis det ikke brukes (som her).

...

Ulempen med flere klasser oppveies av at begge er en klart definert rolle. Det er også mulig å la FXMLLoader-en selv opprette kontroller-instansen, basert på et eget fx:controller-attributt på toppnivå-elementet i FXML-fila:

...

languagejavafx
titleExample4.fxml

...

:

...

 

...

Code Block
languagejavafx
titleExample4.java
package fxcontroller;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Example4 extends Application {
    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load(this.getClass().getResource("Example4.fxml"));
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}
Code Block
languagejavafx
titleExample4.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?emfs /fxcontroller/Example4.fxml; ?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>

<HBox xmlns:fx="http://javafx.com/fxml"
    fx:controller="fxcontroller.Example3Controller">
    <TextField fx:id="textField" text="Type something here!"/>
    <Button text="Click me!" onAction="#handleUpcaseAction"/>
</HBox>

 

 

Vi ser at navnet til kontroller-klassen brukes i FXML-fila, slik at FXMLLoader-en selv kan opprette kontroller-instansen. Kontroller-klassen er uendret, mens applikasjonsklassen blir litt enklere, f.eks. så kan den statiske load-metoden i FXMLLoader-klassen brukes‚ siden vi ikke trenger å registrere kontrolleren.

...