当前位置: 首页 > 知识库问答 >
问题:

JavaFX将重复视图同步到同一控制器(FXML

支才
2023-03-14

下面是一个说明问题的小应用程序:

ButtonPanel.fxml

<ScrollPane fx:controller="ButtonPanelController">
    <VBox>
        <Button fx:id="myButton" text="Click Me" onAction="#buttonClickedAction" />
    </VBox>
</ScrollPane>

按钮PanelController。Java语言

public class ButtonPanelController {

    @FXML
    Button myButton;

    boolean isRed = false;

    public void buttonClickedAction(ActionEvent event) {
        if(isRed) {
            myButton.setStyle("");
        } else {
            myButton.setStyle("-fx-background-color: red");
        }
        isRed = !isRed;
    }
}

TestApp。Java语言

public class TestApp extends Application {

    ButtonPanelController buttonController;

    @Override
    public void start(Stage stage) throws Exception {
        // 1st Stage
        stage.setTitle("1st Stage");
        stage.setWidth(200);
        stage.setHeight(200);
        stage.setResizable(false);

        // Load FXML
        FXMLLoader loader = new FXMLLoader(
                ButtonPanelController.class.getResource("ButtonPanel.fxml"));
        Parent root = (Parent) loader.load();

        // Grab the instance of ButtonPanelController
        buttonController = loader.getController();

        // Show 1st Scene
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();

        // 2nd Stage
        Stage stage2 = new Stage();
        stage2.setTitle("2nd Stage");
        stage2.setWidth(200);
        stage2.setHeight(200);
        stage2.setResizable(false);

        /* Override the ControllerFactory callback to use 
         * the stored instance of ButtonPanelController
         * instead of creating a new one.
         */
        Callback<Class<?>, Object> controllerFactory = type -> {
            if(type == ButtonPanelController.class) {
                return buttonController;
            } else {
                try {
                    return type.newInstance();
                } catch(Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

        // Load FXML
        FXMLLoader loader2 = new FXMLLoader(
                ButtonPanelController.class.getResource("ButtonPanel.fxml"));
        // Set the ControllerFactory before the load takes place
        loader2.setControllerFactory(controllerFactory);
        Parent root2 = (Parent) loader2.load();

        // Show 2nd Scene
        Scene scene2 = new Scene(root2);
        stage2.setScene(scene2);
        stage2.show();
    }

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

基本上,我有一个FXML,用于两个单独的场景,它们可能在屏幕上同时处于活动状态,也可能不处于活动状态。这方面的一个实际例子是,将内容固定到一个侧面板上,再加上一个按钮,该按钮可以在一个可以拖动/调整大小的单独窗口中打开相同的内容。

我试图实现的目标是保持视图同步(其中一个视图的更改会影响另一个视图)。

我可以通过回调将两个视图指向同一个控制器,但是我现在遇到的问题是UI更改仅反映在第二个场景中。两个视图都与控制器对话,但控制器只与第二个场景对话。我假设JavaFX实现MVC或IOC的某些东西在通过FXMLLoader加载时以某种1:1的关系将控制器链接到视图。

我很清楚,尝试将两个视图链接到一个控制器是不好的MVC实践,但是我希望避免实现实际上完全相同的单独FXML和控制器。

有可能实现我上面列出的这种同步吗?

如果我需要创建一个单独的控制器,最好的html" target="_blank">方法是什么来确保两个用户界面同步(甚至是边栏移动)?

提前感谢!

-史蒂夫

共有1个答案

林亦
2023-03-14

代码不起作用的原因是FXML加载器将对FXML中具有fx:id属性的元素的引用注入控制器中具有匹配名称的字段中。因此,当您第一次加载FXML文件时,FXMLLoader将字段myButton设置为加载FXML时创建的按钮的引用。由于第二次加载FXML时使用的是完全相同的控制器实例,因此FXML加载程序现在将同一字段(在同一控制器实例中)设置为对再次加载FXML文件时创建的按钮的引用。换句话说,按钮控制器。myButton现在指的是创建的第二个按钮,而不是第一个。所以当你调用myButton时。设置样式(…) 它更新第二个按钮的样式。

基本上,您总是希望每个视图实例都有一个控制器实例。您需要的是两个控制器访问相同的共享状态。

创建一个存储数据的模型类。在MVC架构中,视图观察模型并在模型中的数据发生变化时更新。控制器对用户与视图的交互做出反应并更新模型。

(可以说,FXML为您提供了更多类似的MVP架构。这也有变体,但通常演示者会观察模型并在模型中的数据发生变化时更新视图,并更新模型以响应用户交互。)

因此,您的模型可能看起来像:

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;

public class Model {

    private final BooleanProperty red = new SimpleBooleanProperty();

    public final BooleanProperty redProperty() {
        return this.red;
    }


    public final boolean isRed() {
        return this.redProperty().get();
    }


    public final void setRed(final boolean red) {
        this.redProperty().set(red);
    }


    public void toggleRed() {
        setRed(! isRed() );
    }
}

您的ButtonPanel.fxml不会改变:

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

<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>

<ScrollPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="ButtonPanelController">
    <VBox >
        <Button fx:id="myButton" text="Click Me" onAction="#buttonClickedAction" />
    </VBox>
</ScrollPane>

您的控制器有对模型的引用。它可以使用模型属性上的绑定或侦听器来更新UI,处理程序方法只是更新模型:

import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class ButtonPanelController {

    @FXML
    Button myButton;

    boolean isRed = false;

    private Model model ;

    public ButtonPanelController(Model model) {
        this.model = model ;
    }

    public void initialize() {

        myButton.styleProperty().bind(Bindings.
                when(model.redProperty()).
                then("-fx-background-color: red;").
                otherwise("")
        );
    }

    public void buttonClickedAction(ActionEvent event) {
        model.toggleRed();
    }
}

最后,保持所有内容的同步,因为视图是同一模型的视图。换句话说,您只需创建一个模型并将其引用传递给两个控制器。由于我将模型作为控制器中的构造函数参数(这很好,因为您知道在创建实例后就有了模型),因此我们需要一个控制器工厂来创建控制器实例:

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

public class TestApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        // 1st Stage
        stage.setTitle("1st Stage");
        stage.setWidth(200);
        stage.setHeight(200);
        stage.setResizable(false);

        // The one and only model we will use for both views and controllers:
        Model model = new Model();

        /* Override the ControllerFactory callback to create 
         * the controller using the model:
         */
        Callback<Class<?>, Object> controllerFactory = type -> {
            if(type == ButtonPanelController.class) {
                return new ButtonPanelController(model);
            } else {
                try {
                    return type.newInstance();
                } catch(Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

        // Load FXML
        FXMLLoader loader = new FXMLLoader(
                ButtonPanelController.class.getResource("ButtonPanel.fxml"));
        loader.setControllerFactory(controllerFactory);

        Parent root = (Parent) loader.load();

        // Show 1st Scene
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();

        // 2nd Stage
        Stage stage2 = new Stage();
        stage2.setTitle("2nd Stage");
        stage2.setWidth(200);
        stage2.setHeight(200);
        stage2.setResizable(false);

        // Load FXML
        FXMLLoader loader2 = new FXMLLoader(
                ButtonPanelController.class.getResource("ButtonPanel.fxml"));
        // Set the ControllerFactory before the load takes place
        loader2.setControllerFactory(controllerFactory);
        Parent root2 = (Parent) loader2.load();

        // Show 2nd Scene
        Scene scene2 = new Scene(root2);
        stage2.setScene(scene2);
        stage2.show();
    }

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

 类似资料:
  • 93.背景 HBase 中的当前复制异步。因此,如果主集群崩溃,则从集群可能没有最新数据。如果用户想要强一致性,那么他们就无法切换到从属群集。 94.设计 请参阅 HBASE-19064 上的设计文档 95.运行和维护 Case.1 设置两个同步复制集群 在源集群和对等集群中添加同步对等体。 对于源群集: hbase> add_peer '1', CLUSTER_KEY => 'lg-hadoo

  • 问题内容: 我正在使用SpriteKit制作游戏。我有3个viewControllers:选择级别vc,游戏vc和win vc。游戏结束后,我想显示Win vc,然后如果我按Win vc上的“确定”按钮,我想关闭Win vc和游戏vc(将两个视图控制器弹出堆栈)。但我不知道该怎么办,因为如果我打电话 Win vc(堆栈顶部)被关闭了,所以我不知道在哪里再次调用它来关闭游戏vc。有什么方法可以在不使

  • 问题内容: 我开始使用Angular.JS。 我有许多共享同一控制器的视图。每个视图都是收集存储在控制器中的数据的步骤: itemSubmitter控制器: 这是第一个视图: 此方法可以实时更新“您的文本是:”段落。 但是,在加载下一个视图时,将重置为其默认值。如何使存储在控制器实例中的值在视图之间持久化? 问题答案: 更改路线时会布置控制器。这是个好习惯,因为您不应该依赖控制器在视图之间传送数据

  • 在我看来,将myObject序列化为JSON和f的Spring代码将同时尝试在get()返回时访问myObject。除了返回MyObject的深度副本之外,还有什么方法可以防止这种情况发生吗?

  • 问题内容: 因此,我有一个带有按钮的根视图控制器,当用户按下该按钮时,将显示另一个视图控制器。第二个控制器具有一个关闭选项,该选项仅返回到根视图控制器,还有一个按钮,当用户触摸它时,该按钮将关闭当前视图控制器,以便它再次返回到根视图控制器并显示另一个按钮。转到我使用的第一个控制器: 在另一个视图控制器中,我选择仅关闭的按钮,然后执行此操作。 因此,对于需要解雇并显示另一个控制器的第二个控制器,我尝

  • 问题内容: 我没有找到有关此问题的文章,但没有一个解决我的问题。 就像我说的那样。 ViewControllerA ViewControllerB 我试图将添加为的子视图,但是它 抛出类似“ ” 的错误。 下面是代码… ViewControllerA ViewControllerB只是一个带有标签的简单屏幕。 ViewControllerB EDIT 根据用户答案的​​建议解决方案,ViewCon