Versions Compared

Key

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

...

  • kjøring/oppstart av programmer
  • strukturer av grafiske og interaktive elementer, altså det rent visuelle
  • interaktivitet, altså brukerinput og dynamikk
  • brukergrensesnittdefinisjonsfilerGUI-definisjonsfiler

Et "vanlig" program starter naturlig nok med det første. Så bygges det opp en visuell struktur, med en blanding av rent grafiske elementer og interaktive komponenter (ofte kalt widgets). De interaktive elementene knyttes deretter til programlogikken som skal reagere på og håndtere input fra brukeren. De fleste rammeverk gjør det mulig å beskrive det andre punktet i separate XML-filer (litt som HTML brukes for web-sider), slik at programmet blir mindre og ryddigere.

...

Når brukeren trykker return (for å angi at teksten er ferdigredigert), så genereres en interaksjonshendelser kalt "action" av typen ActionEvent. Denne rapporteres til grensesnittet EventHandler<ActionEvent> og metoden handle(ActionEvent). I praksis betyr dette at grensesnittet med tilhørende metode må implementeres av et objekt, og dette objektet må registreres som lytter. Det vanlige er at lytteren implementeres vha. en såkalt anonym indreklasse, som enkelt sagt er et enkeltobjekt som implementerer grensesnittet uten at en eksplisitt må opprette og navngi en ny klasse. I dette tilfellet gjøres det med følgende kode:

Code Block
languagejava
new EventHandler<ActionEvent>() {
	@Override
	public void handle(ActionEvent actionEvent) {
		... her er selve koden ...
    }
}

...

Mens brukeren redigerer teksten i et tekstfelt, så genereres det kontinuerlig (for hver enkelt-endring) en dataendringshendelser for "text"-egenskapen (eng: property) til tekstfeltet. En slik egenskap kan ses på som en verdi (i dette tilfellet en String) som er innkapslet av et objekt som lar en lese, endre og lytte til endringer. Dette er en generell teknikk som brukes av alle dataorienterte komponenter, for å bruke og redigere data. Denne typen hendelser rapporteres til grensesnittet ChangeListener spesialisert til typen data, i dette tilfellet ChangeListener<String> og metoden changed. Også her er det praktisk å bruke enkeltobjekter som implementerer grensesnittet, i dette tilfellet følgende kode:

Code Block
languagejava
new ChangeListener<String>() {
	@Override
	public void changed(ObservableValue<? extends String> property, String oldValue, String newValue) {
		... her er selve koden ...
	}
}

...

I eksemplet under har vi kombinert både interaksjonshendelser og dataendringshendelser for et tekstfelt i topp-regionen. Hvor hver redigering så kopieres teksten i tekstfeltet til Text-objektet i midt-regionen, og når brukeren trykker return, så gjøres denne teksten om til store bokstaver.

Code Block
languagejava
    @Override
    public void start(Stage stage) throws Exception {
        
        BorderPane root = new BorderPane(); // Root of the scene graph
        
        textField = new TextField("center");
        textField.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent arg0) {
                centerText.setText(textField.getText().toUpperCase());
            }
        });
		textField.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> value, String oldText, String newText) {
                centerText.setText(textField.getText());
            }
        });
		centerText = new Text(180, 180, textField.getText()); // x, y, text
        
        root.setTop(textField);
        root.setBottom(new Text("bottom"));
        root.setLeft(new Text("left"));
        root.setRight(new Text("right"));
        Pane shapesPane = new Pane();
        shapesPane.setPrefSize(300, 300);
        Line line = new Line(10, 10, 100, 100); // x1, y1, x2, y2
        line.getStrokeDashArray().setAll(10.0d, 10.0d); // dashes
        Rectangle rect = new Rectangle(150, 10, 30, 40); // x, y, w, h
        rect.setFill(Color.BLUE);
        Ellipse ell = new Ellipse(40, 180, 40, 30); // cx, cy, rx, ry
        ell.setStroke(Color.RED);
        ell.setStrokeWidth(5);
        ell.setFill(Color.GREEN);
        centerText = new Text(180, 180, "center");
        List<String> fonts = Font.getFamilies();
        centerText.setFont(new Font(fonts.get((int) (Math.random() * fonts.size())), 32));
        shapesPane.getChildren().addAll(line, rect, ell, centerText);
        root.setCenter(shapesPane);
        
        Scene scene = new Scene(root, 500, 500);
    
        stage.setScene(scene);
        stage.setTitle("BorderPaneApplication");
        stage.show();
    }
Image Added

GUI-definisjonsfiler

For større GUI, så blir koden for å bygge opp hierarkiet av grafiske og interaktive elementer fort nokså stor, selv om den ikke er så kompleks. Spesielt når en begynner å endre mange egenskaper, f.eks. farge, strek, font osv. så blir det mye kode og mye "støy". Helt analogt med hvordan HTML brukes til å beskrive strukturen og innholdet til en nettside, så tilbyr JavaFX et XML-format kalt FXML for å beskrive strukturen og innholdet til et GUI. Hierarkiet av XML-elementer (tags) tilsvarer hierarkiet av grafiske og interaktive elementer, og formatet støtter "alle" klasser og typer som er presentert over. En kan også utvide med egne elementer, hvis en lager egne klasser som passer inn i hierarkiet. Den viktigste ulikheten med HTML er at FXML-koden ikke inkluderer programlogikk, det er Java-koden som laster inn FXML-fila. Vi skal ikke gå i dybden på FXML-språket her (se heller http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html), men komme med en generell forklaring.

Det viktigste å vite om FXML er at det bygger direkte på klassene, metodene og attributtene som finnes i JavaFX-rammeverket og at disse brukes som navn på XML-elementer og attributter. Hvis et element heter javafx.scene.layout.Pane, så er det en direkte referanse til JavaFX-klassen Pane-klassen, og som i Java-kode så er det støtte for å importere navn, så en kan bruke kortformen Pane. Tilsvarende så kan en sette teksten i et Text-objekt med XML-attributtet text i (Text-elementet) fordi Text-klassen har en setText-metode. Sammenhengen mellom hva som er gyldig XML og hvilke klasser, metoder og attributter som finnes i JavaFX-rammeverket er sammenfattet i en del relativt enkle regler, så hvis en kjenner JavaFX-API'et, så er det ikke så vanskelig å bli vant med FXML-formatet.

I midten under ser vi det siste eksemplet kodet som FXML, med koden for å laste den inn og knytte lyttere til textfeltet øverst til venstre.

Code Block
languagejava
    @Override
    public void start(Stage stage) throws Exception {
        URL resource = getClass().getResource(this.getClass().getSimpleName() + ".fxml");
        Parent root = FXMLLoader.load(resource);

		textField = (TextField) root.lookup("#textField");
        centerText = (Text) root.lookup("#centerText");

		textField.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent arg0) {
                centerText.setText(textField.getText().toUpperCase());
            }
        });
        textField.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> arg0, String arg1, String arg2) {
                centerText.setText(textField.getText());
            }
        });
        Scene scene = new Scene(root);
    
        stage.setScene(scene);
        stage.setTitle("BorderPaneApplication");
        stage.show();
    }

Innlasting av FXML med FXMLLoader.load-metoden

Code Block
languagehtml/xml
<?xml version="1.0" encoding="UTF-8"?>
 
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.textfield.*?>
<?import javafx.scene.shape.*?>
<?import java.lang.Double?>
 
<BorderPane xmlns:fx="http://javafx.com/fxml"
    prefHeight="500" prefWidth="500">
     <top>
         <TextField id="textField" text="center"/>
     </top>
     <center>
         <Pane>
             <Line startX="10" startY="10" endX="100" endY="100">
                 <strokeDashArray>
                     <Double fx:value="10"/>
                     <Double fx:value="10"/>
                 </strokeDashArray>
             </Line>
             <Rectangle x="150" y="10" width="30" height="40" fill="blue"/>
             <Ellipse centerX="40" centerY="180" radiusX="40" radiusY="30" strokeWidth="5" fill="green" stroke="red"/>
            <Text id="centerText" x="180" y="180" text="center">
                <font>
                    <Font name="Times" size="32"/>
                </font>
            </Text>
         </Pane>
     </center>
     
     <left>
         <Text text="left"/>
     </left>
     
     <right>
         <Text text="right"/>
     </right>
     
     <bottom>
         <Text text="bottom"/>
     </bottom>
     
</BorderPane>