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

在MVC模式中处理Textfield设置操作的正确方法是什么

朱通
2023-03-14

我在试图让我的文本字段与我在控制器类中定义的ActionEvent句柄连接时遇到问题。这个错误是由java造成的。朗,反思一下。调用TargetException。我一直试图在视图中创建控制器类的实例,然后使用lamba方法引用调用控制器类中的handle方法。

查看类

package converter;

import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;

public class View extends BorderPane{
    
    private Controller control = new Controller(new Model(), new View());
    
    //TextField
    private TextField input = new TextField();
    private TextField input2 = new TextField();

    //RadioButton
    private RadioButton distence = new RadioButton();
    private RadioButton tempeture = new RadioButton();
    private RadioButton weight = new RadioButton();
    //ToggleGroup
    private ToggleGroup group = new ToggleGroup();


    public String getConversion() 
    {
        return group.getSelectedToggle().getUserData().toString();
    }

    public double getInput() 
    {
        return Double.parseDouble(input.getText());
    }
    public double getInput2() 
    {
        return Double.parseDouble(input2.getText());
    }

    public void setInput(double value) 
    {
        input.setText(Double.toString(value));
    }

    public void setInput2(double value) 
    {
        input2.setText(Double.toString(value));
    }
    

    
    public View() 
    {
        
        System.out.println(control);
        //SetID
        //input.setPromptText("Input");
        //output.setPromptText("Output");
        
        input.setId("input");
        input2.setId("input2");

        //SetUserData
        distence.setUserData("dist");
        tempeture.setUserData("temp");
        weight.setUserData("weight");
        
        //Set Label
        distence.setText("Mile and Kilometer");
        tempeture.setText("Celsius and Fahrenheit");
        weight.setText("Pounds and Kilograms");

        //SetGroup
        distence.setToggleGroup(group);
        tempeture.setToggleGroup(group);
        weight.setToggleGroup(group);
        
        //Add TextField ActionEvent
        input.setOnAction(control::handle);
        input2.setOnAction(control::handle);
        
        //Add Group Listener
        group.selectedToggleProperty().addListener((ov, o , n) ->{
//          System.out.println(n.getUserData().toString());
            String tog = n.getUserData().toString();
            if (tog.equals("dist")) {
                
                input.setPromptText("Mile");
                input2.setPromptText("Kilometer");
                
            }else if(tog.equals("temp")) {
                input.setPromptText("Fahrenheit");
                input2.setPromptText("Celsius");
            }else if(tog.equals("weight")) {
                input.setPromptText("Pound");
                input2.setPromptText("Kilogram");
            }
        });
        
        StackPane left = new StackPane();
        StackPane right = new StackPane();

        VBox leftbox = new VBox(3);
        VBox rightbox = new VBox(2);

        leftbox.setSpacing(5);
        rightbox.setSpacing(10);

        leftbox.getChildren().addAll(distence, tempeture, weight);
        left.getChildren().add(leftbox);

        rightbox.getChildren().addAll(input, input2);
        right.getChildren().add(rightbox);

        this.setLeft(left);
        this.setRight(right);
        
    
    }

}

控制器类

public class Controller implements EventHandler<ActionEvent>{
    private Model model;
    private View view;

    public Controller(Model model, View view) 
    {
        this.model = model;
        this.view = view;
    }
    
    public Controller() 
    {
        initalize();
    }
    
    public Controller initalize() 
    {
        this.model = new Model();
        this.view = new View();
        
        return this;
    }
    
    @Override
    public void handle(ActionEvent event) 
    {
        String id = ((javafx.scene.Node)event.getSource()).getId();
        String conversion = view.getConversion();

        switch (id) {
        case "input":

            if(conversion.equals("dist")) {
                view.setInput2(model.kilometer(view.getInput()));
                break;
            }else if(conversion.equals("temp")) {
                view.setInput2(model.cToF(view.getInput()));
                break;
            }else if(conversion.equals("weight")) {
                view.setInput2(model.kilogram(view.getInput()));
                break;
            }
        case "input2":

            if(conversion.equals("dist")) {
                view.setInput(model.mile(view.getInput2()));
                break;
            }else if(conversion.equals("temp")) {
                view.setInput(model.fToC(view.getInput2()));
                break;
            }else if(conversion.equals("weight")) {
                view.setInput(model.pound(view.getInput2()));
                break;
            }

        default:
            break;
        }
    }

}

主课

package converter;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
    private View view;
    
    @Override
    public void init() 
    {
        Model model = new Model();
        view = new View();
        new Controller(model, view);
    }

    @Override
    public void start(Stage primaryStage) {
        try {
            primaryStage.setMinWidth(350);
            primaryStage.setMinHeight(150);
            
            primaryStage.setScene(new Scene(view));
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }

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

堆栈跟踪

Exception in Application init method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1071)
Caused by: java.lang.RuntimeException: Exception in Application init method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:896)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
    at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: java.lang.StackOverflowError
    at javafx.graphics/javafx.scene.Node.getScene(Node.java:1148)
    at javafx.graphics/javafx.scene.Node.updateCanReceiveFocus(Node.java:8502)
    at javafx.graphics/javafx.scene.Node.setTreeVisible(Node.java:8420)
    at javafx.graphics/javafx.scene.Node.updateTreeVisible(Node.java:8411)
    at javafx.graphics/javafx.scene.Node.<init>(Node.java:2596)
    at javafx.graphics/javafx.scene.Parent.<init>(Parent.java:1418)
    at javafx.graphics/javafx.scene.layout.Region.<init>(Region.java:627)
    at javafx.graphics/javafx.scene.layout.Pane.<init>(Pane.java:136)
    at javafx.graphics/javafx.scene.layout.BorderPane.<init>(BorderPane.java:219)
    at converter.View.<init>(View.java:52)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
    at converter.View.<init>(View.java:12)
Exception running application converter.Main

共有2个答案

贺博厚
2023-03-14

您正在这里创建一个循环依赖项。控制器依赖于视图依赖于控制器。

这是糟糕的设计。

您应该实现控制的反转,并依赖于抽象。抽象要么是在视图的构建中给出的,要么是有html" target="_blank">工厂或某个依赖注入框架。另一个选择是让你的视图发出控制器订阅的特定视图事件。有很多方法可以正确解决这个问题——循环依赖不是其中之一。

抽象应该是尽可能最小的完整抽象(接口隔离原则),所以如果你需要一个处理程序,你可以有一个带有句柄(事件)函数的接口,仅此而已。

这并不禁止控制器实现句柄功能,它可能违反单一责任原则,但这将取决于控制器的问题和实现。

请参见此处的实体原则:维基百科实体

更新:您就快到了,只需添加setEventHandler作为视图属性。

public void setEventHandler(@NotNull EventHandler handler) {
    this.handler = handler;
    updateHandler();
}

private void updateHandler() {
 //Add TextField ActionEvent
 input.setOnAction(handler::handle);
 input2.setOnAction(handler::handle);
}

并将其添加到您的Main::init中:

Controller controller = new Controller(model , view);
view.setEventHandler(controller);

还必须修复控制器的默认构造函数。

白和泽
2023-03-14

出现异常是因为在构造视图实例时存在无限递归:

public class View {

    // ...

    private Controller control = new Controller(new Model(), new View());

    // ...
}

当你创建一个视图实例时,你会尝试创建一个新的视图实例(传递给控制器构造函数),这反过来会创建一个新的视图实例,传递给控制器构造函数,等等。即使你消除了这里的依赖关系,你也希望这些对象引用同一个实例;你不想到处创建新的实例。

MVC有几种不同的变体。看起来您正在尝试实现一个“传统”的MVC,其中:

  • 视图观察模型,并在模型发生变化时更新
  • View将其封装的组件(例如文本字段)上的用户操作委托给Controller
  • 控制器更新模型

所以:

  • 视图应同时参考控制器和模型

我还建议不要让控制器实现任何EventHandler接口;只需定义处理用户输入所需的方法。为每个用户操作定义单独的方法,而不是使用一个带有无休止的开关if-else语句的单一handle()方法。

比如:

public class View { 

    private final Controller controller ;
    private final Model model ;

    private final TextField input ;
    private final TextField input2 ;

    // ...

    public View(Model model, Controller controller) {
        this.model = model ;
        this.controller = controller ;

        input = new TextField();
        input2 = new TextField();

        model.someProperty().addListener((obs, oldValue, newValue) -> {
            /* update controls */
        });

        model.someOtherProperty().addListener((obs, oldValue, newValue) -> {
            /* update controls */
        });

        input.setOnAction(event -> controller.handleInput(input.getText()));
        input2.setOnAction(event -> controller.handleInput2(input2.getText()));

        // layout etc

    }
}
public class Controller {

    public final Model model ;

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

    public void handleInput(String input) {
        model.setSomeValue(input); 
    }

    public void handleInput2(String input) {
        model.setSomeOtherValue(input2);
    }

    // etc
}

然后用下面的代码来组装它

Model model = new Model();
Controller controller = new Controller(model);
View view = new View(model, controller);

有关完整的示例,请参阅在不使用FXML的情况下使用JavaFX控制器。

 类似资料:
  • 问题内容: 在PHP系统中,我有一个常见的MVC情况:从包含的数据中接收请求。现在,我有三种方法来处理数据: a)仅调用和处理数据。 b)在变换的数据转换成变量,并将它们传递到。 c)将数据转换为的域对象,并将该对象仅传递给。 目前,我正在使用选项A,但是我认为这是错误的,因此我正在考虑使用选项C。 因此,根据MVC,处理数据的正确方法是什么? 编辑 目前,我没有使用任何MVC框架。 编辑2 通常

  • 问题内容: 我的代码如下: 如果在获取之前设置标题,可以吗?我的标头会被发送,还是服务器会看到默认的用户代理(如果有)? 问题答案: 必须 先设置标头,然后才能产生任何影响- 如果连接已经打开,则将引发。 就头而言,如果已设置,则应将其发送。 请参阅URLConnection JavaDoc。

  • 问题内容: 我一直在使用spring-webflux进行一些研究,我想了解使用路由器功能处理错误的正确方法。 我创建了一个小项目来测试几个场景,并且我希望获得有关它的反馈,并查看其他人在做什么。 到目前为止,我在做什么。 提供以下路由功能: 我已经在我的处理程序上执行了 它们是我的错误处理程序: 这是完整的示例存储库: https://github.com/LearningByExample/re

  • 问题内容: 我使用10gen的本机node.js驱动器将mongodb(2.2.2)与node.js一起使用。 起初一切顺利。但是当涉及到并发基准测试部分时,会发生很多错误。频繁进行1000次并发连接/关闭可能会导致mongodb拒绝任何进一步的请求,并出现以下错误: 另外,如果许多客户端在没有显式关闭的情况下关闭,则mongodb将花费几分钟的时间来检测并关闭它们。这也将导致类似的连接问题。(使

  • 我试试看。js与mongodb(2.2.2)一起使用本机节点。js drive by 10gen。 起初一切都很顺利。但在并发基准测试部分,出现了很多错误。频繁连接/关闭1000次并发可能会导致mongodb拒绝任何进一步的请求,错误如下: 此外,如果很多客户端在没有显式关闭的情况下关闭,mongodb需要几分钟来检测并关闭它们。这也会导致类似的连接问题。(使用/var/log/mongodb/m

  • 问题内容: 我在Django 1.6上无法正常使用翻译存在问题。我已将此添加到我的settings.py中: 还添加了中间件: 以及每当我使用应为l10nd的字符串时的* .py文件: 我的模板开始于: 在模板内部,我使用了trans占位符。例如 我在locale / de / LC_MESSAGES / django.po中提供了翻译: 问题答案: 添加到并将其设置如下: 请注意,它必须是一个元