Introduksjon til JavaFx

1. Introduksjon

JavaFX er et bibliotek for å bygge rike klientapplikasjoner med Java. Det gir et API for å designe GUI-applikasjoner som kjører på nesten alle enheter med Java-støtte.

I denne opplæringen skal vi fokusere på og dekke noen av dens viktigste funksjoner og funksjonalitet.

2. JavaFX API

I Java 8, 9 og 10 er ingen ekstra oppsett nødvendig for å begynne å jobbe med JavaFX-biblioteket. Prosjektet vil bli fjernet fra JDK fra og med JDK 11.

2.1. Arkitektur

JavaFX bruker maskinvareakselerert grafikkrørledning for gjengivelsen, kjent som Prisme. I tillegg, for å akselerere grafikkbruken fullt ut, utnytter den enten programvare eller maskinvarerendemekanisme ved å bruke den internt DirectX og OpenGL.

JavaFX har plattformavhengig Glass vindusverktøysettlag for å koble til det opprinnelige operativsystemet. Den bruker operativsystemets hendelseskø for å planlegge trådbruk. Dessuten håndterer den asynkront vinduer, hendelser, tidtakere.

De Media og Internett motorer muliggjør avspilling av media og HTML / CSS-støtte.

La oss se hvordan hovedstrukturen til et JavaFX-program ser ut:

Her merker vi to hovedcontainere:

  • Scene er hovedbeholderen og inngangspunktet for applikasjonen. Det representerer hovedvinduet og sendes som et argument for start() metode.
  • Scene er en beholder for å holde UI-elementene, for eksempel Image Views, Buttons, Grids, TextBoxes.

De Scene kan byttes ut eller byttes til en annen Scene. Dette representerer en graf med hierarkiske objekter, som er kjent som en Scene Kurve. Hvert element i dette hierarkiet kalles en node. En enkelt node har sin ID, stil, effekter, hendelsesbehandlere, tilstand.

I tillegg har Scene inneholder også layoutcontainere, bilder, media.

2.2. Tråder

På systemnivå, JVM oppretter separate tråder for å kjøre og gjengi applikasjonen:

  • Prisme gjengivelse tråd - ansvarlig for gjengivelse av Scenegraf hver for seg.
  • Søknadstråd - er hovedtråden til alle JavaFX-applikasjoner. Alle live noder og komponenter er knyttet til denne tråden.

2.3. Livssyklus

De javafx.application.Application klassen har følgende livssyklusmetoder:

  • i det() - kalles etter at applikasjonsinstansen er opprettet. På dette punktet er JavaFX API ikke klar ennå, så vi kan ikke lage grafiske komponenter her.
  • start (scenetrinn) - alle de grafiske komponentene er opprettet her. Også, hovedtråden for de grafiske aktivitetene starter her.
  • Stoppe() - kalles før stenging av applikasjonen; for eksempel når en bruker lukker hovedvinduet. Det er nyttig å overstyre denne metoden for opprydding før applikasjonsavslutningen.

Det statiske lansere () metoden starter JavaFX-applikasjonen.

2.4. FXML

JavaFX bruker et spesielt FXML-kodespråk for å lage visningsgrensesnittene.

Dette gir en XML-basert struktur for å skille visningen fra forretningslogikken. XML er mer egnet her, ettersom den er i stand til å representere et naturlig Scenegraf hierarki.

Til slutt, for å laste opp .fxml filen, bruker vi FXMLLoader klasse, noe som resulterer i objektgrafen til scenehierarkiet.

3. Komme i gang

For å bli praktisk, og la oss bygge et lite program som gjør det mulig å søke gjennom en liste over mennesker.

La oss først legge til en Person modellklasse - for å representere domenet vårt:

offentlig klasse Person {private SimpleIntegerProperty id; privat SimpleStringProperty navn; privat SimpleBooleanProperty isEmbled; // getters, setters}

Legg merke til hvordan, å pakke opp int, String og boolsk verdier bruker vi SimpleIntegerProperty, SimpleStringProperty, SimpleBooleanProperty klasser i javafx.beans.property pakke.

La oss deretter lage Hoved klasse som utvider applikasjon abstrakt klasse:

public class Main utvider Application {@Override public void start (Stage primaryStage) kaster unntak {FXMLLoader loader = new FXMLLoader (Main.class.getResource ("/ SearchController.fxml")); AnchorPane-side = (AnchorPane) loader.load (); Scene scene = ny scene (side); primaryStage.setTitle ("Tittelen går her"); primaryStage.setScene (scene); primaryStage.show (); } public static void main (String [] args) {launch (args); }}

Hovedklassen vår overstyrer start() metode, som er inngangspunktet for programmet.

Og så FXMLLoader laster opp objektgrafhierarkiet fra SearchController.fxml inn i det Ankerrute.

Etter å ha startet en ny Scene, setter vi den til den primære Scene. Vi setter også tittelen på vinduet vårt og vise fram() den.

Merk at det er nyttig å inkludere hoved() metode for å kunne kjøre JAR-filen uten JavaFX Launcher.

3.1. FXML-visning

La oss nå dykke dypere ned i SearchController XML-fil.

For søkesøknaden vår legger vi til et tekstfelt for å angi nøkkelordet og søkeknappen:

Ankerrute er rotbeholderen her, og den første noden i grafhierarkiet. Mens du endrer størrelsen på vinduet, vil det flytte barnet til ankerpunktet. De fx: kontroller attributt kobler Java-klassen med markeringen.

Det er noen andre innebygde oppsett tilgjengelig:

  • BorderPane - deler oppsettet i fem seksjoner: topp, høyre, bunn, venstre, sentrum
  • HBox - ordne barnekomponentene i et horisontalt panel
  • VBox - barnet nodene er ordnet i en vertikal kolonne
  • GridPane - nyttig for å lage et rutenett med rader og kolonner

I vårt eksempel, inne i det horisontale HBox panel, brukte vi en Merkelapp å plassere tekst, Tekstfelt for innspill, og a Knapp. Med fx: id vi merker elementene slik at vi kan bruke dem senere i Java-koden.

De VBox panelet er der vi viser søkeresultatene.

For å kartlegge dem til Java-feltene - bruker vi @FXML kommentar:

offentlig klasse SearchController {@FXML privat TextField searchField; @FXML private Button searchButton; @FXML privat VBox dataContainer; @FXML privat TableView tableView; @FXML privat tomrom initialisere () {// søkepanel searchButton.setText ("Søk"); searchButton.setOnAction (hendelse -> loadData ()); searchButton.setStyle ("- fx-bakgrunn-farge: # 457ecd; -fx-tekst-fyll: #ffffff;"); initTable (); }}

Etter å ha fylt ut @FXML merkede felt, initialisere () ringes automatisk. Her kan vi utføre ytterligere handlinger over UI-komponentene - som å registrere hendelseslyttere, legge til stil eller endre tekstegenskapen.

I initTable () metoden oppretter vi tabellen som inneholder resultatene, med 3 kolonner, og legger den til dataContainer VBox:

privat ugyldig initTable () {tableView = ny TableView (); TableColumn id = new TableColumn ("ID"); TableColumn name = new TableColumn ("NAME"); TableColumn ansatt = ny TableColumn ("ANSATT"); tableView.getColumns (). addAll (id, navn, ansatt); dataContainer.getChildren (). add (tableView); }

Til slutt vil all denne logikken som er beskrevet her, gi følgende vindu:

4. Bindende API

Nå som de visuelle aspektene blir håndtert, la oss begynne å se på bindende data.

Bindings-API-et gir noen grensesnitt som varsler objekter når det skjer en verdiendring av et annet objekt.

Vi kan binde en verdi ved hjelp av binde() metode eller ved å legge til lyttere.

Enveis binding gir bare en binding i en retning:

searchLabel.textProperty (). bind (searchField.textProperty ());

Her vil enhver endring i søkefeltet oppdatere tekstverdien til etiketten.

Til sammenligning synkroniserer toveisbinding verdiene til to egenskaper i begge retninger.

Den alternative måten å binde feltene på er ChangeListeners:

searchField.textProperty (). addListener ((observerbar, oldValue, newValue) -> {searchLabel.setText (newValue);});

De Observerbar grensesnitt gjør det mulig å observere verdien av objektet for endringer.

For å eksemplifisere dette er den mest brukte implementeringen javafx.collections.ObservableList grensesnitt:

ObservableList masterData = FXCollections.observableArrayList (); ObservableList results = FXCollections.observableList (masterData);

Her vil enhver modellendring som innsetting, oppdatering eller fjerning av elementene varsle UI-kontrollene umiddelbart.

De stamdata listen inneholder den første listen over Person objekter, og resultatlisten vil være listen vi viser når vi søker.

Vi må også oppdatere initTable () metode for å binde dataene i tabellen til den opprinnelige listen, og for å koble hver kolonne til Person klasse felt:

privat ugyldig initTable () {tableView = ny TableView (FXCollections.observableList (masterData)); TableColumn id = new TableColumn ("ID"); id.setCellValueFactory (ny PropertyValueFactory ("id")); TableColumn name = new TableColumn ("NAME"); name.setCellValueFactory (ny PropertyValueFactory ("navn")); TableColumn ansatt = ny TableColumn ("ANSATT"); ansatt.setCellValueFactory (ny PropertyValueFactory ("erEm Employed")); tableView.getColumns (). addAll (id, navn, ansatt); dataContainer.getChildren (). add (tableView); }

5. Samtidighet

Arbeid med UI-komponentene i et scenediagram er ikke trådsikkert, da det kun er tilgang til det fra applikasjonstråden. De javafx.concurrent pakken er her for å hjelpe med multitråding.

La oss se hvordan vi kan utføre datasøket i bakgrunnstråden:

privat tomrom loadData () {String searchText = searchField.getText (); Oppgave oppgave = ny oppgave() {@Override-beskyttet ObservableList-samtale () kaster Unntak {updateMessage ("Laster inn data"); returner FXCollections.observableArrayList (masterData .stream () .filter (verdi -> value.getName (). toLowerCase (). inneholder (searchText)) .collect (Collectors.toList ())); }}; }

Her lager vi en engangsoppgave javafx.concurrent.Task motsette og overstyre anrop() metode.

De anrop() metoden kjører helt på bakgrunnstråden og returnerer resultatet til applikasjonstråden. Dette betyr at enhver manipulering av UI-komponentene innenfor denne metoden vil føre til et unntak for kjøretid.

Derimot, updateProgress (), updateMessage () kan kalles for å oppdatere søknadstrådelementer. Når oppgavestaten overgår til SUCCEEDED-tilstand, vil onSucceeded () hendelsesbehandler kalles fra søknadstråden:

task.setOnSucceeded (event -> {results = task.getValue (); tableView.setItems (FXCollections.observableList (results));}); 

I samme tilbakeringing har vi oppdatert tableView data til den nye resultatlisten.

De Oppgave er Kjørbar, så for å starte det trenger vi bare å starte en ny Tråd med oppgave parameter:

Tråd tråd = ny tråd (oppgave); th.setDaemon (sant); th.start ();

De setDaemon (true) flagg indikerer at tråden avsluttes etter endt arbeid.

6. Håndtering av hendelser

Vi kan beskrive en hendelse som en handling som kan være interessant for applikasjonen.

For eksempel håndteres eller varsles brukerhandlinger som museklikk, tastetrykk, størrelse på vindu javafx.event.Event klasse eller noen av dens underklasser.

Vi skiller også mellom tre typer hendelser:

  • InputEvent - alle typer nøkkel- og mushandlinger som KEY_PRESSED, KEY_TYPED, KEY_RELEASED eller MOUSE_PRESSES, MOUSE_RELEASED
  • ActionEvent - representerer en rekke handlinger som å skyte en Knapp eller etterbehandling en KeyFrame
  • WindowEventWINDOW_SHOWING, WINDOW_SHOWN

Å demonstrere, kodefragmentet nedenfor fanger hendelsen ved å trykke på Tast inn nøkkelen over searchField:

searchField.setOnKeyPressed (hendelse -> {hvis (event.getCode (). tilsvarer (KeyCode.ENTER)) {loadData ();}});

7. Stil

Vi kan endre brukergrensesnittet til JavaFX-applikasjonen ved å bruke et tilpasset design på det.

Som standard bruker JavaFX modena.css som en CSS-ressurs for hele applikasjonen. Dette er en del av jfxrt.jar.

For å overstyre standardstil, kan vi legge til et stilark til scenen:

scene.getStylesheets (). add ("/ search.css");

Vi kan også bruke innebygd stil; for eksempel for å angi en stilegenskap for en bestemt node:

searchButton.setStyle ("- fx-bakgrunn-farge: slateblue; -fx-tekst-fyll: hvit;");

8. Konklusjon

Denne korte oppgaven dekker det grunnleggende i JavaFX API. Vi gjennomgikk den interne strukturen og introduserte viktige funksjoner i arkitekturen, livssyklusen og komponentene.

Som et resultat lærte vi og er nå i stand til å lage en enkel GUI-applikasjon.

Og som alltid er hele kildekoden til opplæringen tilgjengelig på GitHub.