JavaFxの概要

1.はじめに

JavaFXは、Javaを使用してリッチクライアントアプリケーションを構築するためのライブラリです。これは、 Javaをサポートするほぼすべてのデバイスで実行されるGUIアプリケーションを設計するためのAPIを提供します

このチュートリアルでは、その主要な機能のいくつかに焦点を当てて説明します。

2. JavaFX API

Java 8、9、および10では、JavaFXライブラリの操作を開始するために追加のセットアップは必要ありません。プロジェクトは、JDK11以降のJDKから削除されます。

2.1。建築

JavaFXは、Prismと呼ばれるハードウェアアクセラレーショングラフィックスパイプラインをレンダリングに使用します。さらに、グラフィックスの使用を完全に加速するために、DirectXOpenGLを内部的に使用することにより、ソフトウェアまたはハードウェアのレンダリングメカニズムを活用します。

JavaFXには、ネイティブオペレーティングシステムに接続するためのプラットフォーム依存のGlass WindowingToolkitレイヤーがあります。オペレーティングシステムのイベントキューを使用して、スレッドの使用をスケジュールします。また、ウィンドウ、イベント、タイマーを非同期的に処理します。

メディアウェブエンジンは、メディアが再生可能とHTML / CSSのサポート。

JavaFXアプリケーションの主な構造がどのように見えるかを見てみましょう。

ここで、2つの主要なコンテナに注目します。

  • ステージは、アプリケーションのメインコンテナおよびエントリポイントです。これはメインウィンドウを表し、 start()メソッドの引数として渡されます。
  • シーンは、画像ビュー、ボタン、グリッド、テキストボックスなどのUI要素を保持するためのコンテナです。

シーンは、交換または別のものに切り替えることができるシーン。これは、シーングラフと呼ばれる階層オブジェクトのグラフを表します。その階層の各要素はノードと呼ばれます。単一のノードには、ID、スタイル、効果、イベントハンドラー、状態があります。

さらに、シーンにはレイアウトコンテナ、画像、メディアも含まれています。

2.2。スレッド

システムレベルでは、JVMはアプリケーションを実行およびレンダリングするための個別のスレッドを作成します

  • プリズムレンダリングスレッド–シーングラフを個別にレンダリングします。
  • アプリケーションスレッド–JavaFXアプリケーションのメインスレッドです。すべてのライブノードとコンポーネントがこのスレッドに接続されています。

2.3。ライフサイクル

javafx.application.Applicationのクラスは、次のライフサイクルメソッドがあります。

  • init() –アプリケーションインスタンスが作成された後に呼び出されます。この時点では、JavaFX APIの準備がまだ整っていないため、ここでグラフィカルコンポーネントを作成することはできません。
  • start(ステージステージ) –すべてのグラフィカルコンポーネントがここで作成されます。また、グラフィカルアクティビティのメインスレッドはここから始まります。
  • stop() –アプリケーションがシャットダウンする前に呼び出されます。たとえば、ユーザーがメインウィンドウを閉じたときです。アプリケーションが終了する前に、クリーンアップのためにこのメソッドをオーバーライドすると便利です。

静的launch()メソッドはJavaFXアプリケーションを起動します。

2.4。FXML

JavaFXは、特別なFXMLマークアップ言語を使用してビューインターフェイスを作成します。

これにより、ビューをビジネスロジックから分離するためのXMLベースの構造が提供されます。XMLは、シーングラフの階層を非常に自然に表すことができるため、ここではより適しています。

最後に、.fxmlファイルをロードするために、FXMLLoaderクラスを使用します。これにより、シーン階層のオブジェクトグラフが作成されます。

3.はじめに

実用的にするために、人々のリストを検索できる小さなアプリケーションを作成しましょう。

まず、Personモデルクラスを追加しましょう–ドメインを表すために:

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

どのよう通知は、アップラップするint型、文字列ブール値は、我々が使用しているSimpleIntegerProperty、SimpleStringProperty、SimpleBooleanPropertyの中でクラスをjavafx.beans.propertyのパッケージを。

次に、Application抽象クラスを拡張するMainクラスを作成しましょう。

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); } }

メインクラスは、プログラムのエントリポイントであるstart()メソッドをオーバーライドします。

その後、FXMLLoaderのからオブジェクトグラフの階層までの負荷SearchController.fxmlAnchorPane

新しいシーンを開始した後、それをプライマリステージに設定します。また、ウィンドウのタイトルを設定してshow()します。

JavaFX LauncherなしでJARファイルを実行できるようにするには、main()メソッドを含めると便利であることに注意してください。

3.1。FXMLビュー

次に、SearchControllerXMLファイルについて詳しく見ていきましょう。

検索アプリケーションでは、キーワードと検索ボタンを入力するためのテキストフィールドを追加します。

AnchorPaneは、ここではルートコンテナであり、グラフ階層の最初のノードです。ウィンドウのサイズを変更している間、子をアンカーポイントに再配置します。FX:コントローラの属性ワイヤーマークアップとJavaクラス。

利用可能な他のいくつかの組み込みレイアウトがあります:

  • BorderPane –レイアウトを上、右、下、左、中央の5つのセクションに分割します
  • HBox –子コンポーネントを水平パネルに配置します
  • VBox –子ノードは垂直列に配置されます
  • GridPane –行と列のあるグリッドを作成するのに便利です

この例では、水平HBoxパネル内で、Labelを使用してテキストを配置し、TextFieldを入力に使用し、Buttonを使用しましたFX:ID我々は、Javaコードの後半でそれらを使用できるように、私たちは、要素をマーク。

VBox我々は、検索結果が表示されますどこパネルがあります。

次に、それらをJavaフィールドにマップするために– @ FXMLアノテーションを使用します

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(); } }

@FXMLアノテーション付きフィールドにデータを入力した後、initialize()が自動的に呼び出されます。ここでは、イベントリスナーの登録、スタイルの追加、テキストプロパティの変更など、UIコンポーネントに対してさらにアクションを実行できます。

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

カスタムデザインを適用することで、JavaFXアプリケーションのUIを変更できます。

デフォルトでは、JavaFXはアプリケーション全体のCSSリソースとしてmodena.cssを使用します。これはjfxrt.jarの一部です。

デフォルトのスタイルを上書きするために、シーンにスタイルシートを追加できます。

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

インラインスタイルも使用できます。たとえば、特定のノードのスタイルプロパティを設定するには、次のようにします。

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

8.結論

この簡単な記事では、JavaFXAPIの基本について説明します。内部構造を確認し、そのアーキテクチャ、ライフサイクル、およびコンポーネントの主要な機能を紹介しました。

その結果、簡単なGUIアプリケーションを学び、作成できるようになりました。

そして、いつものように、チュートリアルの完全なソースコードはGitHubで入手できます。