用于动态Javafx视图的控制器类

用于动态Javafx视图的控制器类,java,spring,spring-boot,javafx,Java,Spring,Spring Boot,Javafx,我不熟悉Javafx和Spring,并制作了一个简单的桌面应用程序,从数据库中加载数据,并将其显示在表视图中。我将所有代码放在一个类中,该类创建视图和加载表,并处理添加新行和编辑表。但是我不知道如何将这个类划分为控制器和视图部分(类似MVC模式)。我的类如下所示: package com.waq; // some imports @Component public class PartsView{ private TableView table; TextField des

我不熟悉JavafxSpring,并制作了一个简单的桌面应用程序,从数据库中加载数据,并将其显示在表视图中。我将所有代码放在一个类中,该类创建视图加载表,并处理添加新行和编辑表。但是我不知道如何将这个类划分为控制器视图部分(类似MVC模式)。我的如下所示:

package com.waq;

// some imports

@Component
public class PartsView{

    private TableView table;
    TextField desc;
    Label title;
    Button save;
    GridPane grid;

    @Autowired
    private PartService partService;

    public PartsView(){
        createTable();
        grid = new GridPane();
        save = new Button();
        save.setText("Save");
        save.setOnAction((ActionEvent e) -> {
            // Some logic
        }
    }


    private void loadData() {
        List<Part> parts = partService.findAll();
        if(parts.size() > 0) {
            ObservableList<Part> observableArrayList = FXCollections.observableArrayList(parts);
            table.setItems( observableArrayList);
        }
        else
            table.setPlaceholder(new Label("No data to show."));
    }

    private void saveData(){
        // save record.
    }


    private void createTable(){
        CustomTableColumn<Part,String> idColumn = new CustomTableColumn<Part,String>("id");
        idColumn.setPercentWidth(10);
        idColumn.setCellValueFactory(new PropertyValueFactory<Part,String>("id"));

        CustomTableColumn<Part,String> descCol = new CustomTableColumn<Part,String>("description");
        descCol.setPercentWidth(50);
        descCol.setCellValueFactory(new PropertyValueFactory<Part,String>("description"));

        table.getColumns().addAll(idColumn,descCol);

        grid.getChildren().add(table);
    }

}
package com.waq;
//一些进口商品
@组成部分
公共类PartsView{
私有表视图表;
TextField desc;
标签名称;
按钮保存;
网格窗格网格;
@自动连线
私人部分服务;
公共部分视图(){
createTable();
grid=新的GridPane();
保存=新建按钮();
save.setText(“save”);
save.setOnAction((ActionEvent e)->{
//一些逻辑
}
}
私有void loadData(){
List parts=partService.findAll();
如果(parts.size()>0){
ObservableList observableArrayList=FXCollections.observableArrayList(部件);
表.集合项目(可观察的列表);
}
其他的
table.setPlaceholder(新标签(“无数据显示”);
}
私有void saveData(){
//保存记录。
}
私有void createTable(){
CustomTableColumn idColumn=新的CustomTableColumn(“id”);
idColumn.setPercentWidth(10);
idColumn.setCellValueFactory(新属性ValueFactory(“id”);
CustomTableColumn descCol=新的CustomTableColumn(“说明”);
描述设置百分比宽度(50);
描述setCellValueFactory(新属性ValueFactory(“说明”);
table.getColumns().addAll(idColumn,descCol);
grid.getChildren().add(表);
}
}
我看到了其他示例,但大多数示例都是在fxml文件中生成视图,然后使用@fxml注释获取视图对象。但在我的示例中,我在类中创建视图对象。如果我在视图类和控制器类中分离该类,那么我将如何定义该操作,例如save按钮和如何在controller类中访问表视图


任何帮助都将不胜感激。

如果您将自定义JavaFx
节点
创建为
Bean
,您可以
@Autowired
将其添加到控制器,但您必须将一个添加到
节点
,它代表您的自定义
节点

你有两种方法:

  • 扩展了
    节点
    的类(或
    节点
    的继承人)(查看示例)

  • 从包含其他节点的类
    根节点返回

  • 例如:

    @Component
    @Scope("prototype") //If you want to reuse component `@Scope("prototype")`
    public class PartsView  extends GridPane{ 
      ...
      public void init(){
        getChildren().add(table);
      }
    
    }
    
    控制器应如下所示:

    public class TestController implements Initializable  {
    
      @FXML //StackPane as example
      private StackPane stackPane; //injecting before constructor, when you load .fxml
      @Autowired
      private PartsView partsView; //injecting when you load spring-context
    
    
      @Override
      public void initialize(URL location, ResourceBundle resources) {
        // initialize fields marked @FXML annotation.
        // method initialize triggering after constructor
      }
    }
    
    问题在于,在初始化
    控制器
    之前,您必须将自定义
    节点
    添加到上下文中。您可以编写
    配置
    来解决此问题:

    @Configuration
    @ComponentScan({
        "you.node.packege",
     })
    public class TestConfig{
    
    @Lazy                                  //depends on your case
    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE) //depends on your case
    public ControlFx<TabPane, PlanningController> testViewFx() throws IOException {
        return loadView("/view/TestLayout.fxml");
    }
    
    
        private <V extends Node, C extends Initializable> ControlFx<V,C> loadView(String url) throws IOException {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource(url));
            return  new ViewFx<>(loader.load(), loader.getController());
        }
     }
    

    我用代码生成所有视图,没有任何fxml文件。在这种情况下,它将如何工作

    使用
    @Controller
    注释(扩展
    @Component
    )和
    @PostConstruct
    注释,或使用
    @Autowired
    和构造函数(或任何
    init
    方式):


    在第2和第3段代码中,您提到加载fxml文件,但我没有

    首先必须初始化spring上下文。我使用了
    AnnotationConfigApplicationContext

    public abstract class AbstractJavaFxApplication extends Application {
    
        private static String[] savedArgs;
        protected AnnotationConfigApplicationContext context;
    
        @Override
        public void init() {
            context =  new AnnotationConfigApplicationContext(configurations());
            context.getAutowireCapableBeanFactory().autowireBean(this);
    
        }
    
        @Override
        public void stop() throws Exception {
            super.stop();
            context.close();
        }
    
        protected static void launchApp(Class<? extends AbstractJavaFxApplication> clazz, String[] args) {
            AbstractJavaFxApplication.savedArgs = args;
            Application.launch(clazz, savedArgs);
        }
    
        protected abstract Class<?>[] configurations();
    }
    
    控制器:

    @Controller
    public class RootLayoutController {
    
        private StackPane stackPane;
        @Autowired
        private PartsView partsView;
    
        @PostConstruct
        private void init(){
            stackPane = new StackPane();
            stackPane.getChildren().add(partsView);
        }
    
        public Pane getPane(){
            return stackPane;
        }        
    }
    

    我用代码生成所有视图,没有任何fxml文件。在这种情况下,它将如何工作?在您的第2和第3个代码段中,您提到加载fxml文件,但我没有任何文件。@Waqar Ahmed请看示例
    @Controller
    public class TestController {
    
       private StackPane stackPane;
       // @Autowired
       private PartsView  partsView;
    
       @Autowired
       public TestController (PartsView  partsView){
          ...
          stackPane.getChildren().add(partsView)
       }
    
       // @PostConstruct
       // private void beanPostConstruct() {
       //     stackPane.getChildren().add(partsView);
       // }
    }
    
    public abstract class AbstractJavaFxApplication extends Application {
    
        private static String[] savedArgs;
        protected AnnotationConfigApplicationContext context;
    
        @Override
        public void init() {
            context =  new AnnotationConfigApplicationContext(configurations());
            context.getAutowireCapableBeanFactory().autowireBean(this);
    
        }
    
        @Override
        public void stop() throws Exception {
            super.stop();
            context.close();
        }
    
        protected static void launchApp(Class<? extends AbstractJavaFxApplication> clazz, String[] args) {
            AbstractJavaFxApplication.savedArgs = args;
            Application.launch(clazz, savedArgs);
        }
    
        protected abstract Class<?>[] configurations();
    }
    
    public class ReportApp extends AbstractJavaFxApplication {
    
        public final double PRIMARY_WINDOW_HEIGHT = 500;
        public final double PRIMARY_WINDOW_WIDTH = 500;
    
        @Value("${ui.title:JavaFX приложение}")
        private String windowTitle;
    
        @Autowired
        private RootLayoutController controller;
    
        @Override
        public void start(Stage stage) throws Exception {
            stage.setTitle(windowTitle);
            stage.setScene(
                    new Scene(
                            controller.getPane(),
                            PRIMARY_WINDOW_WIDTH,
                            PRIMARY_WINDOW_HEIGHT
                    )
            );
            stage.setResizable(true);
            stage.centerOnScreen();
            stage.show();
        }
    
        public static void main(String[] args) {
            launchApp(ReportApp.class, args);
        }
    
        @Override
        protected Class<?>[] configurations() {
            return new Class<?>[]{
                Config.class
            };
        }
    }
    
    @Configuration
    @ComponentScan({"components", "controllers"})
    public class Config {
    
        @Bean
        ...
    }
    
    @Controller
    public class RootLayoutController {
    
        private StackPane stackPane;
        @Autowired
        private PartsView partsView;
    
        @PostConstruct
        private void init(){
            stackPane = new StackPane();
            stackPane.getChildren().add(partsView);
        }
    
        public Pane getPane(){
            return stackPane;
        }        
    }