gpt4 book ai didi

重新审视 JavaFX 自定义控件

转载 作者:行者123 更新时间:2023-12-04 01:49:18 28 4
gpt4 key购买 nike

我已经运行了 Mastering FXML example , How to create custom components in JavaFX 2.0 using FXML并尝试了该站点的各种其他解决方案,但我仍然没有找到一个足够简单的示例来说明如何设置不是 GUI 的唯一部分的自定义控件。由于问题仍然出现,看来我们需要一个更简单的例子来帮助我们中的一些人......

我正在尝试创建一个简单的控件,该控件由一个垂直的 SplitPane 组成,顶部有一个 Button,下部有一个标签。然后我想将此 SplitPane 控件的实例放置在 TabPane 的多个选项卡中。要么控件不显示,要么我陷入各种错误,具体取决于我尝试遵循的示例。所以,我会回溯一下,只是简单地问:我如何分离出 SplitPane 成为此处的自定义控件?

这是 FXML 文档:

<?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.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>

<TabPane prefHeight="256.0" prefWidth="477.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="customcontroltest.FXMLDocumentController">
<tabs>
<Tab>
<content>
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefHeight="114.0" prefWidth="160.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Button fx:id="button" onAction="#handleButtonAction" text="Click Me!" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Label fx:id="label" minHeight="16" minWidth="69" />
</children>
</AnchorPane>
</items>
</SplitPane>
</content>
</Tab>
</tabs>
</TabPane>

和 Controller 类:

package customcontroltest;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class FXMLDocumentController implements Initializable
{

@FXML
private Label label;

@FXML
private void handleButtonAction(ActionEvent event)
{
label.setText("Hello World!");
}

@Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}
}

和主要测试类:

package customcontroltest;

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


public class CustomControlTest extends Application
{
@Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

Scene scene = new Scene(root);

stage.setScene(scene);
stage.show();
}

public static void main(String[] args)
{
launch(args);
}
}

我制作了一个新的 FXML 文件,并将整个 SplitPane 标签及其所有内容剪切/粘贴到其中。我将 FXML 文档中的 SplitPane 标记替换为 <packageName.ControlClassName /> .然后我制作了 Controller 类来扩展 SplitPane。我试过在 FXML 标记和/或 Controller 类中指定 Controller ,但从来没有做对。知识渊博的人愿意花几分钟来展示一个有效的例子吗?我猜想更多的人会发现这样的例子非常有用。因此,SplitPane 应该是新的自定义控件,然后您可以默认将其加载到 TabPane 的第一个选项卡中。然后我将编写代码以将更多实例添加到后续选项卡中。

非常感谢您。

更新:我已经打破了SplitPane进入它自己的 FXML 和 Controller 类。这是 FXML (CustomSplitPane.fxml):

<fx:root type="javafx.scene.control.SplitPane" dividerPositions="0.5" orientation="VERTICAL" prefHeight="114.0" prefWidth="160.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="customcontroltest.CustomSplitPaneController">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Button fx:id="button" onAction="#handleButtonAction" text="Click Me!" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Label fx:id="label" minHeight="16" minWidth="69" />
</children>
</AnchorPane>
</items>
</fx:root>

和 Controller 类(CustomSplitPaneController.java):

package customcontroltest;

public class CustomSplitPaneController extends AnchorPane
{
@FXML
private Label label;
private SplitPane mySplitPane;

public CustomSplitPaneController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("CustomSplitPane.fxml"));

try
{
fxmlLoader.load();
} catch (IOException exception)
{
throw new RuntimeException(exception);
}
}

@FXML
private void handleButtonAction(ActionEvent event)
{
label.setText("Hello World!");
}
}

原来的主 FXML 现在看起来像这样:

<TabPane prefHeight="256.0" prefWidth="477.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="customcontroltest.FXMLDocumentController">
<tabs>
<Tab>
<content>
<customcontroltest.CustomSplitPaneController />
</content>
</Tab>
</tabs>
</TabPane>

fxmlLoader.load()CustomSplitPaneController似乎导致了 java.lang.StackOverflowError。也许现在这里的人更清楚我所缺少的是什么?

再次感谢。

最佳答案

fx:controller FXML 文件中的属性是 FXML 加载器创建指定类的实例并将其用作 FXML 定义的 UI 层次结构的 Controller 的指令。

在尝试创建您发布的自定义拆分 Pane 时,当您创建 CustomSplitPaneController 的实例时会发生什么?是:

  • 您创建了一个 FXMLLoaderCustomSplitPaneController 的构造函数中,加载 CustomSplitPane.fxml
  • CustomSplitPane.fxml有一个 fx:controller属性指定 CustomSplitPaneController作为 Controller 类,因此它创建了 CustomSplitPaneController 的新实例(当然是通过调用它的构造函数)
  • CustomSplitPaneController 的构造函数创建一个 FXMLLoader加载 CustomSplitPane.fxml
  • 等等。

很快,您会遇到堆栈溢出异常。


JavaFX 中的控制类封装了 View 和 Controller 。在标准的 JavaFX 控件类中, View 由 Skin 表示。类和 Controller 由 Behavior类(class)。控件类本身扩展了 Node (或子类:Region),当您实例化它时,它会同时实例化皮肤和行为。皮肤定义控件的布局和外观,行为将各种输入操作映射到修改控件本身属性的实际代码。

在您尝试复制的模式中,显示为 herehere ,这里稍作修改。在此版本中,“ View ”由 FXML 文件定义, Controller (行为)直接在控件类本身中实现(没有单独的行为类)。

要完成这项工作,您必须使用与平常略有不同的 FXML。首先,当您使用自定义控件时,您将直接实例化控件类(无需了解定义其布局的 FXML)。所以如果你在 java 中使用它,你会做 new CustomSplitPane() ,如果你在 FXML 中使用它,你会做 <CustomSplitPane> .无论哪种方式,您都会调用自定义控件的构造函数(我称之为 CustomSplitPane )。

使用CustomSplitPane在 UI 层次结构中,它当然必须是 Node子类。如果你想让它成为一种 SplitPane , 你会让它扩展 SplitPane :

public class CustomSplitPane extends SplitPane {

// ...

}

现在,在 CustomSplitPane 的构造函数中,您需要加载定义布局的 FXML 文件,但您需要它来布置当前对象。 (在 FXML 文件的通常用法中,FXMLLoader 为层次结构的根创建一个指定类型的新节点,load() 方法返回它。您希望 FXMLLoader 使用现有对象作为层次结构的根。)为此,您使用 <fx:root>元素作为 FXML 文件的根元素,你告诉 FXMLLoader使用 this作为根:

loader.setRoot(this);

此外,由于处理程序方法是在当前对象中定义的,因此您还希望 Controller 成为当前对象:

loader.setController(this);

由于您将现有对象指定为 Controller ,因此您不能有 fx:controller FXML 文件中的属性。

所以你最终得到:

package customcontroltest;

public class CustomSplitPane extends SplitPane {
@FXML
private Label label;

public CustomSplitPaneController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("CustomSplitPane.fxml"));

fxmlLoader.setRoot(this);
fxmlLoader.setController(this);

try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}

@FXML
private void handleButtonAction(ActionEvent event)
label.setText("Hello World!");
}
}

和 FXML 文件:

<fx:root type="javafx.scene.control.SplitPane" dividerPositions="0.5" orientation="VERTICAL" prefHeight="114.0" prefWidth="160.0" xmlns:fx="http://javafx.com/fxml/1" >
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Button fx:id="button" onAction="#handleButtonAction" text="Click Me!" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Label fx:id="label" minHeight="16" minWidth="69" />
</children>
</AnchorPane>
</items>
</fx:root>

现在您可以根据需要在另一个 FXML 文件中使用它:

<TabPane prefHeight="256.0" prefWidth="477.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="customcontroltest.FXMLDocumentController">
<tabs>
<Tab>
<content>
<customcontroltest.CustomSplitPane />
</content>
</Tab>
</tabs>
</TabPane>

关于重新审视 JavaFX 自定义控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41579752/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com