java - 动态 Javafx View 的 Controller 类

标签 java spring spring-boot javafx

我是 JavafxSpring 的新手,正在制作一个简单的桌面应用程序来从 database 加载数据并将其显示在 表格 View 。我已将所有代码放在一个类中,该类创建 View 加载 表并处理添加新行和编辑表。但是我很困惑如何将这个类拆分为 ControllerView 部分(像 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);
    }

}

我看到了其他示例,但大多数都是在 fxml 文件中生成 View ,然后使用 @FXML 注释获取 View 对象。但在我的例子中,我在类中创建 View 对象。如果我在 View 类和 Controller 类中分离此类,那么我将如何定义保存按钮的操作以及如何在 Controller 类中访问我的 TableView ?

我们将不胜感激。

最佳答案

如果您创建自定义 JavaFx Node 作为 Bean,您可以将它@Autowired 到 Controller ,但您必须添加一个到 Node 表示您的自定义 Node

你有两种方式:

  1. 扩展您的Node类(或Node的继承者)(看例子)

  2. 从包含其他节点的类root-Node返回

例子:

@Component
@Scope("prototype") //If you want to reuse component `@Scope("prototype")`
public class PartsView  extends GridPane{ 
  ...
  public void init(){
    getChildren().add(table);
  }

}

Controller 应该是这样的:

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
  }
}

主要问题是您必须在Controller 初始化之前将自定义Node 添加到上下文中。可以写Configuration来解决:

@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());
    }
 }

ControlFx 类:

public class ControlFx<N extends Node ,C extends Initializable>  {
    private final N view;
    private final C controller;

    public ControlFx(N view, C controller) {
        this.view = view;
        this.controller = controller;
    }

    public N getView() {
        return view;
    }

    public C getController() {
        return controller;
    }

}

I separate this class in view and controller classes, then how I will define the action lets say for the save button and how I will access my table view in the controller class?

您可以将facade-方法写入您的自定义类:

public  void setOnActionSaveButton(EventHandler<ActionEvent> value){
    save.setOnAction((ActionEvent e) -> {
        // Some logic
    }
}

I am generating all the view with code, without any fxml file. In that case how it will work?

@Controller注解(@Component的扩展)与@PostConstruct注解一起使用,或将@Autowired与构造函数(或任何 init 方式):

@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);
   // }
}

In your 2nd and 3rd code snippet, you mentioned loading fxml file but I dont have any.

你必须首先初始化 spring-context。我使用了 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();
}

实现:

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 :

@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;
    }        
}

关于java - 动态 Javafx View 的 Controller 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55077891/

相关文章:

java - JSR-303 子类验证

spring - ISO货币 list

java - Autowiring 类方法的空指针异常

spring - 如何配置自定义 Kafka 反序列化器并使用 KafkaListener 获取使用的 JSON 数据

java - 如何在 Spring Boot 中启用 H2 数据库服务器模式

java - 用于在 AWS 参数存储中设置参数的 Java API 是什么?

java - 继承使用 Scanner 的 Java 构造函数

java - 在 Tomcat 中访问 externalize application.properties for Spring boot 应用程序?

java - 针对不同事件的 Spring Integration 自定义轮询器

java - Spring Boot 应用程序中的 Spring Data Elasticsearch