初探JavaFx

课程作业要做 CSMA/CD 协议演示,用 JavaFX 画界面比 Swing 顺手。IDEA 自带 JavaFX 模板,配合 Scene Builder 拖控件,下面是第一次搭起来时的步骤备忘。

IDEA下开发JavaFx Application

New > project > JavaFx Application

新建后会有 Main 入口和示例 FXML,先能跑通再改布局。

添加SceneBuilder

  1. 下载SceneBuilder
  1. file | setting > JavaFx > add path

Scene Builder 里改 FXML,保存后 IDEA 里刷新即可,别手改 XML 和可视化两套不同步。

设置自定义图标

primaryStage.getIcons().add(new Image("file:res/internet.png"));

图标放 resresources,路径相对运行目录;打包后若图标丢,检查 Artifacts 里是否把资源目录打进去。

打包生成exe

File | project structure > Artifacts > JavaFx >(class | title | Navite Bundle)

注意目录文件要能找到

Native Bundle 会调 jpackage/JavaFX 打包工具,JDK 版本和模块路径要对;开发机 Windows 打 exe 最省事,跨平台要分别在目标系统打。

事件绑定

fxml

onAction add

在 Scene Builder 里给 Button 填 #handleSendDataPackage,或在 XML 里写 onAction="#handleSendDataPackage"

Java

添加函数

控制器里用 @FXML 标注同名方法,参数一般是 ActionEvent

fx:id 对象属性修改

  1. 添加一个fxml成员变量
  1. 调用setText("");

FXML 里 fx:id="host1SendNum" 对应 Java 里 @FXML private Label host1SendNum;,加载 FXML 后自动注入,再 host1SendNum.setText(...) 更新界面。

我的代码

下面是当时仿真界面的 FXML 和主类骨架,按钮事件里还没填协议逻辑,主要验证布局和资源加载:

MainApp.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>

<FlowPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="cn.thinkmoon.CSMA_CD.MainApp">
<children>
    <Pane prefHeight="400.0" prefWidth="800.0">
        <children>
            <Button layoutX="154.0" layoutY="206.0" mnemonicParsing="false" onAction="#handleSendDataPackage" text="发送数据包">
                <font>
                    <Font size="14.0" />
                </font></Button>
            <Button layoutX="283.0" layoutY="206.0" mnemonicParsing="false" text="重置操作">
                <font>
                    <Font size="14.0" />
                </font></Button>
            <TextArea layoutX="500.0" layoutY="51.0" prefHeight="299.0" prefWidth="277.0" />
            <Label fx:id="host1SendNum" layoutX="43.0" layoutY="70.0" text="主机1所需发送的数据包数:">
                <font>
                    <Font size="17.0" />
                </font>
            </Label>
            <Label layoutX="43.0" layoutY="130.0" text="主机2所需发送的数据包数:">
                <font>
                    <Font size="17.0" />
                </font>
            </Label>
            <Label layoutX="52.0" layoutY="291.0" text="主机1">
                <font>
                    <Font size="18.0" />
                </font>
            </Label>
            <Label layoutX="410.0" layoutY="291.0" text="主机2">
                <font>
                    <Font size="18.0" />
                </font>
            </Label>
            <TextField layoutX="279.0" layoutY="71.0" />
            <TextField layoutX="279.0" layoutY="130.0" />
            <Label layoutX="292.0" layoutY="14.0" text="模拟以太网发送过程">
                <font>
                    <Font size="17.0" />
                </font>
            </Label>
            <Label layoutX="48.0" layoutY="327.0" style="-fx-border-width: 1; -fx-border-color: #218ada;" styleClass="tip" text="信道空闲" textFill="#218ada">
                <padding>
                    <Insets bottom="2.0" left="2.0" right="2.0" top="1.0" />
                </padding>
            </Label>
            <Label layoutX="406.0" layoutY="327.0" style="-fx-border-width: 1; -fx-border-color: #218ada;" styleClass="tip" text="信道空闲" textFill="#218ada">
                <padding>
                    <Insets bottom="2.0" left="2.0" right="2.0" top="1.0" />
                </padding>
            </Label>
            <TextField layoutX="114.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="140.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="170.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="199.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="226.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="257.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="283.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="311.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="340.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
            <TextField layoutX="369.0" layoutY="292.0" prefHeight="23.0" prefWidth="19.0" />
        </children>
    </Pane>
      <DialogPane />
</children>
</FlowPane>

MainApp.java

package cn.thinkmoon.CSMA_CD;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.stage.Stage;

import java.io.IOException;

public class MainApp extends Application {
    private Stage primaryStage;
    @FXML
    private Label host1SendNum;
    @Override
    public void start(Stage primaryStage) throws Exception{
        this.primaryStage = primaryStage;
        initRootLayout();
    }
    /**
     * 初始化主界面布局
     */
    public void initRootLayout() {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("view/MainApp.fxml"));
            primaryStage.setTitle("CSMA/CD协议仿真及演示");
            primaryStage.setScene(new Scene(root, 800, 400));
            primaryStage.setResizable(false);
            primaryStage.getIcons().add(new Image("file:res/internet.png"));
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        launch(args);
    }
    @FXML
    private void handleSendDataPackage(ActionEvent event) {

    }
}

FXMLLoader.load 同包下 view/MainApp.fxml 要能找到;setResizable(false) 是作业固定窗口尺寸。后续在 handleSendDataPackage 里接仿真逻辑即可。

版权声明: 本文首发于 指尖魔法屋-初探JavaFxhttps://blog.thinkmoon.cn/post/520-notes-javafx-java/) 转载或引用必须申明原指尖魔法屋来源及源地址!