...
Excerpt |
---|
JavaFX er Java sitt nye rammeverk for å lage og beskrive grafiske brukergrensesnitt (GUI). Her gir vi en kort introduksjon til å lage enkle GUI med JavaFX vha. Eclipse med e(fx)clipse-tillegget. |
JavaFX og det tilhørende filformatet FXML og verktøyet SceneBuilder, gjør det relativt enkelt å lage fasaden til app-er. Med Eclipse-tillegget e(fx)clipse så kan du faktisk lage fasaden uten programmering, og så trinnvis gjøre app-en funksjonell ved å koble elementene i fasaden, så som knapper og tekstfelt, til selve (koden for) app-logikken. Her viser vi hvordan dette gjøres for en enkel app basert på Counter-logikken i Tilstand og oppførsel.
Counter-appen
Tanken er nå å lage en app som lar oss opprette og initialisere, lese og endre tilstanden til et Counter-objekt. Vi skal starte med en enkel variant, som lar oss lese og endre tilstanden til et forhåndsopprettet Counter-objekt, og så utvider vi den til å støtte å lage nye Counter-objekter.
...
Enkel app for å telle opp telleren til et Counter-objekt, som er laget på forhånd med en grense på 35. Trykk Count-knappen for å telle opp. |
...
FXML-filer kan redigeres som tekst i Eclipse. Prøv f.eks. å erstatte <!-- ... --> med <Button text="Count"/> (du trenger også et import-element for javafx.scene.control.Button). Ved kjøring vil du da få opp et vindu med en Count-knapp i (inni et HBox-panel). Hvis en redigerer FXML som tekst på denne måten, kan det være nyttig å åpne JavaFX Preview-panelet med Window > Show View > Other... > JavaFX Preview. Dette panelet viser hele tiden hva innholdet i den aktive FXML-editoren betyr, altså slik det vil se ut hvis FXML-koden kjøres. Figuren under viser koden for HBox-panelet med en Count-knapp i og JavaFX Preview-panel. NB: JavaFX Preview-panelet fungerer ikke i 2019-utgaven av faget og Eclipse
For en så liten app er det greit å redigere FXML-koden som tekst, men generelt er det greiere å bruke applikasjonen SceneBuilder. Høyreklikk på fila (installer først ved å følge lenka og instruksjonene) og velg Open with SceneBuilder, og du vil kunne redigere FXML-koden grafisk. SceneBuilder bruker FXML som lagringsformat, men lar deg redigere innholdet vha. direkte manipulasjon, tekstfelt, menyer og dialoger.
...
FXML- og Java-kode | Forklaring | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
| FXML-koden inneholder et fx:controller-attributt, som angir (det fulle) navnet til kontroller-klassen. Når FXML-koden kjøres (lastes inn og vises), så vil det automatisk lages et objekt av denne klassen. Her angis at kontroller-klassen er counter.CounterController-objekt og dermed vil et objekt av denne klassen styre appen. CounterController-objektet gis sjansen til å utføre initialiseringskode, ved at en metode med signaturen void initialize() og annotert med @FXML (egentlig javafx.fxml.FXML) automatisk kalles (hvis den finnes). Annotasjonen står foran metoden og en import-setningen gjør at vi slipper å bruke det fulle navnet. Her opprettes det et Counter-objekt med 5 som øvre grense for telleren, og en får koblingen som vist under.
CounterController-objektet skal bl.a. oppdatere Text-objektet som viser counter-verdien. For å kunne gjøre det, så må CounterController-objektet ha en referanse til Text-objektet, altså et felt av typen Text (egentlig javafx.scene.text.Text), slik at en får koblingene vist under.
Selve koblingen opprettes automatisk av JavaFX ved kjøring av FXML-en vha. to elementer i koden:
Merk at navnet på feltet kan være hva som helst, bare det stemmer med fx:id-attributtet og felt-typen er riktig. Dersom noe ikke stemmer, så vil det bli markert som en feil i FXML-koden av editoren. Det siste som trengs er kode som håndterer Count-knappen og koblingen mellom knappen og koden. Dette håndteres med to elementer i koden:
handleCountAction-metoden sørger for å øke telleren, med counter.count(), og oppdatere teksten som vises av Text-objektet, med counterOutput.setText(...). Som over så spiller ikke navnet (på metoden) noen rolle, bare det stemmer med verdien til attributtet som bruker navnet. onAction er attributtet som brukes av Button-klassen når knappen trykkes, og hver type interaktive JavaFX-objekt har sine on-attributter for de ulike typene interaksjon den støtter. Hvis kontroller-objektet skal reagere på en annen type input, så brukes tilsvarende on-attributt.
|
Counter-app - komplett variant
...
En mer komplett app for å lage nye Counter-objekter og telle.
...
De spesielle attributtene som trengs i FXML-koden, altså fx:controller, fx:id og on..., kan legges inn i FXML-editoren i Eclipse eller i tilsvarende tekstfelt i SceneBuilder. fx:controller legges inn Controller-seksjonen nederst til venstre, mens fx:id og on...-attributtene legges inn i Code-seksjonen ned til høyre.
Counter-app - komplett variant
En mer komplett app for å lage nye Counter-objekter og telle dem opp. I starten har en ikke noe Counter-objekt. En ny lages ved å fylle inn den øvre grensa i tekstfeltet og trykke New Counter-knappen. Tilstanden til dette Counter-objektet vises på linja under og en kan telle opp telleren ved å trykke Count-knappen. |
Som over så starter vi med å redigere FXML-koden, slik at vi får en linje med et tekstfelt, av typen TextField, og en knapp til. For å få ønsket layout, med to rader, så må vi i tillegg ha en HBox rundt tekstfeltet og knappen, og en VBox (vertical box) utenpå den eksisterende HBox-en og den nye. Under til venstre vises ønsket struktur, og til høyre en kort forklaring på hvordan få det til.
En kan pakke (eng: wrap) et VBox-panel rundt eksisterende elementer ved å velge disse, høyreklikke og velge Wrap In > VBox. Et nytt HBox-panel legges inn ved å velge Container-seksjonen i paletten og dra og slippe et HBox-objekt inn VBox-objektet, men over det eksisterende HBox-panelet. Markøren gir tilbakemelding om hvor objektet havner når du slipper, men det er lett å bomme. Da må en i så fall dra og slippe objektet innen hierarkiet til det havner på rett plass. TextField- og Button-objektene finner du begge i Controls-seksjonen, som generelt har interaktive elementer. (Text-objektet var i Shapes-seksjonen, siden det er passiv grafikk.) I panelet til venstre er forøvrig visning av fx:id slått på, ved å velge Hierarchy displays > fx:id i nedtrekksmenyen markert med tannhjul. Vi ser det allerede er lagt inn en fx:id for tekstfeltet med navn endInput, som betyr at vi må ha et tilsvarende felt i CounterController-klassen. Dette trengs fordi vi må kunne lese teksten ut av feltet fra CounterController-objektet, når vi skal lage et nytt Counter-objekt med en spesifikk øvre grense. |
Under er FXML- og Java-koden for den utvidete varianten vist, med forklaring til høyre.
FXML- og Java-kode | Forklaring | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
| FXML-koden har blitt litt større, dels fordi vi har utvidet hierarkiet med et nivå til og lagt til ekstra elementer, og dels fordi SceneBuilder har lagt til ekstra <children>-elementer og noen attributter. <children>-elementene er egentlig implisitt inni panel-elementer og dermed overflødige her, men det er greit å la dem stå. Det viktigste er at det nye TextField-elementet har et fx:id-attributt og det nye Button-elementet et onAction-attributt. Java-koden har tilsvarende et nytt felt av typen TextField med navn tilsvarende fx:id-attrbibuttet og og en metode med navn tilsvarende onAction-attributtet. Og begge har med @FXML-annotasjonen, som angir at de brukes av JavaFX og FXML-mekanismen. Java-koden har forøvrig fått følgende endringer:
|
Når du kjører FXML-koden, så kan du forresten legge merke til to feil/mangler:
- Det sjekkes ikke om den øvre grensa i tekstfeltet faktisk er et gyldig tall. Hvis en f.eks. skriver fem i stedet for 5 og trykker på New Counter-knappen så skjer det tilsynelatende ingenting. Det er fordi konverteringskoden i Integer.valueOf-metoden kræsjer og det derfor ikke legges inn noe nytt Counter-objekt. Det hadde vært bedre om en hele tiden sjekket om teksten var gyldig og evt. markerte det med farge. New Counter-knappen kunne dessuten blir deaktivert, hvis teksten var ugyldig.
- En kan trykke på Count-knappen selv om det ikke er laget noe Counter-objekt ennå. counter.count()-kallet vil kræsje fordi counter-feltet er null. Også her vil det være bedre om knappen var deaktivert, inntil et Counter-objekt faktisk var laget.
Prøv gjerne å fikse disse problemene, men merk at løsningen på det første problemet er mer komplisert enn en skulle tro. Det andre problemer er enklere å løse... hint: gjør det mulig å nå knappen vha. et fx:id og et felt, og bruk Button sin setDisabled-metode.