Класс контроллера для динамического представления Javafx

Я новичок в Javafx и Spring и делаю простое настольное приложение для загрузки данных из база данных и отображения их в табличное представление. Я поместил весь свой код в один класс, который создает таблицы Просмотры и нагрузка и обрабатывает добавление новых строк и редактирование таблицы. Но я запутался, как разделить этот класс на части Контроллер и Вид (как шаблон 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, а затем получали объект представления, используя аннотацию @FXML. Но здесь, в моем случае, я создаю объекты представления в классе. Если я разделю этот класс на классы представления и контроллера, то как я определю действие, скажем, для кнопки сохранения, и как я получу доступ к моему табличному представлению в классе контроллера?

Любая помощь будет оценена.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
217
1

Ответы 1

Если вы создаете собственный JavaFx Node как Bean, вы можете @Autowired его использовать в контроллере, но вы должны добавить его в Node, который представляет ваш пользовательский Node.

У вас есть два пути:

  1. extends ваш класс 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);
  }

}

Контроллер должен выглядеть так:

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

Основная проблема заключается в том, что вы должны добавить свой собственный Node в контекст перед инициализацией Controller. Вы можете написать 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
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. В таком случае, как это будет работать?

Waqar Ahmed 09.03.2019 20:10

Во втором и третьем фрагментах кода вы упомянули загрузку файла fxml, но у меня его нет.

Waqar Ahmed 09.03.2019 22:13

@Waqar Ahmed посмотри на примеры

kozmo 10.03.2019 09:30

Другие вопросы по теме