...
Med JavaFX kan en velge å bruke kode for å bygge opp GUI-et eller beskrive GUI-et med FXML-filer og så lese inn og vise frem innholdet. Vi velger å bruke FXML, siden det er mye enklere. Prosessen er grovt sett delt i to:
- En lager en FXML-fil i Eclipse, som enten kan redigeres som tekst i Eclipse eller og åpnes i SceneBuilder for mer grafisk redigering av GUI-fasaden.
- En lager en Java-klasse for styring av app-en og kobler denne til FXML-koden vha. spesielle Java- og FXML-elementer.
Oppretting av FXML-fil
En lager en FXML-fil med Eclipse sin New FXML Document veiviser: høyreklikk på pakka (kode-mappa) hvor Counter-klassen ligger, og velg New > Other... > New FXML Document. Fyll så ut navnet, f.eks. Counter1 og velg HBox (for horisontal box) som såkalt "Root Element" (panel-type). Avslutt og du vil få opp en FXML-editor med kode for et tomt HBox-panel. For å vise frem innholdet, altså "kjøre" FXML-koden, så kan du høyre-klikke på fila og velge Run As > FXML Application (dette forutsetter et spesielt Eclipse-tillegg). Du vil da få opp et tomt vindu, siden du ikke har redigert FXML-koden og lagt inn noe synlig innhold.
Veiviser for FXML-dokumenterfiler | FXML-editor med nytt ny FXML-dokumentfil |
---|---|
FXML-kode for tomt HBox-panel. Den første linja er obligatorisk for alle XML-dokumenter (utelates i eksemplene under). <?import ...>-elementet lar oss bruke HBox som navn på det som egentlig er Java-klassen javafx.scene.layout.HBox. <HBox ...>-elementet angir at app-vinduet skal inneholdet et objekt av typen javafx.scene.layout.HBox, som gir en horisontal layout på panel-innholdet. Her er elementet imidlertid tomt, så ingenting vises ved kjøring. Merk attributtet xmlns:fx="...", som angir at elementene skal tolkes som FXML-kode, altså referanser til JavaFX-klasser. Uten dette attributtet på det ytterste elementet vil ingenting virke. |
Redigering av FXML-koden
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 frem hva innholdet i den aktive FXML-editoren betyr, altså slik det vil se ut hvis FXML-koden kjøres. Under vises Figuren under viser koden for HBox-panelet med en Count-knapp i og JavaFX Preview-panel.
...
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 altså FXML som lagringsformat, men lar deg redigere innholdet vha. direkte manipulasjon, tekstfelt, menyer og dialoger.
SceneBuilder-applikasjonen lar deg redigere FXML-koden grafisk. Området i midten viser GUI-et. Øverst til venstre er det en palett med elementer en kan dra og slippe i GUI-området i midten. Nederst til venstre vises den hierarkiske strukturen av paneler og elementer, her med en HBox med en Button inni. Til høyre er det et tredelt detalj-panel. Properties-seksjonen viser generelle grafiske egenskaper og egenskaper som er mer spesifikke for det valgte elementet. Her er knappen valgt, og vi ser at knappeteksten styres av Text-egenskapen, som har verdien Count. Layout-seksjonen har med plassering av elementet å gjøre og Code-seksjonen med koblingen til GUI-logikken. |
I den enkle varianten app-en trenger vi en tekst til venstre for knappen. Det kan en få til ved å legge til et Text-objekt slik at det havner før Butten-objektet inni HBox-objektet. Finn først frem til Text-objektet i paletten (til venstre) ved å velge Shapes-seksjonen og bla nedover. Så kan du enten dra og slippe Text-objektet til venstre for knappen i GUI-området (i midten), eller rett over Button-objektet i hierarkiet (nede til venstre). Rediger så teksten så den blir Current counter value: 0, enten ved å dobbelt-klikke på teksten i GUI-området og redigere, eller ved å redigere Text-egenskapen i Properties-seksjonen (oppe til høyre). Resultatet er vist under til venstre. Lagre, gå tilbake til Eclipse og klikk på eller i FXML-editoren for å sikre at Eclipse viser den nylig lagrede versjonen. Koden skal være omtrent som vist under til høyre.
|
App-logikk
Så langt har vi et GUI uten oppførsel, for hvis du kjører og trykker på knappen så skjer det ingenting. Det vi mangler er app-logikken som styrer koblingen mellom den indre tilstanden til applikasjonen og GUI-fasaden. I vårt tilfelle utgjøres den indre tilstanden av Counter-objektet og , mens GUI-fasaden består av Text- og Button-objektene, eller generelt av hierarkiet av JavaFX-objekter. Koblingslogikken må sikre at Text-objektet viser counter-verdien ( til Counter-objektet ) og at trykking på Count-knappen trigger count-metoden. Koblingen er altså toveis: (deler av) den indre tilstanden vises i GUI-fasaden et og GUI-fasaden et styrer (deler av) den indre tilstanden.
App-logikken ligger i en egen klasse, som gjerne kalles en kontroller-klasse, og når appen kjøres vil det finnes ett objekt av denne klassen, som er koblet sammen med de relevante JavaFX Text- og Button-objektene og objektene som utgjør den indre tilstandeni GUI-et og Count-objektet. Sammenkoblingen av alle disse objektene krever spesiell kode i både FXML-koden og i kontroller-klassen. Under ser du til venstre ser du den relevante FXML- og kontroller-kodekoden, med en forklaring på sammenhengene i midtentil høyre.
FXML- og Java-kode | Forklaring | Kontroller-kode | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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 vil det altså være et CounterController-objektangis 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.
Selv 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. Som over så spiller ikke navnet (på metoden) noen rolle, bare det stemmer med verdien til attributtet. Code Block | public class CounterController { Counter counter; @FXML Text counterOutput; @FXML void initialize() { counter = new Counter(5); } @FXML void handleCountAction() { counter.count(); counterOutput.setText("Current counter value: " + counter.getCounter()); } } |
Counter-app - komplett variant
...