java 如何扩展使用 FXML 的自定义 JavaFX 组件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31569299/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to extend custom JavaFX components that use FXML
提问by ThatGuyThatDoesThings
How do I properly extend a custom JavaFX component to add or modify its GUI components if it uses FXML for the view?
如果自定义 JavaFX 组件使用 FXML 作为视图,我如何正确扩展自定义 JavaFX 组件以添加或修改其 GUI 组件?
As an example scenario, suppose I use the following approach of creating a custom JavaFX component:
作为示例场景,假设我使用以下方法创建自定义 JavaFX 组件:
SpecializedButton.java (Controller)
SpecializedButton.java(控制器)
package test;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class SpecializedButton extends HBox
{
@FXML private Button button;
@FXML private Label label;
public SpecializedButton()
{
FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );
loader.setRoot( this );
loader.setController( this );
try {
loader.load();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
// specialized methods for this specialized button
// ...
public void doSomething() {
button.setText( "Did something!" );
}
}
SpecializedButton.fxml (View)
SpecializedButton.fxml(查看)
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<fx:root type="HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<AnchorPane HBox.hgrow="ALWAYS">
<children>
<Label fx:id="label" text="Click to do something: " AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane HBox.hgrow="ALWAYS">
<children>
<Button fx:id="button" onAction="#doSomething" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</children>
</fx:root>
MyApp.java
我的应用程序
package test;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MyApp extends Application
{
public static void main( String[] args )
{
launch( args );
}
public void start( Stage stage ) throws Exception
{
stage.setScene( new Scene( new SpecializedButton(), 300, 50 ) );
stage.show();
}
}
The SpecializedButton class loads the FXML view (SpecializedButton.fxml) and acts as the controller for it.
SpecializedButton 类加载 FXML 视图 (SpecializedButton.fxml) 并充当它的控制器。
The SpecializedButton FXML view just creates an HBox with two AnchorPanes and a Label and Button in the left and right sides, respectively. When the button is clicked, it calls doSomething() in the SpecializedButton controller class.
SpecializedButton FXML 视图只是创建了一个 HBox,其中有两个 AnchorPanes,分别在左侧和右侧有一个 Label 和 Button。单击按钮时,它会调用 SpecializedButton 控制器类中的 doSomething()。
Problem
问题
With this setup, I am using FXML to separate the view from the application logic.
通过此设置,我使用 FXML 将视图与应用程序逻辑分开。
However, if I want to create a new HighlySpecializedButtonclass that extends SpecializedButton, I don't know how to go about "extending" the view as well.
但是,如果我想创建一个扩展 SpecializedButton的新的HighSpecializedButton类,我也不知道如何“扩展”视图。
If I wanted to use the SpecializedButton view, but modify it for the HighlySpecializedButton, I'd have to copy the view code into a new FXML file, resulting in duplicate code instead of proper inheritance.
如果我想使用 SpecializedButton 视图,但为 HighSpecializedButton 修改它,我必须将视图代码复制到新的 FXML 文件中,从而导致代码重复而不是正确的继承。
If I wasn't using FXML, and was creating the GUI components within a Java class instead, extending that class would give me the ability to properly inherit and add/modify the components from the super class.
如果我没有使用 FXML,而是在 Java 类中创建 GUI 组件,那么扩展该类将使我能够正确继承和添加/修改来自超类的组件。
I am clearly misunderstanding some very important concepts about JavaFX.
我显然误解了有关 JavaFX 的一些非常重要的概念。
Question
问题
Are FXML views in this case so "dumb" that I'd have to create an entirely new FXML view every time I wanted to inherit from a custom component?
在这种情况下,FXML 视图是否如此“愚蠢”,以至于每次我想从自定义组件继承时都必须创建一个全新的 FXML 视图?
Or, if I am misunderstanding the real issue, what is the proper way of creating extendable custom JavaFX components, with or without FXML?
或者,如果我误解了真正的问题,使用或不使用 FXML 创建可扩展的自定义 JavaFX 组件的正确方法是什么?
I'd greatly appreciate any insight. Thanks in advance!
我将不胜感激任何见解。提前致谢!
采纳答案by mrak
From what I understand, you want to be able to extend existing components and avoid copy & pasting the entire markup for the new component.
据我了解,您希望能够扩展现有组件并避免复制和粘贴新组件的整个标记。
You can define an extension point for the component with the @DefaultProperty
annotation.
Look at e.x. javafx.scene.layout.Pane
: There is a ObservableList getChildren()
defined with @DefaultProperty("children")
.
In this way, when you use e.x an HBox
(extends Pane) as the root element for your component, all child components are added to the children
property defined by the Pane.
您可以使用@DefaultProperty
注释为组件定义扩展点。看看 ex javafx.scene.layout.Pane
:有一个ObservableList getChildren()
定义的 with @DefaultProperty("children")
。这样,当您使用 ex an HBox
(extends Pane) 作为组件的根元素时,所有子组件都会添加到children
Pane 定义的属性中。
In the same way you can define extension points in FXML:
以同样的方式,您可以在 FXML 中定义扩展点:
SpecializedButton.fxml(I simplified it a little for this example)
SpecializedButton.fxml(我在这个例子中简化了一点)
<fx:root type="HBox">
<HBox>
<Label fx:id="label" text="Click to do something: " />
<HBox fx:id="extension"></HBox>
</HBox>
<HBox>
<Button fx:id="button" onAction="#doSomething"/>
</HBox>
</fx:root>
The HBox fx:id="extension"
will be my extension point:
这HBox fx:id="extension"
将是我的扩展点:
@DefaultProperty(value = "extension")
public class SpecializedButton extends HBox
{
@FXML private HBox extension;
public ObservableList<Node> getExtension() {
return extension.getChildren();
}
// ... more component specific code
}
SuperSpecializedButton.fxml
SuperSpecializedButton.fxml
<fx:root type="SpecializedButton">
<Label text="EXTENDED HALLO"/>
</fx:root>
And:
和:
public class SuperSpecializedButton extends SpecializedButton
{
// ... more component specific code
}
As you can see, I need only to define fxml for the super-specialized component.
如您所见,我只需要为超级专用组件定义 fxml。
This code will work only on JavaFX >= 2.1. Beforehand the component injection for the super class didn't worked with the FXMLLoader.
此代码仅适用于 JavaFX >= 2.1。之前,超类的组件注入不适用于 FXMLLoader。
I added an working example on github: https://github.com/matrak/javafx-extend-fxml
我在 github 上添加了一个工作示例:https: //github.com/matrak/javafx-extend-fxml
回答by Miguel Cardenas
I haven't done that before, but you could implement the Initializableinterface to setup the view component when you instantiate a new custom component.
我以前没有这样做过,但是当你实例化一个新的自定义组件时,你可以实现Initializable接口来设置视图组件。
To add new component(labels, buttons, img, etc...) I would wrap all the children component on the view file, within an AnchorPane, GridPane, VBox or the one you prefer to add more components programmatically. Since you are extending your class from HBox you could do this already.
要添加新组件(标签、按钮、img 等...),我会将视图文件中的所有子组件包装在 AnchorPane、GridPane、VBox 或您喜欢以编程方式添加更多组件的组件中。由于您是从 HBox 扩展您的类,因此您已经可以做到这一点。
Here's what I would do:
这是我会做的:
public class SpecializedButton extends HBox implements Initializable{
@FXML private Button button;
@FXML private Label label;
public SpecializedButton(){
FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );
loader.setRoot( this );
loader.setController( this );
try {
loader.load();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
// specialized methods for this specialized button
// ...
public void doSomething() {
button.setText( "Did something!" );
}
@Override
public void initialize(URL url, ResourceBundle rb) {
button.setText("Initialized Text");
}
}
So for the HighlySpecializedButton class:
所以对于 HighSpecializedButton 类:
public class HighlySpecializedButton extends SpecializedButton {
public HighlySpecializedButton (){
super();
}
// HighlySpecializedButton methods
@Override
public void initialize(URL url, ResourceBundle rb) {
this.getChildren().add(new Button("New Button"));
}
}
Hope this could help you
希望这可以帮助你