ある日、私は自分のニーズに合わせた小さなデスクトップ アプリケーション (外国語を学習するための小さな辞書のようなもの) を作成するというアイデアを思いつき、どうすればこれを実現できるだろうかと頭を悩ませ始めました。当然、最初に頭に浮かんだのはSwingでした。おそらく誰もがSwingについて聞いたことがあるでしょう。これは、ユーザーのグラフィカル インターフェイスを作成するためのライブラリです。私たちの最愛の Oracle はまだ完全に Swing を放棄していないため、Swing は廃止されたとは見なされず、アプリケーションは依然として Swing 上で実行されます。しかし、JavaFX は Swing によって最新化されておらず、Oracle の人々は JavaFX の将来がどうなるかを私たちに教えてくれました。実際、JavaFX はサービスプロバイダーとして Swing コンポーネントを使用します)
(犬の登録窓口のようなもの)
犬を選択して削除ボタンを押すと、その犬はリストから削除されます。4本足の友達を選択してフィールドを変更し、編集ボタンを押すと、犬の情報が更新されます。[新規] ボタンを押すと、新しい犬のレコードを作成するためのウィンドウが表示されます (犬の名前から始めます)。 次に、[保存] をクリックして最初のウィンドウの残りのフィールドに入力し、[編集] ボタンをクリックして、保存。簡単そうに思えますよね?これが Java アプリケーションでどのように見えるかを見てみましょう。まず、次の手順で生成されたこれら 2 つのウィンドウの XML レイアウトをここに残しておきます。
今日はおそらくこれですべてです。今日は、基本的な概念と JavaFX の使用例について簡単に説明しました。また、小規模なデスクトップ アプリケーションを構築することができます (幸いにもインターネット上に豊富にある追加情報を使用します)。そして、あなたも同様に))
JavaFXとは何ですか?
JavaFX は本質的に Java 用の GUI ツールキットです。ここで少し脱線しますが、GUI とは何かを思い出してください。 グラフィカル ユーザー インターフェイス- グラフィカル ユーザー インターフェイスは、すべての要素 (ボタン、メニュー、アイコン、リスト) がユーザーに表示されるユーザー インターフェイスの一種です。表示は写真、グラフィックの形式で行われます。コマンド ライン インターフェイスとは異なり、GUI ではユーザーは入力デバイスを使用して表示されているオブジェクトにランダムにアクセスできます。多くの場合、インターフェイス要素はメタファーの形式で実装され、ユーザーの理解を容易にするためにそのプロパティと目的が表示されます。 JavaFX は、 Java でゲームやデスクトップ アプリケーションを作成することを目的としています。実際、Java 用の新しい GUI ツールが提案されているため、Swing に置き換わることになります。また、Web アプリケーションで慣れているものと同様に、GUI レイアウト ファイル (XML) のスタイルを設定し、CSS を使用してファイルをよりエレガントにすることもできます。JavaFX はさらに、単一の GUI ツールキットで、統合された 3D グラフィックス、オーディオ、ビデオ、組み込みネットワーキング アプリケーションを処理します。学習が簡単で、適切に最適化されています。Windows、UNIX システム、Mac OS だけでなく、多くのオペレーティング システムをサポートしています。JavaFX の機能:
- JavaFX には当初、あらゆる種類のボタン、テキスト フィールド、テーブル、ツリー、メニュー、グラフなどのグラフィカル インターフェイス パーツの大規模なセットが付属しており、これにより時間を大幅に節約できます。
- JavaFX は CSS スタイルを使用することが多く、Java コードで作成するのではなく、特別な FXML 形式を使用して GUI を作成できるようになります。これにより、Java コードを長時間いじらなくても、GUI をすばやくレイアウトしたり、外観や構成を変更したりすることが簡単になります。
- JavaFX にはすぐに使用できる図のパーツがあるため、基本的な図が必要なときにいつでも最初から作成する必要はありません。
- JavaFX にはさらに 3D グラフィックス サポートが付属しており、ある種のゲームや同様のアプリケーションを開発する場合に便利です。
- ステージは基本的に、開始キャンバスとして機能し、残りのコンポーネントを含む周囲のウィンドウです。アプリケーションには複数のステージを含めることができますが、いずれの場合もそのようなコンポーネントは 1 つ存在する必要があります。基本的に、Stage はメイン コンテナおよびエントリ ポイントです。
- シーン-ステージの内容を表示します(入れ子人形など)。各ステージには複数のコンポーネント (シーン) を含めることができ、コンポーネント間で切り替えることができます。内部的には、これはシーン グラフと呼ばれるオブジェクト グラフによって実装されます (各要素はノードであり、Nodeとも呼ばれます)。
- ノードは、ラベル ボタンやレイアウトなどのコントロールであり、内部に複数のネストされたコンポーネントを含めることができます。各シーンには 1 つのネストされたノードを含めることができますが、複数のコンポーネントを含むレイアウトにすることもできます。ネストはマルチレベルにすることができ、レイアウトには他のレイアウトや通常のコンポーネントが含まれます。このような各ノードには、独自の識別子、スタイル、エフェクト、状態、およびイベント ハンドラーがあります。
JavaFX: 使用例
メソッドmain
(エントリ ポイント) を使用して通常のクラスを作成します。
public class AppFX extends Application {
public static void main(String[] args) {
Application.launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
pimarySatge.show();
}
}
ここで、クラスはjavafx.application.Application
(Bugaga ボックスから取得したもの) を継承します。main では、静的な Application メソッドを呼び出してlaunch()
ウィンドウを起動します。また、私たちのアイデアは、私たちが最終的に行うことである Application メソッドを実装していないという不平を言いますstart
。それはなんのためですか?そして、プロパティ (ウィンドウの機能) を管理できるようにするためです。これを行うには、primaryStage
メソッドを呼び出す受信引数を使用してshow
、 でウィンドウが起動されるのを確認できるようにしますmain
。メソッドを少し埋めてみましょうstart
。
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Dogs application");
primaryStage.setWidth(500);
primaryStage.setHeight(400);
InputStream iconStream =
getClass().getResourceAsStream("/images/someImage.png");
Image image = new Image(iconStream);
primaryStage.getIcons().add(image);
Button button = new Button("WOF WOF ???'");
button.setOnAction(e -< {
Alert alert = new Alert(Alert.AlertType.INFORMATION, "WOF WOF WOF!!!");
alert.showAndWait();
});
Scene primaryScene = new Scene(button);
primaryStage.setScene(primaryScene);
primaryStage.show();
}
さて、ここで何が見えるでしょうか?1 行ずつ見てみましょう: 2 - ウィンドウ自体の名前を設定します (ステージ) 3.4 - そのサイズを設定します 6.7 - ファイルへの読み取りストリームのパスを設定します (アイコン) 8 - ファイルを Image オブジェクトとして作成します。コンストラクターで渡されるストリームによって実際のファイルに接続されます 9 - ウィンドウの上部パネルにアイコンを設定します 11 - ボタン オブジェクトを作成します 13-16 - ボタンが押されたときの反応を設定します 17 - シーンを作成しますボタンを配置します 18 - シーンを共通ウィンドウに配置します 20 - ウィンドウの可視性フラグを設定します その結果、お気に入りのペセルを受け入れるための小さなウィンドウが表示されます。 すべてが Swing よりもはるかに単純に見えますね。しかし、まだ終わっていません。アプリケーションを表示するためにすべてのコードを記述するのは良いことではなく、わかりやすくするために何らかの方法でコードを分割する必要があります (グラフィカル コンポーネントを 1 つのバスケットに、ロジックを別のバスケットに)。ここで XML が登場します... なんと、XML? その通り。具体的には、JavaFX - FXML の特定の実装を使用します。この実装では、アプリケーションのグラフィカル コンポーネントとそのプロパティ (あらゆる種類のサイズなど) を定義し、ロジックの管理に役立つコントローラーに接続します。この XML の例を見てみましょう。
<?xml version="1.0" encoding="UTF-8"?>
<?language javascript?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
id="Dogs application" prefHeight="200" prefWidth="300" alignment="center">
<Label text="Wow wow?"/>
<Button fx:id="mainButton" text="Greeting" onAction="buttonClicked()"/>
<fx:script>
function buttonClicked() {
mainButton.setText("Wow wow wow!!!")
}
</fx:script>
</VBox>
2 - 使用するスクリプト言語 4-6 - インポートされたデータ 8-9 Vbox - サブコンポーネントを 1 行に配置するコンテナ。11 - テキストを表示します 13 - ボタンをクリックすると、スクリプトの 15 行目から 18 行目で説明されているメソッドを使用します。メソッド内にこの XML ファイルを呼び出すためのコードがあるはずですstart
が、これはそれほど重要ではありません。省略します (以下にこのファイルを取得する例を示します)。つまり、xml はもちろん優れています (ただし、それほど優れているわけではありません)。手動で記述するのは非常に混乱します。これは前世紀のことではありませんか?
JavaFX SceneBuilder の紹介
(ドラムロール) が登場するのはこの時点です。JavaFXSceneBuilder
では、 Scene Builder は、グラフィカル インターフェイスの形式でウィンドウを設計し、保存できるツールです。このプログラムは、その結果に基づいて、 XML ファイルを構築し、アプリケーション内でそれを改善します。この fmxl ビルダーのインターフェイスは次のようになります。
ちょっとした余談。JavaFX レッスン
インストールの詳細と、このツールの詳細な検討については省略します。これらはさらに検討する価値のあるトピックです。したがって、JavaFX レッスンへの興味深いリンクをいくつか残しておきます。1つは(JavaFX のオンライン チュートリアル)、2 つは(別の優れたチュートリアル) です。私がスケッチした小さな例を見てみましょう。最終的に次のようなものが得られました:SceneBuilder
最初 (基本):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<AnchorPane prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.tutorial.controller.BaseController">
<children>
<SplitPane dividerPositions="0.29797979797979796" prefHeight="300.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TableView fx:id="dogs" layoutX="-2.0" layoutY="-4.0" prefHeight="307.0" prefWidth="190.0" AnchorPane.bottomAnchor="-5.0" AnchorPane.leftAnchor="-2.0" AnchorPane.rightAnchor="-13.0" AnchorPane.topAnchor="-4.0">
<columns>
<TableColumn fx:id="nameList" prefWidth="100.33334350585938" text="Nickname" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Label layoutX="49.0" layoutY="25.0" text="Person Details" AnchorPane.leftAnchor="5.0" AnchorPane.topAnchor="5.0" />
<GridPane accessibleText="erreererer" gridLinesVisible="true" layoutX="5.0" layoutY="31.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="31.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label prefHeight="17.0" prefWidth="70.0" text="Nickname" />
<Label text="Breed" GridPane.rowIndex="1" />
<Label text="Age" GridPane.rowIndex="2" />
<Label text="City" GridPane.rowIndex="3" />
<Label text="Level of training" GridPane.rowIndex="4" />
<TextField fx:id="breed" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<TextField fx:id="age" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<TextField fx:id="city" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<TextField fx:id="levelOfTraining" GridPane.columnIndex="1" GridPane.rowIndex="4" />
<TextField fx:id="name" GridPane.columnIndex="1" />
</children>
</GridPane>
<Button layoutX="251.0" layoutY="259.0" mnemonicParsing="false" onAction="#create" text="New" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="230.0" AnchorPane.rightAnchor="130.0" AnchorPane.topAnchor="260.0" />
<Button layoutX="316.0" layoutY="262.0" mnemonicParsing="false" onAction="#edit" text="Edit" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="290.0" AnchorPane.rightAnchor="70.0" AnchorPane.topAnchor="260.0" />
<Button layoutX="360.0" layoutY="262.0" mnemonicParsing="false" onAction="#delete" text="Delete" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="350.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="260.0" />
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
2 番目 (新しい犬を作成する):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.tutorial.controller.NewDogController">
<children>
<GridPane layoutX="31.0" layoutY="25.0" prefHeight="122.0" prefWidth="412.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="185.0" minWidth="10.0" prefWidth="149.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="173.0" minWidth="10.0" prefWidth="146.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label prefHeight="48.0" prefWidth="178.0" text="Please, write name:">
<font>
<Font size="20.0" />
</font>
</Label>
<TextField fx:id="nickName" prefHeight="36.0" prefWidth="173.0" GridPane.columnIndex="1" />
</children>
</GridPane>
<Button layoutX="222.0" layoutY="149.0" mnemonicParsing="false" onAction="#ok" prefHeight="37.0" prefWidth="95.0" text="Save" />
<Button layoutX="325.0" layoutY="149.0" mnemonicParsing="false" onAction="#cansel" prefHeight="37.0" prefWidth="95.0" text="Cansel" />
</children>
</AnchorPane>
フォルダー構造は次のようになります。 ご覧のとおり、特別なことは何もありません。特定のウィンドウを表すコントローラーがあり、データを表すモデルがあります。アプリケーションを起動するクラス (アプリケーション実装) を見てみましょう。 @Data
public class AppFX extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private ObservableList listDog = FXCollections.observableArrayList();
public AppFX() {
listDog.add(new Dog("Fluffy", "Pug", 8, "Odessa", 2));
listDog.add(new Dog("Archie", "Poodle", 3, "Lviv", 6));
listDog.add(new Dog("Willie", "Bulldog", 5, "Kiev", 4));
listDog.add(new Dog("Hector", "Shepherd", 9, "Minsk", 6));
listDog.add(new Dog("Duncan", "Dachshund", 1, "Hogwarts", 9));
}
ここでは、初期データ (特別なシート - ObservableList に保存) を入力するコンストラクターが表示されます。
public static void main(String[] args) {
Application.launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Dogs application");
showBaseWindow();
}
特別なことは何もありません。アプリケーションを起動する main
実装は次のとおりです。start()
public void showBaseWindow() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(AppFX.class.getResource("/maket/rootWindow.fxml"));
rootLayout = loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
InputStream iconStream = getClass().getResourceAsStream("/icons/someImage.png");
Image image = new Image(iconStream);
primaryStage.getIcons().add(image);
BaseController controller = loader.getController();
controller.setAppFX(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
ここでは、実際に で起動するメソッドstart()
、つまりベース ウィンドウの設定を設定するメソッドを示します。リソース内の XML レイアウトなど: アイコンの付与、特定のコントローラーへのリンク、コントローラーへのthis
クラスへのリンクの付与など)
public void showCreateWindow(Dog dog) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(AppFX.class.getResource("/maket/new.fxml"));
AnchorPane page = loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Wow Wow Wow");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
dialogStage.setScene(new Scene(page));
CreateController controller = loader.getController();
controller.setDialogStage(dialogStage);
controller.setDog(dog);
dialogStage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ここでは、2 番目のウィンドウ、つまり新しいレコード (新しい犬の名前) を作成するためのウィンドウの表示を担当するメソッドが示されています。また、コントローラー、XML レイアウト、ステージなども設定します。次に検討するクラスは、犬 (犬に関する情報) を表すモデルになります。 @Data
public class Dog {
private StringProperty name;
private StringProperty breed;
private IntegerProperty age;
private StringProperty city;
private IntegerProperty levelOfTraining;
public Dog(String name, String breed, int age, String city, int levelOfTraining) {
this.name = new SimpleStringProperty(name);
this.breed = new SimpleStringProperty(breed);
this.age = new SimpleIntegerProperty(age);
this.city = new SimpleStringProperty(city);
this.levelOfTraining = new SimpleIntegerProperty(levelOfTraining);
}
public Dog() {
name = new SimpleStringProperty();
breed = null;
age = null;
city = null;
levelOfTraining = null;
}
}
ここでは 2 つのコンストラクターが表示されます。1 つは、すべての引数を持つほぼ通常のコンストラクター (単純な型の特別な FX ラッパーを使用するためです) と、引数のないコンストラクターです。新しい犬を作成するときに使用します。名前。基本ウィンドウのコントローラー: @Data
public class BaseController {
@FXML
private TableView dogs;
@FXML
private TableColumn nameList;
@FXML
private TextField name;
@FXML
private TextField breed;
@FXML
private TextField age;
@FXML
private TextField city;
@FXML
private TextField levelOfTraining;
private AppFX appFX;
ここではオブジェクト フィールドが表示されていますが、TextField 形式になっています。これはテキスト入力フィールドを表す形式です。@FXML は、Java コードとレイアウトの対応するオブジェクト (ボタン、フィールドなど) をリンクするように設計されたアノテーションです。
@FXML
private void initialize() {
nameList.setCellValueFactory(
cellData -> cellData.getValue().getName());
dogs.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> showDogsInformation(newValue));
}
ここでは、リストの右側に犬の名前を表示するメソッドが表示されます (その @FXML 注釈は JavaFX TableView レイアウト コンポーネントにバインドされています)。
public void setAppFX(AppFX appFX) {
this.appFX = appFX;
dogs.setItems(appFX.getListDog());
}
private void showDogsInformation(Dog dog) {
if (dog != null) {
name.setText(dog.getName() != null ? dog.getName().getValue() : null);
breed.setText(dog.getBreed() != null ? dog.getBreed().getValue() : null);
age.setText(dog.getAge() != null ? String.valueOf(dog.getAge().get()) : null);
city.setText(dog.getCity() != null ? dog.getCity().getValue() : null);
levelOfTraining.setText(dog.getLevelOfTraining() != null ? String.valueOf(dog.getLevelOfTraining().get()) : null);
} else {
name.setText("");
breed.setText("");
age.setText("");
city.setText("");
levelOfTraining.setText("");
}
}
最初のメソッドでは、Application を実装するクラスへの内部参照を設定し (そのメソッドを呼び出して 2 番目のウィンドウを呼び出せるように)、表示される初期リストを設定しています。2 つ目は、現在の犬が特定のデータを持っているかどうかを確認し、これに基づいてテキスト フィールドを設定します。
@FXML
private void delete() {
int selectedIndex = dogs.getSelectionModel().getSelectedIndex();
dogs.getItems().remove(selectedIndex);
}
@FXML
private void edit() {
int selectedIndex = dogs.getSelectionModel().getSelectedIndex();
dogs.getItems().set(selectedIndex, new Dog(name.getText(), breed.getText(), Integer.valueOf(age.getText()), city.getText(), Integer.valueOf(levelOfTraining.getText())));
}
@FXML
private void create() {
Dog someDog = new Dog();
appFX.showCreateWindow(someDog);
if (someDog.getName() != null && !someDog.getName().getValue().isEmpty()) {
appFX.getListDog().add(someDog);
}
}
}
ここでは、ボタンに関連付けられたベース ウィンドウの 3 つのメソッドを示します。
- 削除— 選択された(選択された)犬をインデックスによって削除します。
- 編集- 転送されたデータを使用して新しい犬を作成し、以前の犬の代わりにそれを設定します。
- create - 新しい犬を作成し、作成ウィンドウを呼び出すメソッドを呼び出し、新しいオブジェクトを渡し、名前が null でない場合はウィンドウを閉じた後、新しいペットを保存します。
@Data
public class CreateController {
private Stage dialogStage;
private Dog dog;
@FXML
private TextField nickName;
@FXML
private void ok() {
if (nickName != null && !nickName.getText().isEmpty()) {
dog.setName(new SimpleStringProperty(nickName.getText()));
dialogStage.close();
}
}
@FXML
private void cansel() {
dialogStage.close();
}
}
ここでは、ウィンドウ内のテキスト フィールドとの接続が表示され、[保存] ボタンと [キャンセル] ボタンが処理され、何らかの形でウィンドウが閉じられます。ご覧のとおり、私の小さなアプリケーションの利便性を高めるために、Lombok を使用しました。そうしないと、コードが非常に大きくなり、レビューに収まらなかったでしょう。
GO TO FULL VERSION