Pengenalan JavaFx

1. Pengenalan

JavaFX adalah perpustakaan untuk membangun aplikasi klien kaya dengan Java. Ini menyediakan API untuk merancang aplikasi GUI yang berjalan di hampir setiap perangkat dengan dukungan Java.

Dalam tutorial ini, kita akan memberi tumpuan dan merangkumi beberapa keupayaan dan fungsi utamanya.

2. API JavaFX

Di Java 8, 9, dan 10 tidak diperlukan persediaan tambahan untuk mulai bekerja dengan perpustakaan JavaFX. Projek ini akan dikeluarkan dari JDK bermula dengan JDK 11.

2.1. Senibina

JavaFX menggunakan saluran paip grafik dipercepat perkakasan untuk rendering, yang dikenal sebagai Prism . Terlebih lagi, untuk mempercepat penggunaan grafik sepenuhnya, ia memanfaatkan mekanisme rendering perisian atau perkakasan, dengan menggunakan DirectX dan OpenGL secara dalaman .

JavaFX mempunyai lapisan toolkit Glass Window yang bergantung pada platform untuk disambungkan ke sistem operasi asli . Ia menggunakan barisan acara sistem operasi untuk menjadualkan penggunaan utas. Ia juga mengendalikan tetingkap, acara, pemasa secara tidak segerak.

Mesin Media dan Web membolehkan main balik media dan sokongan HTML / CSS.

Mari lihat bagaimana struktur utama aplikasi JavaFX:

Di sini, kami melihat dua bekas utama:

  • Pentas adalah wadah utama dan pintu masuk aplikasi . Ini mewakili tetingkap utama dan diteruskan sebagai argumen kaedah permulaan () .
  • Scene adalah wadah untuk menahan elemen UI, seperti Paparan Imej, Butang, Kotak, Kotak Kotak.

The Scene boleh diganti atau ditukar ke lain Scene . Ini mewakili grafik objek hierarki, yang dikenali sebagai Graf Scene . Setiap elemen dalam hierarki tersebut dipanggil simpul. Node tunggal mempunyai ID, gaya, kesan, pengendali acara, keadaan.

Selain itu, Scene juga mengandungi wadah susun atur, gambar, media.

2.2. Benang

Pada peringkat sistem, JVM membuat utas terpisah untuk menjalankan dan membuat aplikasi :

  • Prism rendering thread - bertanggungjawab membuat Scene Graph secara berasingan.
  • Benang aplikasi - adalah utas utama mana-mana aplikasi JavaFX. Semua nod dan komponen langsung dilampirkan pada utas ini.

2.3. Kitaran hidup

The javafx.application.Application kelas mempunyai kaedah kitaran hayat berikut:

  • init () - dipanggil setelah contoh aplikasi dibuat . Pada ketika ini, JavaFX API belum siap, jadi kami tidak dapat membuat komponen grafik di sini.
  • permulaan (Tahap pentas) - semua komponen grafik dibuat di sini. Juga, utas utama untuk aktiviti grafik bermula di sini.
  • berhenti () - dipanggil sebelum penutupan aplikasi; sebagai contoh, apabila pengguna menutup tetingkap utama. Adalah berguna untuk mengatasi kaedah ini untuk beberapa pembersihan sebelum penamatan aplikasi.

Kaedah pelancaran statik () memulakan aplikasi JavaFX.

2.4. FXML

JavaFX menggunakan bahasa markup FXML khas untuk membuat antara muka pandangan.

Ini menyediakan struktur berasaskan XML untuk memisahkan pandangan dari logik perniagaan. XML lebih sesuai di sini, kerana secara semula jadi dapat mewakili hierarki Graf Scene .

Akhirnya, untuk memuat fail .fxml , kami menggunakan kelas FXMLLoader , yang menghasilkan grafik objek dari hierarki pemandangan.

3. Bermula

Untuk menjadi praktikal, dan mari kita membina aplikasi kecil yang membolehkan mencari melalui senarai orang.

Pertama, mari tambah kelas model Person - untuk mewakili domain kami:

public class Person { private SimpleIntegerProperty id; private SimpleStringProperty name; private SimpleBooleanProperty isEmployed; // getters, setters }

Perhatikan bagaimana, untuk menyelesaikan nilai int, String dan boolean , kita menggunakan kelas SimpleIntegerProperty, SimpleStringProperty, SimpleBooleanProperty dalam pakej javafx.beans.property .

Seterusnya, mari buat kelas Utama yang meluaskan kelas abstrak Aplikasi :

public class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader( Main.class.getResource("/SearchController.fxml")); AnchorPane page = (AnchorPane) loader.load(); Scene scene = new Scene(page); primaryStage.setTitle("Title goes here"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }

Kelas utama kami mengatasi kaedah mula () , yang merupakan titik masuk program.

Kemudian, FXMLLoader memuatkan hierarki grafik objek dari SearchController.fxml ke AnchorPane .

Setelah memulakan Adegan baru , kami menetapkannya ke Tahap utama . Kami juga menetapkan tajuk untuk tetingkap kami dan menunjukkannya () .

Perhatikan bahawa berguna untuk memasukkan kaedah utama () untuk dapat menjalankan fail JAR tanpa Pelancar JavaFX .

3.1. Paparan FXML

Sekarang mari kita selami lebih dalam fail XML SearchController .

Untuk aplikasi carian kami, kami akan menambahkan medan teks untuk memasukkan kata kunci dan butang carian:

AnchorPane adalah wadah akar di sini, dan simpul pertama hierarki grafik. Semasa mengubah saiz tetingkap, ia akan meletakkan semula anak ke titik jangkarnya. The fx: pengawal wayar atribut kelas Java dengan markup.

Terdapat beberapa susun atur lain yang tersedia:

  • BorderPane - membahagikan susun atur menjadi lima bahagian: atas, kanan, bawah, kiri, tengah
  • HBox - susun komponen anak di panel mendatar
  • VBox - simpul anak disusun dalam lajur menegak
  • GridPane - berguna untuk membuat grid dengan baris dan lajur

In our example, inside of the horizontal HBox panel, we used a Label to place text, TextField for the input, and a Button. With fx: id we mark the elements so that we can use them later in the Java code.

The VBox panel is where we'll display the search results.

Then, to map them to the Java fields – we use the @FXML annotation:

public class SearchController { @FXML private TextField searchField; @FXML private Button searchButton; @FXML private VBox dataContainer; @FXML private TableView tableView; @FXML private void initialize() { // search panel searchButton.setText("Search"); searchButton.setOnAction(event -> loadData()); searchButton.setStyle("-fx-background-color: #457ecd; -fx-text-fill: #ffffff;"); initTable(); } }

After populating the @FXML annotated fields, initialize() will be called automatically. Here, we're able to perform further actions over the UI components – like registering event listeners, adding style or changing the text property.

In the initTable() method we'll create the table that will contain the results, with 3 columns, and add it to the dataContainer VBox:

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

Finally, all of this logic described here will produce the following window:

4. Binding API

Now that the visual aspects are handled, let's start looking at binding data.

The binding API provides some interfaces that notify objects when a value change of another object occurs.

We can bind a value using the bind() method or by adding listeners.

Unidirectional binding provides a binding for one direction only:

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

Here, any change in the search field will update the text value of the label.

By comparison, bidirectional binding synchronizes the values of two properties in both directions.

The alternative way of binding the fields are ChangeListeners:

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

The Observable interface allows observing the value of the object for changes.

To exemplify this, the most commonly used implementation is the javafx.collections.ObservableList interface:

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

Here, any model change like insertion, update or removal of the elements, will notify the UI controls immediately.

The masterData list will contain the initial list of Person objects, and the results list will be the list we display upon searching.

We also have to update the initTable() method to bind the data in the table to the initial list, and to connect each column to the Person class fields:

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

5. Concurrency

Working with the UI components in a scene graph isn't thread-safe, as it's accessed only from the Application thread. The javafx.concurrent package is here to help with multithreading.

Let's see how we can perform the data search in the background thread:

private void loadData() { String searchText = searchField.getText(); Task
    
      task = new Task
     
      () { @Override protected ObservableList call() throws Exception { updateMessage("Loading data"); return FXCollections.observableArrayList(masterData .stream() .filter(value -> value.getName().toLowerCase().contains(searchText)) .collect(Collectors.toList())); } }; }
     
    

Here, we create a one-time task javafx.concurrent.Task object and override the call() method.

The call() method runs entirely on the background thread and returns the result to the Application thread. This means any manipulation of the UI components within this method, will throw a runtime exception.

However, updateProgress(), updateMessage() can be called to update Application thread items. When the task state transitions to SUCCEEDED state, the onSucceeded() event handler is called from the Application thread:

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

In the same callback, we've updated the tableView data to the new list of results.

The Task is Runnable, so to start it we need just to start a new Thread with the task parameter:

Thread th = new Thread(task); th.setDaemon(true); th.start();

The setDaemon(true) flag indicates that the thread will terminate after finishing the work.

6. Event Handling

We can describe an event as an action that might be interesting to the application.

For example, user actions like mouse clicks, key presses, window resize are handled or notified by javafx.event.Event class or any of its subclasses.

Also, we distinguish three types of events:

  • InputEvent – all the types of key and mouse actions like KEY_PRESSED, KEY_TYPED, KEY_RELEASED or MOUSE_PRESSES, MOUSE_RELEASED
  • ActionEvent – represents a variety of actions like firing a Button or finishing a KeyFrame
  • WindowEventWINDOW_SHOWING, WINDOW_SHOWN

To demonstrate, the code fragment below catches the event of pressing the Enter key over the searchField:

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

7. Style

Kita dapat mengubah UI aplikasi JavaFX dengan menerapkan reka bentuk khusus padanya.

Secara lalai, JavaFX menggunakan modena.css sebagai sumber CSS untuk keseluruhan aplikasi. Ini adalah bahagian dari jfxrt.jar .

Untuk mengatasi gaya lalai, kita dapat menambahkan lembaran gaya ke tempat kejadian:

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

Kami juga boleh menggunakan gaya sebaris; sebagai contoh, untuk menetapkan sifat gaya untuk nod tertentu:

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

8. Kesimpulannya

Penulisan ringkas ini merangkumi asas-asas API JavaFX. Kami melalui struktur dalaman dan memperkenalkan keupayaan utama seni bina, kitaran hidup, dan komponennya.

Hasilnya, kami belajar dan sekarang dapat membuat aplikasi GUI sederhana.

Dan, seperti biasa, kod sumber penuh tutorial terdapat di GitHub.