Inleiding tot JavaFx

1. Inleiding

JavaFX is een bibliotheek voor het bouwen van uitgebreide clienttoepassingen met Java. Het biedt een API voor het ontwerpen van GUI-applicaties die op bijna elk apparaat met Java-ondersteuning draaien.

In deze zelfstudie gaan we ons concentreren op enkele van de belangrijkste mogelijkheden en functionaliteit en deze behandelen.

2. JavaFX API

In Java 8, 9 en 10 is geen aanvullende installatie nodig om met de JavaFX-bibliotheek te werken. Het project wordt verwijderd uit de JDK te beginnen met JDK 11.

2.1. Architectuur

JavaFX gebruikt door hardware versnelde grafische pijplijnen voor de weergave, ook wel bekend als Prisma. Wat meer is, om het grafische gebruik volledig te versnellen, maakt het gebruik van het software- of hardwarematige weergavemechanisme door intern gebruik te maken van DirectX en OpenGL.

JavaFX heeft een platformafhankelijk Glas windowing toolkit-laag om verbinding te maken met het native besturingssysteem. Het gebruikt de gebeurteniswachtrij van het besturingssysteem om het threadgebruik te plannen. Het behandelt ook asynchroon vensters, gebeurtenissen en timers.

De Media en Web engines maken het afspelen van media en HTML / CSS-ondersteuning mogelijk.

Laten we eens kijken hoe de hoofdstructuur van een JavaFX-applicatie eruit ziet:

Hier zien we twee hoofdcontainers:

  • Stadium is de hoofdcontainer en het toegangspunt van de applicatie. Het vertegenwoordigt het hoofdvenster en doorgegeven als een argument van de begin() methode.
  • Tafereel is een container voor de UI-elementen, zoals afbeeldingsweergaven, knoppen, rasters, tekstvakken.

De Tafereel kan worden vervangen of omgeschakeld naar een andere Tafereel. Dit vertegenwoordigt een grafiek van hiërarchische objecten, die bekend staat als een Tafereel Grafiek. Elk element in die hiërarchie wordt een knooppunt genoemd. Een enkel knooppunt heeft zijn ID, stijl, effecten, gebeurtenishandlers, staat.

Bovendien is het Tafereel Bevat ook de lay-outcontainers, afbeeldingen, media.

2.2. Draden

Op systeemniveau de JVM maakt aparte threads voor het uitvoeren en weergeven van de applicatie:

  • Prisma rendering thread - verantwoordelijk voor het renderen van het Scene Grafiek afzonderlijk.
  • Toepassingsdraad - is de hoofdthread van elke JavaFX-toepassing. Alle live-knooppunten en componenten zijn aan deze thread bevestigd.

2.3. Levenscyclus

De javafx.application.Application klasse heeft de volgende levenscyclusmethoden:

  • in het() - wordt aangeroepen nadat de applicatie-instantie is gemaakt. Op dit moment is de JavaFX API nog niet klaar, dus we kunnen hier geen grafische componenten maken.
  • start (etappe) - alle grafische componenten worden hier aangemaakt. Ook, de rode draad voor de grafische activiteiten begint hier.
  • hou op() - wordt aangeroepen voordat de applicatie wordt afgesloten; bijvoorbeeld wanneer een gebruiker het hoofdvenster sluit. Het is handig om deze methode te negeren voor wat opschoning voordat de toepassing wordt beëindigd.

De statische lancering() methode start de JavaFX-applicatie.

2.4. FXML

JavaFX gebruikt een speciale FXML-opmaaktaal om de weergave-interfaces te maken.

Dit biedt een XML-gebaseerde structuur om de weergave te scheiden van de bedrijfslogica. XML is hier geschikter, omdat het op natuurlijke wijze een Scene Grafiek hiërarchie.

Eindelijk, om het .fxml bestand gebruiken we de FXMLLoader class, wat resulteert in de objectgrafiek van de scènehiërarchie.

3. Aan de slag

Om praktisch te worden, en laten we een kleine applicatie bouwen waarmee je door een lijst met mensen kunt zoeken.

Laten we eerst een Persoon modelklasse - om ons domein te vertegenwoordigen:

openbare klasse Persoon {privé SimpleIntegerProperty-id; private SimpleStringProperty naam; privé SimpleBooleanProperty isEmployed; // getters, setters}

Merk op hoe u het int, String en boolean waarden gebruiken we de SimpleIntegerProperty, SimpleStringProperty, SimpleBooleanProperty klassen in de javafx.beans.property pakket.

Laten we vervolgens het Hoofd klasse die de extensie Toepassing abstracte klasse:

public class Main breidt Applicatie uit {@Override public void start (Stage primaryStage) genereert Uitzondering {FXMLLoader loader = nieuwe FXMLLoader (Main.class.getResource ("/ SearchController.fxml")); AnchorPane-pagina = (AnchorPane) loader.load (); Scene scene = nieuwe scene (pagina); primaryStage.setTitle ("Titel komt hier"); primaryStage.setScene (scène); primaryStage.show (); } public static void main (String [] args) {launch (args); }}

Onze hoofdklasse heeft voorrang op de begin() methode, het startpunt voor het programma.

Dan de FXMLLoader laadt de objectgrafiekhiërarchie van SearchController.fxml in de AnchorPane.

Na het starten van een nieuw Tafereel, stellen we deze in op de primaire Stadium. We hebben ook de titel voor ons raam en tonen() het.

Merk op dat het handig is om de hoofd() methode om het JAR-bestand te kunnen uitvoeren zonder de JavaFX Launcher.

3.1. FXML-weergave

Laten we nu dieper in het SearchController XML-bestand.

Voor onze zoektoepassing voegen we een tekstveld toe om het trefwoord en de zoekknop in te voeren:

AnchorPane is hier de hoofdcontainer en het eerste knooppunt van de grafiekhiërarchie. Terwijl het formaat van het venster wordt aangepast, wordt het kind naar zijn ankerpunt verplaatst. De fx: controller attribuut verbindt de Java-klasse met de opmaak.

Er zijn enkele andere ingebouwde lay-outs beschikbaar:

  • BorderPane - verdeelt de lay-out in vijf secties: boven, rechts, onder, links, midden
  • HBox - rangschik de onderliggende componenten in een horizontaal paneel
  • VBox - de onderliggende knooppunten zijn gerangschikt in een verticale kolom
  • GridPane - handig voor het maken van een raster met rijen en kolommen

In ons voorbeeld, binnenkant van de horizontale HBox paneel, we gebruikten een Etiket tekst plaatsen, TextField voor de input, en een Knop. Met fx: id we markeren de elementen zodat we ze later in de Java-code kunnen gebruiken.

De VBox paneel is waar we de zoekresultaten zullen weergeven.

Om ze vervolgens aan de Java-velden toe te wijzen, gebruiken we de @FXML annotatie:

openbare klasse SearchController {@FXML privé TextField searchField; @FXML privé Knop searchButton; @FXML privé VBox dataContainer; @FXML privé TableView tableView; @FXML private void initialize () {// zoekpaneel searchButton.setText ("Zoeken"); searchButton.setOnAction (event -> loadData ()); searchButton.setStyle ("- fx-background-color: # 457ecd; -fx-text-fill: #ffffff;"); initTable (); }}

Na het invullen van het @FXML geannoteerde velden, initialiseren () wordt automatisch gebeld. Hier kunnen we verdere acties uitvoeren op de UI-componenten, zoals het registreren van gebeurtenislisteners, het toevoegen van stijl of het wijzigen van de teksteigenschap.

In de initTable () methode maken we de tabel met de resultaten, met 3 kolommen, en voegen deze toe aan de dataContainer VBox:

private void initTable () {tableView = nieuwe TableView (); TableColumn id = nieuwe TableColumn ("ID"); TableColumn name = nieuwe TableColumn ("NAME"); TableColumn aangewend = nieuwe TableColumn ("EMPLOYED"); tableView.getColumns (). addAll (id, naam, werkzaam); dataContainer.getChildren (). add (tableView); }

Ten slotte zal al deze hier beschreven logica het volgende venster produceren:

4. Bindende API

Nu de visuele aspecten zijn afgehandeld, gaan we kijken naar bindende gegevens.

De bindings-API biedt enkele interfaces die objecten op de hoogte stellen wanneer een waardeverandering van een ander object plaatsvindt.

We kunnen een waarde binden met de binden() methode of door luisteraars toe te voegen.

Unidirectionele binding biedt een binding voor slechts één richting:

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

Hier zal elke wijziging in het zoekveld de tekstwaarde van het label bijwerken.

Ter vergelijking: bidirectionele binding synchroniseert de waarden van twee eigenschappen in beide richtingen.

De alternatieve manier om de velden te binden zijn ChangeListeners:

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

De Waarneembaar interface maakt het observeren van de waarde van het object op veranderingen mogelijk.

Om dit te illustreren, is de meest gebruikte implementatie de javafx.collections.ObservableList koppel:

ObservableList masterData = FXCollections.observableArrayList (); ObservableList-resultaten = FXCollections.observableList (masterData);

Hier zal elke modelwijziging, zoals het invoegen, bijwerken of verwijderen van de elementen, de UI-besturingselementen onmiddellijk op de hoogte stellen.

De stamgegevens lijst bevat de eerste lijst van Persoon objecten, en de resultatenlijst is de lijst die we weergeven bij het zoeken.

We moeten ook het initTable () methode om de gegevens in de tabel te binden aan de initiële lijst en om elke kolom te verbinden met de Persoon klasse velden:

private void initTable () {tableView = nieuwe TableView (FXCollections.observableList (masterData)); TableColumn id = nieuwe TableColumn ("ID"); id.setCellValueFactory (nieuwe PropertyValueFactory ("id")); TableColumn name = nieuwe TableColumn ("NAME"); name.setCellValueFactory (nieuwe PropertyValueFactory ("naam")); TableColumn aangewend = nieuwe TableColumn ("EMPLOYED"); Employed.setCellValueFactory (nieuwe PropertyValueFactory ("isEmployed")); tableView.getColumns (). addAll (id, naam, werkzaam); dataContainer.getChildren (). add (tableView); }

5. Gelijktijdigheid

Werken met de UI-componenten in een scènegrafiek is niet thread-safe, omdat het alleen toegankelijk is vanuit de applicatiethread. De javafx.concurrent pakket is hier om te helpen met multithreading.

Laten we eens kijken hoe we het zoeken naar gegevens in de achtergrondthread kunnen uitvoeren:

private void loadData () {String searchText = searchField.getText (); Taak task = nieuwe taak() {@Override beschermde ObservableList call () genereert Uitzondering {updateMessage ("Bezig met laden van gegevens"); return FXCollections.observableArrayList (masterData .stream () .filter (waarde -> waarde.getName (). toLowerCase (). bevat (searchText)) .collect (Collectors.toList ())); }}; }

Hier maken we een eenmalige taak aan javafx.concurrent.Task object en negeer het bellen () methode.

De bellen () methode draait volledig op de achtergrondthread en retourneert het resultaat naar de toepassingsthread. Dit betekent dat elke manipulatie van de UI-componenten binnen deze methode een runtime-uitzondering genereert.

Echter, updateProgress (), updateMessage () kan worden aangeroepen om Application thread-items bij te werken. Wanneer de taakstatus overgaat naar SUCCEEDED, wordt het onSucceeded () gebeurtenishandler wordt aangeroepen vanuit de toepassingsthread:

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

In dezelfde callback hebben we het tableView gegevens naar de nieuwe lijst met resultaten.

De Taak is Runnable, dus om ermee te beginnen, moeten we gewoon een nieuwe beginnen Draad met de taak parameter:

Thread th = nieuwe Thread (taak); th.setDaemon (true); th.start ();

De setDaemon (waar) vlag geeft aan dat de thread wordt beëindigd na het voltooien van het werk.

6. Afhandeling van gebeurtenissen

We kunnen een evenement omschrijven als een actie die mogelijk interessant is voor de toepassing.

Gebruikersacties zoals muisklikken, toetsaanslagen, formaat van vensters worden afgehandeld of gemeld door javafx.event.Event class of een van zijn subklassen.

We onderscheiden ook drie soorten evenementen:

  • InputEvent - alle soorten toets- en muisacties zoals KEY_PRESSED, KEY_TYPED, KEY_RELEASED of MOUSE_PRESSES, MOUSE_RELEASED
  • ActionEvent - staat voor een verscheidenheid aan acties, zoals het afvuren van een Knop of het afronden van een KeyFrame
  • WindowEventWINDOW_SHOWING, WINDOW_SHOWN

Laten zien, het onderstaande codefragment vangt de gebeurtenis op van het indrukken van de Enter sleutel over de zoekveld:

searchField.setOnKeyPressed (event -> {if (event.getCode (). equals (KeyCode.ENTER)) {loadData ();}});

7. Stijl

We kunnen de gebruikersinterface van de JavaFX-applicatie wijzigen door er een aangepast ontwerp op toe te passen.

Standaard gebruikt JavaFX modena.css als een CSS-bron voor de hele applicatie. Dit is een onderdeel van de jfxrt.jar.

Om de standaardstijl te overschrijven, kunnen we een stylesheet aan de scène toevoegen:

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

We kunnen ook inline-stijl gebruiken; om bijvoorbeeld een stijleigenschap in te stellen voor een specifiek knooppunt:

searchButton.setStyle ("- fx-background-color: slateblue; -fx-text-fill: white;");

8. Conclusie

Dit korte artikel behandelt de basisprincipes van JavaFX API. We hebben de interne structuur doorgenomen en de belangrijkste mogelijkheden van de architectuur, levenscyclus en componenten geïntroduceerd.

Als resultaat hebben we geleerd en zijn we nu in staat om een ​​eenvoudige GUI-applicatie te maken.

En, zoals altijd, is de volledige broncode van de tutorial beschikbaar op GitHub.