【问题标题】:Using JavaFX controller without FXML使用没有 FXML 的 JavaFX 控制器
【发布时间】:2016-08-20 11:40:02
【问题描述】:

是否有可能在不使用 FXML 的情况下使用带有 JavaFX GUI 的控制器。

我注意到 FXML 文件包含一个 fx-controller 属性来绑定控制器,但我发现它不是一个简单的方法。

关于在不使用 FXML 文件或 JavaFX Scene Builder 的情况下使用 JavaFX 构建 MVC 架构的任何想法?

【问题讨论】:

  • 您不必使用 FXML 或 SceneBuilder。您可以简单地自己创建对象并将它们自己添加到您的场景/舞台中。至于你如何实现它,它是完全开放的。我已经实现了一个屏幕管理库,它可以处理 FXML 或手动创建的屏幕。可以的:)

标签: java javafx-8 fxml


【解决方案1】:

对于我们其他人...这是一个非常简单的示例,展示了如何在不使用任何 FXML 文件的情况下创建 JavaFX 表单。这个例子可以在已经运行的应用程序中使用,所以我跳过了 Main 类和所有这些......它只是为了展示 JavaFX 的简单性。

简而言之,您只需基于 AnchorPane 等容器创建场景,然后创建舞台并将场景分配给舞台...添加控件然后显示舞台

package javafx;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class SimpleFX {

    private AnchorPane anchorPane;

    private TextArea textArea () {
        TextArea textArea = new TextArea();
        textArea.setLayoutX(20);
        textArea.setLayoutY(20);
        textArea.setMaxWidth(450);
        textArea.setMinHeight(380);
        return textArea;
    }

    private TextField textField () {
        TextField textField = new TextField();
        textField.setLayoutX(20);
        textField.setLayoutY(410);
        textField.setMinWidth(450);
        textField.setMinHeight(25);
        return textField;
    }

    private Button button() {
        Button button = new Button("Button");
        button.setLayoutX(240);
        button.setLayoutY(450);
        return button;
    }


    private void addControls    () {
        anchorPane.getChildren().add(0,textArea());
        anchorPane.getChildren().add(1,textField());
        anchorPane.getChildren().add(2,button());
    }

    public void startForm () {
        anchorPane = new AnchorPane();
        Scene scene = new Scene(anchorPane, 500, 500);
        Stage stage = new Stage();
        stage.setScene(scene);
        addControls();
        stage.show();
    }
}

【讨论】:

    【解决方案2】:

    您的问题对我来说并不是特别清楚:您只需创建类并基本上将所有内容与听众联系在一起。我不知道这是否有帮助,但这是一个简单的示例,它只有几个文本字段和一个显示它们总和的标签。这就是我认为的“经典 MVC”:视图观察模型并在模型发生变化时更新 UI 元素。它向 UI 元素注册处理程序,并在事件发生时委托给控制器:控制器依次处理输入(如有必要)并更新模型。

    型号:

    package mvcexample;
    
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.ReadOnlyIntegerWrapper;
    import javafx.beans.property.SimpleIntegerProperty;
    
    public class AdditionModel {
        private final IntegerProperty x = new SimpleIntegerProperty();
        private final IntegerProperty y = new SimpleIntegerProperty();
        private final ReadOnlyIntegerWrapper sum = new ReadOnlyIntegerWrapper();
    
        public AdditionModel() {
            sum.bind(x.add(y));
        }
    
        public final IntegerProperty xProperty() {
            return this.x;
        }
    
        public final int getX() {
            return this.xProperty().get();
        }
    
        public final void setX(final int x) {
            this.xProperty().set(x);
        }
    
        public final IntegerProperty yProperty() {
            return this.y;
        }
    
        public final int getY() {
            return this.yProperty().get();
        }
    
        public final void setY(final int y) {
            this.yProperty().set(y);
        }
    
        public final javafx.beans.property.ReadOnlyIntegerProperty sumProperty() {
            return this.sum.getReadOnlyProperty();
        }
    
        public final int getSum() {
            return this.sumProperty().get();
        }
    
    
    
    }
    

    控制器:

    package mvcexample;
    
    public class AdditionController {
    
        private final AdditionModel model ;
    
        public AdditionController(AdditionModel model) {
            this.model = model ;
        }
    
        public void updateX(String x) {
            model.setX(convertStringToInt(x));
        }
    
        public void updateY(String y) {
            model.setY(convertStringToInt(y));
        }
    
        private int convertStringToInt(String s) {
            if (s == null || s.isEmpty()) {
                return 0 ;
            }
            if ("-".equals(s)) {
                return 0 ;
            }
            return Integer.parseInt(s);
        }
    }
    

    查看:

    package mvcexample;
    
    import javafx.geometry.HPos;
    import javafx.geometry.Pos;
    import javafx.scene.Parent;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextField;
    import javafx.scene.control.TextFormatter;
    import javafx.scene.control.TextFormatter.Change;
    import javafx.scene.layout.ColumnConstraints;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.Priority;
    
    public class AdditionView {
        private GridPane view ;
        private TextField xField;
        private TextField yField;
        private Label sumLabel;
    
        private AdditionController controller ;
        private AdditionModel model ;
    
        public AdditionView(AdditionController controller, AdditionModel model) {
    
            this.controller = controller ;
            this.model = model ;
    
            createAndConfigurePane();
    
            createAndLayoutControls();
    
            updateControllerFromListeners();
    
            observeModelAndUpdateControls();
    
        }
    
        public Parent asParent() {
            return view ;
        }
    
        private void observeModelAndUpdateControls() {
            model.xProperty().addListener((obs, oldX, newX) -> 
                    updateIfNeeded(newX, xField));
    
            model.yProperty().addListener((obs, oldY, newY) -> 
                    updateIfNeeded(newY, yField));
    
            sumLabel.textProperty().bind(model.sumProperty().asString());
        }
    
        private void updateIfNeeded(Number value, TextField field) {
            String s = value.toString() ;
            if (! field.getText().equals(s)) {
                field.setText(s);
            }
        }
    
        private void updateControllerFromListeners() {
            xField.textProperty().addListener((obs, oldText, newText) -> controller.updateX(newText));
            yField.textProperty().addListener((obs, oldText, newText) -> controller.updateY(newText));
        }
    
        private void createAndLayoutControls() {
            xField = new TextField();
            configTextFieldForInts(xField);
    
            yField = new TextField();
            configTextFieldForInts(yField);
    
            sumLabel = new Label();
    
            view.addRow(0, new Label("X:"), xField);
            view.addRow(1, new Label("Y:"), yField);
            view.addRow(2, new Label("Sum:"), sumLabel);
        }
    
        private void createAndConfigurePane() {
            view = new GridPane();
    
            ColumnConstraints leftCol = new ColumnConstraints();
            leftCol.setHalignment(HPos.RIGHT);
            leftCol.setHgrow(Priority.NEVER);
    
            ColumnConstraints rightCol = new ColumnConstraints();
            rightCol.setHgrow(Priority.SOMETIMES);
    
            view.getColumnConstraints().addAll(leftCol, rightCol);
    
            view.setAlignment(Pos.CENTER);
            view.setHgap(5);
            view.setVgap(10);
        }
    
        private void configTextFieldForInts(TextField field) {
            field.setTextFormatter(new TextFormatter<Integer>((Change c) -> {
                if (c.getControlNewText().matches("-?\\d*")) {
                    return c ;
                }
                return null ;
            }));
        }
    }
    

    应用类:

    package mvcexample;
    
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    public class MVCExample extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            AdditionModel model = new AdditionModel();
            AdditionController controller = new AdditionController(model);
            AdditionView view = new AdditionView(controller, model);
    
            Scene scene = new Scene(view.asParent(), 400, 400);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 这就是我所说的。在没有 FXML 的 JavaFX 上使用“经典 MVC”;谢谢,这很公平
    • 您是否有理由不只是在视图类中扩展 GridPane?为什么要改用 getAsParent 方法?
    • @hubbabubba 基本 API 设计。如果视图扩展GridPane,则客户端代码可以将其实例分配给类型为GridPane 的变量,例如GridPane view = new AdditionView(...);。这意味着我永远无法更改为使用不同的布局。布局应该是视图类的实现细节,而不是暴露给类的公共 API 的东西。我设置它的方式,我只保证它是某种Parent,所以我以后可以更改为使用不同的布局而不影响任何其他代码。
    【解决方案3】:

    我广泛使用 JavaFX,不使用 FXML 或场景构建器。所以我可以保证它可以做到。

    以下是我的 IDE 为获取 JavaFX 主类而自动生成的代码。这将是您的应用程序的根。然后您将添加到它以创建您的应用程序。

    public class NewFXMain extends Application {
    
    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {
    
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });
    
        StackPane root = new StackPane();
        root.getChildren().add(btn);
    
        Scene scene = new Scene(root, 300, 250);
    
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
    
    }
    

    【讨论】:

    • 感谢您的回答,但我想知道如何在没有 FXML 的情况下将控制器绑定到视图,因为我希望它作为 MVC 3 层
    • @HassamAbdelillah 什么观点?如果您不使用 FXML,则无需绑定任何内容。您只需将调用从 UI 组件直接传递到控制器组件。当您创建组件时,无论如何您都将它们的操作绑定到代码中的控制器操作,因此在此实例中没有任何可绑定的内容。对于 FXML,您只需要该方法。您可以在上面 Jose 的回答中看到该操作绑定到控件。
    猜你喜欢
    • 2015-05-29
    • 2013-12-25
    • 2017-06-29
    • 2012-09-14
    • 2013-07-22
    • 1970-01-01
    • 2014-06-11
    • 2014-11-07
    相关资源
    最近更新 更多