Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Excerpt

JavaFX er ment å være Java Swing sin etterfølger som det foretrukne rammeverk for grafikk og brukergrensesnitt. JavaFX er gjør det bl.a. enklere å kombinere grafikk og interaktive komponenter, har bedre støtte for rik grafikk og animasjon, gjør det lettere å skille funksjon og det rent visuelle.

...

Et GUI-rammeverk er en samling klasser som gjør det mulig å bygge rike, funksjonelle grafiske brukergrensenitt uten alt for mye arbeid. Brukergrensesnitt bygget med slike rammeverk har noenlunde samme struktur, og her skal vi gi en liten introduksjon med utgangspunkt i JavaFX-rammeverket. Vi har valgt JavaFX, fordi det er mer fleksibelt og moderne enn Java Swing, som det er ment å ta over etter og det ligner mer på andre "rammeverk" som HTML5 og Flash.

Merk at selv om JavaFX er en standard del av både Java 7 -installasjonenog Java 8, men merk at om du bruker Java 7, så må du selv legge til en JavaFX-jar i Build Path for prosjektet ditt, for å kunne bruke JavaFX-klassene. Denne heter jfxrt.jar og finnes i jre/lib-mappen i Java-installasjonen din. Et alternativ er å installere e(fx)clipse-tillegget til Eclipse, som bidrar med en veiviser for å opprette JavaFX-prosjekter.

...

  • 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.

...

Det er få om ingen begrensninger på hva som kan være inni hva, og sammen så forteller denne strukturen akkurat hva som skal vises på skjermen for brukeren. Dersom en underveis i kjøringen endrer på noen av objektene, f.eks. endrer posisjoner, farger, fonter eller legger til og/eller fjerner elementer, så oppdateres skjermen tilsvarende. Man slipper altså å forholde seg til primitive tegnemetoder, men manipulerer istedet på objektstruktere objektstrukturer og lar systemet ta seg av detaljer omkring hvordan disse tegnes (såkalt rendering).

...

Nyttige figurer er strek (Line), rektangel (Rectangle), sirkel og ellipse (Circle og Ellipse), buesegment (Arc), polygon (Polygon), segmentert figur (Path) og tekst (Text). I tillegg er det en egen klasse for bilder (ImageView som viser et Image, som ikke er en Shape). Noen av disse er vist i eksemplet under, hvor center-regionen er fylt med et Pane-objekt med noen figurer i, med ulike grafiske effekter.

Code Block
languagejava
public class BorderPaneShapesApplication extends Application {
    
    @Override
    public void start(Stage stage) throws Exception {
        
        BorderPane root = new BorderPane(); // Root of the scene graph
        
        // Add one Text node in each surrounding region
        root.setTop(new Text("top"));
        root.setBottom(new Text("bottom"));
        root.setLeft(new Text("left"));
        root.setRight(new Text("right"));
        
        Pane shapesPane = new Pane();
        shapesPane.setPrefSize(300, 300);
        shapesPane.getChildren().addAll(
                new Line(10, 10, 100, 100),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
                new Text(180, 180, "center") // x, y, textell.setStroke(Color.RED);
        ell.setStrokeWidth(5);
        rootell.setCentersetFill(shapesPaneColor.GREEN);
        
Text text = new Text(180, 180, "center");
   Scene scene = new Scene(root, 500, 500  List<String> fonts = Font.getFamilies();
    
    text.setFont(new Font(fonts.get((int) (Math.random() * stagefonts.setScene(scenesize())), 32));
        stage.setTitle("BorderPaneApplication"shapesPane.getChildren().addAll(line, rect, ell, text);
        stageroot.showsetCenter(shapesPane);
       } 
    public static void main(String[] args) {    Scene scene = new Scene(root, 500, 500);
    
        launch(BorderPaneShapesApplication.class, argsstage.setScene(scene);
    }
}
Image Removed

 

    stage.setTitle("BorderPaneApplication");
        stage.show();
    }
Image Added

Les mer om enkel 2d-grafikk her: Enkel 2D-grafikk med JavaFX og FXML

Grafisk Grafisk stil med CSS (Cascading Style Sheets)

...

De interaktive komponentene har en felles måte å rapportere hva brukeren gjør, gjennom såkalte hendelser (eng: event). Hendelsene kan fortelle om både elementære ting (såkalte leksikalske hendelser) som at muspekeren flyttes eller at en tast trykkes ned, eller mer høynivå ting (såkalte syntaktiske hendelser) som at en knapp trykkes, meny- eller liste-element velges, tekst redigeres osv. For å motta en hendelse, så må applikasjonen registrere såkalte lyttere, dvs. objekter som implementerer spesifikke lytter-grensesnitt og som får beskjed om hendelsene ved at spesifikke metoder kalles.

Lyttere kan registreres på en komponent på to måter:

generell addEventHandler-metode - denne tar inn en hendelsestype og lytter-objektet og registrerer lytteren for den angitte hendelsestypen, f.eks. addEventHandler(ActionEvent.ACTION, actionEventHandler)

spesielle setOnXXX-metoder - disse er for spesifikke hendelsestyper og tar inn en lytter og registrere den for denne hendelsestyper, f.eks. setOnAction(actionEventHandler)

I eksemplet under har vi lagt inn et tekstfelt i topp-regionen og reagerer på return ved å kopiere teksten i tekstfeltet inn i Text-objektet i midt-regionen.

...

Det er to hovedkategorier hendelser, interaksjonshendelser og dataendringshendelser, og disse bruker hver sine lyttergrensesnitt og metoder for å registrere lyttere. La oss ta et tekstfelt som eksempel, siden rapporterer hendelser av begge typer:

Interaksjonshendelsen "action"

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 ...
    }
}

Denne lytter(grensesnitt-instans)en oppgis direkte som argument til metoden som registrerer lytteren. Det finnes to (typer) metoder som brukes til dette:

  • en generell addEventHandler-metode, som tar inn en hendelsestype og lyttereren, i dette tilfellet addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {... });
  • spesielle setOnXXX-metoder, som brukes for spesifikke hendelsestyper og tar inn en lytter, i dette tilfellet setOnAction(, new EventHandler<ActionEvent>() {... })

I koden nedenfor har vi brukt setOn-varianten, siden den er enklest å lese og skrive.

Dataendringshendelse for "text"-egenskapen

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 ...
	}
}

Denne lytter(grensesnitt-instans)en oppgis (som over) direkte som argument til metoden som registrerer lytteren. Metoden er en del av objektet som innkapsler String-verdien, så følgende kode brukes: textProperty().addListener(new ChangeListener<String>() {... }); textProperty()-kallet returnerer text-egenskapen og det er denne som registrerer lytteren med addListener.

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.

Under ser vi det siste eksemplet kodet som FXML.

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>