重新访问JavaFX自定义控件

重新访问JavaFX自定义控件,javafx,Javafx,我已经在这个站点上运行了,并尝试了各种其他解决方案,但是我仍然没有找到一个足够简单的示例来说明如何设置自定义控件,而自定义控件不是GUI的唯一部分。由于这个问题仍在出现,似乎我们需要一个更简单的例子 我试图创建一个简单的控件,它由一个垂直拆分窗格组成,该窗格的顶部有一个按钮,下部有一个标签。然后,我想将此SplitPane控件的实例放置在TabPane中的多个选项卡中。 要么控件不显示,要么我陷入各种错误,这取决于我尝试遵循的示例。因此,我将回溯一点,并且只简单地问:如何将SplitPane分离

我已经在这个站点上运行了,并尝试了各种其他解决方案,但是我仍然没有找到一个足够简单的示例来说明如何设置自定义控件,而自定义控件不是GUI的唯一部分。由于这个问题仍在出现,似乎我们需要一个更简单的例子

我试图创建一个简单的控件,它由一个垂直拆分窗格组成,该窗格的顶部有一个按钮,下部有一个标签。然后,我想将此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>
以及主要测试类别:

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标记替换为
。然后我创建了控制器类来扩展SplitPane。我曾尝试在FXML标记和/或控制器类中指定控制器,但始终没有正确指定。 有正确知识的人会愿意花几分钟来展示一个这样的工作示例吗?我猜更多的人会发现这样的例子非常有用。 因此,SplitPane应该是新的自定义控件,您可以在默认情况下将其加载到TabPane的第一个选项卡中。然后我将编写代码,将更多实例添加到后续选项卡中

事先非常感谢你

更新: 我已经将
SplitPane
分解为它自己的FXML和controller类。 以下是FXML(CustomSplitPane.FXML):

原来的主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>

CustomSplitPaneController
中的
fxmlLoader.load()
似乎导致了java.lang.StackOverflower错误。 也许现在对这里的人来说我错过了什么更明显了


再次感谢。

您的自定义控件类应该扩展
父类之一,例如
区域
窗格
。如果您懒得做布局工作,只需扩展一个高级窗格,如
GridPane

然后,您的自定义控件类应该加载一个包含
SplitPane
及其子级的FXML。FXML的控制器可以是这个自定义控件类,也可以将它分离到它的个人控制器类中。
SplitPane
节点应作为自定义控件类的子级添加;这意味着您的自定义控件(它是
父类
类型)必须处理一些布局逻辑。此时,自定义控件已完成


此控件已准备好在FXML中使用。但是,如果要在场景生成器中使用它,则需要将其打包到JAR文件中,并将其添加到场景生成器中。请注意,为了使场景生成器正常工作,您的自定义控件类必须定义一个无参数构造函数。

我不完全确定这是否是您要寻找的,但JavaFX控件基于模型、视图、控制器(MVC)模式

模型 其中模型类是存储系统任何信息的地方。例如,如果您有一个textField,您将在model类中存储该文本字段的值。我一直认为它是我控制的一个微型数据库

看法 视图类是控件的外观。定义大小、形状、颜色等。请注意“颜色”,这是设置控件默认颜色的地方。(这也可以使用FXML完成,但我个人更愿意使用java代码)。该模型通常作为使用bean属性绑定的参数传递给视图构造函数。(对于java,不确定如何使用FXML)

控制器 控制器类是可以进行操作的地方。如果我单击一个按钮,或者在我的文本字段中更改某些内容,那么控制器会做什么,或者它如何操作模型。模型和视图都作为参数传递给控制器。这为控制器提供了模型和视图的参考,允许控制器按设计操作每个模型和视图。其他外部类可以与控制器类交互,并且控制器类作用于模型和视图

话虽如此,在没有任何附加信息的情况下,看起来您所做的一切都只是将现有控件组合成预定义的可重用控件。可能值得研究定义一个扩展SplitPane的类,以及一个已经将按钮和标签添加到所需位置的构造函数。然后,可以将新类视为拆分窗格,并将按钮的操作内置

书中有一个很好的分解


按JavaFX 8示例介绍第6章FXML文件中的
fx:controller
属性是FXML加载程序创建指定类实例并将其用作FXML定义的UI层次结构的控制器的指令

在尝试创建发布的自定义拆分窗格时,创建
CustomSplitPaneController
的实例时会发生以下情况:

  • CustomSplitPaneController
    的构造函数中创建一个
    FXMLLoader
    ,它加载
    CustomSplitPane.fxml
  • CustomSplitPane.fxml
    有一个
    fx:controller
    属性,指定
    CustomSplitPaneController
    作为控制器类,因此它创建了一个
    CustomSplitPaneController
    的新实例(当然,通过调用其构造函数)
  • CustomSplitPaneController
    的构造函数创建一个
    fxmloader
    加载
    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>
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!");
    }
}
<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>
public class CustomSplitPane extends SplitPane {

    // ...

}
loader.setRoot(this);
loader.setController(this);
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!");
    }
}
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);
    }
}
<?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>
            <customcontroltest.CustomSplitPaneController /> 
         </content>
      </Tab>
   </tabs>
</TabPane>
package customcontroltest;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;

public class FXMLDocumentController implements Initializable
{
    @Override
    public void initialize(URL url, ResourceBundle rb)
    {
        // TODO
    }    
}
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<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>
package customcontroltest;

import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;


public class CustomSplitPaneController 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!");
    }
}