JavaFX и Spring - повторно используемый компонент FXML - PullRequest
0 голосов
/ 07 июля 2019

Я создаю графический интерфейс с помощью Scene Builder в JavaFX и Spring Boot. У некоторых моих сцен есть один общий элемент - простой индикатор KPI, называемый PerformanceBeacon. В зависимости от конфигурации, выполненной классом контроллера, маяк может использоваться, например. для параметров проекта, таких как бюджет, время и так далее. В идеале даже одна сцена может иметь несколько таких элементов.

Проблема в том, что наличие нескольких маяков KPI в одной сцене не работает, поскольку все встроенные контроллеры указывают на одну и ту же память, и, следовательно, я могу манипулировать только одним - последним - маяком KPI.

Фрагмент из файла fxml выглядит следующим образом:

...
<VBox layoutX="301.0" layoutY="325.0" spacing="6.0" AnchorPane.leftAnchor="2.0" AnchorPane.topAnchor="24.0">
      <children>
               <fx:include source="PerformanceBeacon.fxml" fx:id="embeddedTimeKPI" />
               <fx:include source="PerformanceBeacon.fxml" fx:id="embeddedBudgetKPI"/>
               <fx:include source="PerformanceBeacon.fxml" fx:id="embeddedResourcesKPI"/>
               <fx:include source="PerformanceBeacon.fxml" fx:id="embeddedScopeKPI"/>
               <fx:include source="PerformanceBeacon.fxml" fx:id="embeddedEffortKPI"/>
               <fx:include source="PerformanceBeacon.fxml" fx:id="embeddedQualityKPI"/>
      </children>
</VBox>
...

Встроенные контроллеры определены в классе контроллеров окружающего пользовательского интерфейса, и код должен «только» устанавливать метку каждой из гиперссылок в каждом PerformanceBeacon.

@Controller
public class ProductManagerCtrl implements Initializable {
 ...
 @FXML protected PerformanceBeaconCtrl embeddedTimeKPIController;       
 @FXML protected PerformanceBeaconCtrl embeddedBudgetKPIController; 
 @FXML protected PerformanceBeaconCtrl embeddedResourcesKPIController;  
 @FXML protected PerformanceBeaconCtrl embeddedScopeKPIController;      
 @FXML protected PerformanceBeaconCtrl embeddedEffortKPIController;     
 @FXML protected PerformanceBeaconCtrl embeddedQualityKPIController;

 ...

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {

        ...
        initializePerformanceIndicator();
        ...
     }

    protected void initializePerformanceIndicator() {
        embeddedBudgetKPIController.setHyperlink("Budget", null);
        embeddedScopeKPIController.setHyperlink("Scope", null);
        embeddedTimeKPIController.setHyperlink("Time", null);
        embeddedQualityKPIController.setHyperlink("Quality", null);
        embeddedResourcesKPIController.setHyperlink("Resources", null);
        embeddedEffortKPIController.setHyperlink("Effort estimates", null);
    }

Когда я отлаживаю код, каждый из этих встроенных контроллеров указывает на одну и ту же память. Таким образом, у меня есть один контроллер, и я могу манипулировать только последним элементом. В конце последний маяк имеет метку «Оценки усилий», а все остальные остаются без изменений.

PerformanceBeacon.fxml довольно прост. Я копирую его, если вы хотите попробовать себя

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

<?import java.lang.Double?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.shape.Polygon?>

<GridPane styleClass="pane" stylesheets="@../stylesheets/PerformanceBeacon.css" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.agiletunes.productmanager.controllers.PerformanceBeaconCtrl">
   <children>
      <Circle fx:id="beacon" fill="#1fff4d" radius="10.0" stroke="BLACK" strokeType="INSIDE" GridPane.columnIndex="1" GridPane.valignment="CENTER">
         <GridPane.margin>
            <Insets bottom="12.0" left="12.0" right="6.0" top="12.0" />
         </GridPane.margin>
      </Circle>
      <Polygon fx:id="trendArrow" fill="#11ff1c" rotate="45.0" stroke="#2e229a" strokeType="INSIDE" GridPane.columnIndex="2" GridPane.valignment="CENTER">
        <points>
          <Double fx:value="-10.0" />
          <Double fx:value="10.0" />
          <Double fx:value="-5.0" />
          <Double fx:value="10.0" />
          <Double fx:value="-5.0" />
          <Double fx:value="15.0" />
          <Double fx:value="5.0" />
          <Double fx:value="15.0" />
          <Double fx:value="5.0" />
          <Double fx:value="10.0" />
          <Double fx:value="10.0" />
          <Double fx:value="10.0" />
          <Double fx:value="0.0" />
          <Double fx:value="-10.0" />
        </points>
         <GridPane.margin>
            <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
         </GridPane.margin>
      </Polygon>
      <Hyperlink fx:id="hyperlink" onAction="#showDetails" text="Subject">
         <GridPane.margin>
            <Insets left="24.0" />
         </GridPane.margin>
      </Hyperlink>
   </children>
   <columnConstraints>
      <ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="170.0" />
      <ColumnConstraints minWidth="10.0" />
      <ColumnConstraints minWidth="10.0" />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
   </rowConstraints>
</GridPane>

И последний, но не менее важный, контроллер PerformanceBeacon

@Controller
public class PerformanceBeaconCtrl {

    @FXML   protected Circle beacon;
    @FXML   private Polygon trendArrow;
    @FXML   private Hyperlink hyperlink;

    @FXML
    public void showDetails(ActionEvent event) {
        // TODO
    }

    public void setHyperlink(String label, URL url) {
        Platform.runLater(() -> {
            hyperlink.setText(label);
        });
    }

    public void setBeaconColor(Color color) {
        Platform.runLater(() -> {
            beacon.setFill(color);
        });
    }

    public void setTrend(Double angle) {
        Platform.runLater(() -> {
            trendArrow.rotateProperty().set(angle);
        });
    }

}

Я успешно использую встроенные элементы / контроллер пользовательского интерфейса и в других местах моего кода. Но никогда не один и тот же элемент несколько раз.

Я не уверен, является ли это побочным эффектом Spring?

Что я делаю не так? Заранее спасибо.

1 Ответ

0 голосов
/ 11 июля 2019

Описанное наблюдение позволяет предположить, что проблема была связана с единственным элементом где-то в проекте.Мое подозрение относительно влияния весны было правильным.Класс контроллера ProductManagerCtrl создается с использованием

fxmlLoader.setControllerFactory(c -> ProdMgrApp.springContext.getBean(c));

, и, как оказалось, этот подход создает синглтоны по умолчанию.Поэтому все встроенные контроллеры также являются синглетонами.Способ исправить это - аннотировать класс контроллера PerformanceBeaconCtrl также с помощью

@Scope("prototype")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...