JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)

Posted Killing Vibe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)相关的知识,希望对你有一定的参考价值。

文章目录


博主个人社区:开发与算法学习社区

博主个人主页:Killing Vibe的博客

欢迎大家加入,一起交流学习~~

一、前言

首先声明一下,Java的强项并不在GUI上,现在是web的天下,博主发此篇文章仅是个人兴趣,如果您是学Java开发的,业余想做GUI小工具,可以选择使用JavaFX。如果需要开发企业级应用,推荐还是用qt或者wpf那些,用Electron也是很好的选择。

二、JavaFX与Scene Builder下载

从JDK11的发布开始,JavaFX已经不被包含在JDK中了。为了方便大家使用,可以直接使用JDK8。

下载地址如下:

JavaFX中文官方网站

不会前端?没关系。

Scene Builder作为一款Java拖拽式页面设计编码工具,具有强大的拖拽设计能力,对于一些入门以及需要快速响应页面编码的情况,该工具的效果绝对令人满意。

官方有详细的文档供大家参考,本文只介绍一下Scene Builder的简单使用,帮助大家极速上手用Java开发一款桌面级应用。

三、Scene Builder的使用

  1. 下载完后打开,选择Basic Application就行,助于理解

  1. 打开完后应该是这个界面

中间的就是咱们要设计的桌面程序的UI界面了,左边Library就有JavaFX的一些控件,比如按钮啊啥的,直接就可以拖动到中间,十分方便。我们可以根据自己的界面需求,设计自己的界面,最后只需要点一下保存就可以生成fxml文件了。

四、详细教学(示例)

4.1 环境配置

1.推荐使用Intellij-idea,第一步先构建一个项目(正常的话肯定构建一个Maven项目)

2.点击File—>点击settings—>Languages&Frameworks—>JavaFX

在Path to SceneBuilder 中填入下载好的SceneBuilder.exe文件。

4.2 创建fxml文件以及Controller类文件

什么是fxml文件?

FXML是一种以XML的格式表示JavaFX界面对象的文件,FXML文件中的每一个元素可以映射到JavaFX中的一个类,每个FXML元素的属性或者其子元素都可以映射为该对应JavaFXML类的属性.

说白了就是控制这个界面的文件,可以理解为界面的代码表示~~

为什么需要Controller类文件?

用来绑定这个fxml文件用的,用于控制这个界面的一些操作,实现一些功能~~

1.弄明白了之后,我们现在项目中创建两个文件,如下:

创建一个fxml文件:


创建一个Controller类文件:

然后修改一下这个fx:controller里面的属性


2.然后右键点击这个test.fxml,点击Open In SceneBuilder

3.然后就会弹出如下界面,也就是刚刚介绍过的SceneBuilder

4.3 自定义界面

下面随意做个栗子:

1.随便拉一个button控件到白色区域

2.然后可以在右边的properties属性栏更改这个按钮的样式,外观等等,例如我这里随便改了个hehe。

3.下面还有个code代码栏里面,可以设置这个按钮的id,点击事件等等,例如我这里也改成了hehe,你们也可以改成xxxbutton,这样便于开发

4.点击上方有个preview按钮可以预览我们当前设置好的界面

5.点击view,选择Show Sample Controller Skeleton,可以直接显示Controller类的代码骨架,特别方便!!

6.此时只需要复制这段代码到我们之前创建的TestController类中就行了。


7.别忘了点保存啊,点击File,然后点击save,然后我们之前创建的fxml文件就会自动补代码~~是不是很方便

8.原来的fxml文件就加入了button标签,还有对应的属性

4.4 运行我们的程序

上述两个文件弄好了之后,我们只需要创建一个入口类,就能运行起我们的程序了。

首先再创建一个Main类,然后把下面的代码复制进去(至于为什么可先不管,不懂可以私信博主,文章后面会简要说一下原理)

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.Objects;

/**
 * @author Dianzhi Han
 * @version 1.0
 * @description: TODO
 * @date 2022/11/18 12:45
 */

public class Main extends Application 

    @Override
    public void start(Stage primaryStage) throws Exception
        Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getClassLoader().getResource("test.fxml")));
        primaryStage.setTitle("test");
        primaryStage.setScene(new Scene(root, 1300, 1000));
        primaryStage.show();
    
    public static void main(String[] args) 
        launch(args);
    

然后点运行就可以了~~


可以根据自己的功能需求在click方法中修改你点击按钮可以实现的功能~

五、拓展

  • 之前我们创建的那个Controller类通常需要实现Initializable接口,并重写里面的initialize方法。用于在界面初始化的时候,初始化一些比如数据库数据表之类的东西
  • 执行程序的顺序是 init() -> start() -> stop(),我们入口类Main就是继承了Application抽象类,并重写了它的start()方法,而Controller类则可以重写init()方法,来做一些初始化相关的工作。

拿上面的按钮例子来解释一下标签中的属性是干嘛的吧~

  • fx:id指的就是这个控件的id值,为的是在Controller类中控制这个控件
  • onAction指的是点击这个按钮就能实现什么功能
  • text指的就是这个按钮的文本
  • 其余那些就是控制按钮大小的

总结

以上是博主千辛万苦纯手打的JavaFX的入门教程以及SceneBuilder使用,如果小伙伴们有什么不懂得,可以直接私信博主,有帮助的话麻烦点赞+收藏关注,您得支持是博主更新得最大动力~

自定义 JavaFX 控件 - Scene Builder 2.0 中的“已指定根值”

【中文标题】自定义 JavaFX 控件 - Scene Builder 2.0 中的“已指定根值”【英文标题】:Custom JavaFX control - "root value already specified" in Scene Builder 2.0 【发布时间】:2014-11-02 05:43:48 【问题描述】:

我已经实现了一个自定义控件,使用一个 fxml 文件和一个 Java 类,类似于this official tutorial 中解释的内容(参见下面的代码)。请注意,fxml 根元素是用fx:root 定义的,我以编程方式调用setRoot

我已尝试将控件包含在应用程序的 FXML 布局中,并且应用程序加载正常(并按预期显示控件)。

但是,如果我尝试在 Scene Builder 2.0 中导入包含我的控件的 jar 文件,该控件不会出现在要导入的组件列表中(与同一个 jar 中的某些其他控件不同)。如果我选择“显示 JAR 分析报告”,则会显示由javafx.fxml.LoadException: Root value already specified 引起的错误。

您知道为什么在 Scene Builder 中加载时出现此错误,即使它在实际应用程序中正确加载?

这是 FXML:

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

<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>

<fx:root type="javafx.scene.layout.GridPane" id="MediaMetadataDisplay" hgap="20.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0"
         prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <columnConstraints>
        <ColumnConstraints fillWidth="false" hgrow="NEVER" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="200.0"/>
        <ColumnConstraints halignment="LEFT" hgrow="ALWAYS"/>
    </columnConstraints>
    <rowConstraints>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="40.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES"/>
    </rowConstraints>
    <children>
        <ImageView id="coverView" fx:id="coverView" fitHeight="200.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.rowSpan="7"/>
        <Label id="trackName" fx:id="trackName" maxWidth="1.7976931348623157E308" text="trackName" GridPane.columnIndex="1" GridPane.rowIndex="1">
            <font>
                <Font name="System Bold" size="16.0"/>
            </font>
        </Label>
        <Label id="artist" fx:id="artist" maxWidth="1.7976931348623157E308" text="artist" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
        <Label id="album" fx:id="album" maxWidth="1.7976931348623157E308" text="album" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
        <Label id="genre" fx:id="genre" maxWidth="1.7976931348623157E308" text="genre" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
        <Label id="trackNumber" fx:id="trackNumber" maxWidth="1.7976931348623157E308" text="trackNumber" GridPane.columnIndex="1" GridPane.rowIndex="5"/>
    </children>
</fx:root>

还有 Java 控制器/根元素:

package customjavafx.scene.control;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.media.Media;

import java.io.IOException;
import java.util.Map;

public class MediaMetadataDisplay extends GridPane 

    public MediaMetadataDisplay() 
        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MediaMetadataDisplay.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try 
            fxmlLoader.load();
         catch (IOException exception) 
            throw new RuntimeException(exception);
        

        media.addListener((obs, oldVal, newVal) -> updateMedia(newVal));
    

    private final ObjectProperty<Media> media = new SimpleObjectProperty<>((Media) null, "media");
    @FXML private ImageView coverView;
    @FXML private Label trackName;
    @FXML private Label artist;
    @FXML private Label album;
    @FXML private Label genre;
    @FXML private Label trackNumber;

    public void updateMedia(Media media) 
        // TODO show updated metadata
    

    public ObjectProperty<Media> mediaProperty() 
        return media;
    
    public Media getMedia() 
        return mediaProperty().get();
    
    public void setMedia(final Media media) 
        mediaProperty().set(media);
    

错误原因,在 Scene Builder 显示的堆栈跟踪中:

Caused by: javafx.fxml.LoadException: Root value already specified.
file:/Users/guillaumegaly/Library/Application%20Support/Scene%20Builder/Library/custom-controls_2.11.jar!/customjavafx/scene/control/MediaMetadataDisplay.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2613)
    at javafx.fxml.FXMLLoader.createElement(FXMLLoader.java:2771)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2720)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
    at customjavafx.scene.control.MediaMetadataDisplay.<init>(MediaMetadataDisplay.java:26)
    ... 18 more

【问题讨论】:

粗略查看后,您的代码看起来不错,所以我不确定为什么它不起作用(尽管我没有尝试将它与 SceneBuilder 一起使用)。也许在importing custom controls in SceneBuilder 上查看 Rob Terp 的博客可能会提供一些见解。此外,您可以将您的问题交叉发布到Oracle JavaFX forums;我相信 SceneBuilder 开发人员有时会监控这些论坛。 我也看过那个教程,除了扩展不同的布局(AnchorPane 与 GridPane)之外,它对我来说似乎没有什么不同。我已按照您的建议在 Oracle 论坛上发布了我的问题,我们将看看它是如何进行的。 【参考方案1】:

删除线

fxmlLoader.setRoot(this);

您的 FXML 将根定义为 AnchorPane(您不能两次设置根,这就是您收到错误的原因)。

<fx:root type="javafx.scene.layout.GridPane" id="MediaMetadataDisplay" hgap="20.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0"
         prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">

非常感谢这篇文章

Root value already specified

【讨论】:

如果我删除setRoot,那么它怎么知道在哪里附加组件? Fx:root 标签会为你设置root。它基本上是javafx。 Fxml 文件 我以某种方式遇到了相反的问题(在 .fxml 文件中使用 &lt;fx:root... 语法时),如果我不调用 setRoot(),我会收到 no root specified 错误。【参考方案2】:

我一直在关注文档中的 Javafx FXML 教程,但我被困在了自定义组件部分。对我来说,问题是当我调用 setRoot 方法时,它会抛出“Root already specified”错误。就个人而言,我通过将根标记添加到自定义组件 FXML 文档中来解决此问题。现在可以了。

我添加这个是为了如果其他人有同样的问题,谷歌可以给他们答案。

【讨论】:

以上是关于JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)

JavaFX Scene Builder 使用基础

JavaFX的Scene Builder的奇怪屏幕大小

JavaFX Scene Builder 怎么用???

无法打开 Scene Builder JavaFX

自定义 JavaFX 控件 - Scene Builder 2.0 中的“已指定根值”