Статьи

Как встроить диаграмму JavaFX в сцену визуальной библиотеки

JavaFX действительно великолепен в качестве улучшающей технологии для существующих приложений на Java / Swing и основ кода. Разработчик Java, который понимает Swing, но только начинает изучать JavaFX, может легко обновить его или ее программное обеспечение на основе компонентов. Например, допустим, у нас есть приложение на платформе NetBeans со сценой Visual Library, которая является платформой Swing, и мы хотим добавить диаграмму JavaFX в сцену. Что-то вроде этого:

Это проще, чем кажется, и стало возможным благодаря стандартному шаблону JavaFX / Swing Interop. Поэтому в этой статье будет показано, как создать компонент Swing Interop, который можно использовать для достижения этой цели. Затем он покажет, как взять этот новый компонент взаимодействия и фактически внедрить его в сцену Visual Library.

Сначала вам нужно будет немного подготовиться к настройке приложения для этого урока. Если у вас еще нет приложения NetBeans Platform со сценой Visual Library, сделайте следующее:

  1. Создать приложение. Создайте новое приложение платформы NetBeans и добавьте следующие модули на вкладке «Библиотеки» в свойствах проекта:
  • API настроек
  • UI Utilities API
  • Утилиты API
  • API визуальной библиотеки
  • API оконной системы
  • API поиска
  • Включите JavaFX Runtime. Создайте модуль-оболочку библиотеки NetBeans, включающий «jfxrt.jar», и установите зависимость от него в модуле, описанном выше.
  • Добавьте новый класс в пакет модуля, который расширяет тип JFXPanel. Мы будем писать наш специальный код JavaFX в этом классе. JFXPanel — это встраиваемый компонент, который в основном рассматривается как традиционный JPanel. JFXPanel — это класс с возможностью взаимного взаимодействия, который «JavaFX-осведомлен». Мы используем шаблон JFXPanel Platform.runLater, чтобы компоненты JavaFX могли отображаться в потоке времени выполнения JavaFX.
    Ниже приведен этот шаблон конструктора:
    public class LineChartJFXPanel extends JFXPanel {
        private LineChart chart;
        private NumberAxis xAxis;
        private NumberAxis yAxis;    
        private ObservableList<XYChart.Series<Double,Double>> lineChartData;
        
        public LineChartJFXPanel() {
            super();
            // create JavaFX scene
            Platform.setImplicitExit(false);
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    createScene();
                }
            });  
        }
        private void createScene() {
            Group root = new Group();
    
            //xAxis = new NumberAxis("Values for X-Axis", 0, 3, 1);
            //yAxis = new NumberAxis("Values for Y-Axis", 0, 3, 1);
            xAxis = new NumberAxis();
            xAxis.setLabel("X-Axis");
            yAxis = new NumberAxis();
            yAxis.setLabel("Y-Axis");
    
            Integer theSize = 200;
            
            lineChartData = FXCollections.observableArrayList(
                new LineChart.Series<>("Series 1", FXCollections.observableArrayList(
                    new XYChart.Data<>(0.0, 0.0)
                )));
            chart = new LineChart(xAxis, yAxis, lineChartData);
            root.getChildren().add(chart);     
    
            Double [] xDoubleArray = new Double[theSize];
            Double [] yDoubleArray = new Double[theSize];
    
            for(int i = 0;i < theSize;i++) {
                Double x = new Double(java.lang.Math.log(i));
                if(x.isInfinite() || x.isNaN())
                    x = 0.0;
                xDoubleArray[i] =  x;
                yDoubleArray[i] =  new Double(java.lang.Math.random()*i);
            }
            updateXY("log(1:1000) / random(1:1000)",xDoubleArray, yDoubleArray); 
            xAxis.setLabel("log(1:1000)");
            yAxis.setLabel("random(1:1000)");        
            
            
            this.setScene(new Scene(root));
            this.validate();
        }     
        public void updateXY(final String seriesTitle, final Double [] xArray, final Double [] yArray) {
            Platform.setImplicitExit(false);
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    //convert double arrays to a typed list
                    List <XYChart.Data> xyChartDataList = new ArrayList<>();
                    for(int i=0;i<xArray.length;i++) {
                        xyChartDataList.add(new XYChart.Data<>(xArray[i],yArray[i]));
                    }
                    //Wrap typed list in ObservableList wrapper collection
                    ObservableList<XYChart.Data> observableList = FXCollections.observableList(xyChartDataList);    
                    //add new ObservableList collection to chart
                    lineChartData.setAll( new LineChart.Series(seriesTitle,observableList));
                }
            }); 
        }  
    }

    Заметьте, что для обновления данных диаграммы был создан удобный метод «updateXY ()»? Это простой пример предоставления способа обновления диаграммы. Обратите внимание, что он также следует шаблону Platform.runLater (). Это достаточно хорошо для учебника, но чтобы действительно быть полезным, вам нужно улучшить интерфейс, чтобы включить методы, которые создают и удаляют ряды данных. Кроме того, вы определенно захотите использовать эти методы как часть зарегистрированного механизма Service или Lookup внутри платформы NetBeans. Эти понятия немного выходят за рамки данного руководства и требуют отдельного обсуждения. На данный момент у вас есть новый класс компонентов JavaFX, который можно поместить в любое существующее дерево компонентов Swing.

  • Создание TopComponent с помощью Visual Library Scene: используйте мастер New Window для создания TopComponent. Этот TopComponent будет иметь виджет Scene и Node. Внутри виджета Node мы добавим экземпляр нашего нового класса LineChartJFXPanel. Ни TopComponent, ни сцена Visual Library не будут знать или заботиться о JavaFX.

    Ниже приведен код установки в конструкторе TopComponent:

    public final class JavaFXWidgetTestTopComponent extends TopComponent {
    
        private VMDGraphScene scene;
        private JScrollPane scrollPane = new JScrollPane(); 
        public LineChartJFXPanel lineChartPanel;
        
        public JavaFXWidgetTestTopComponent() {
            initComponents();
            setName(Bundle.CTL_JavaFXWidgetTestTopComponent());
            setToolTipText(Bundle.HINT_JavaFXWidgetTestTopComponent());
            setLayout(new BorderLayout());
            add(scrollPane,BorderLayout.CENTER);
            //Setup visual library scene
            scene = new VMDGraphScene();
            scrollPane.setViewportView(scene.createView());  
            //Initialize and add JavaFX chart widget
            //Line chart
            lineChartPanel = new LineChartJFXPanel();
            //create our new node in the scene 
            VMDNodeWidget widget = (VMDNodeWidget) addNode ("JavaFX Chart");
            //Add our JavaFX panel to a ComponentWidget which is adept at embedding Swing components
            ComponentWidget componentWidget = new ComponentWidget(scene, lineChartPanel);
            //The JavaFX chart component is now a child of this node and can be found as such
            widget.addChild(componentWidget); 
    
            scene.getSceneAnimator().animatePreferredLocation(widget, getMousePosition(true));
            scene.validate(); //Make sure you validate the scene so that everything renders nicely
        }

    Остальная часть автоматически сгенерированного кода TopComponent должна быть в порядке. Я решил использовать классы VMD Visual Library (VMDGraphScene и VMDNodeWidget), потому что они предоставляют готовые складные узлы, и я хотел посмотреть, как ведет себя рендеринг JavaFX. Довольно неплохо, хотя иногда VMDNodeWidget требует, чтобы пользователь выдавал комбо «Свернуть / развернуть» для рендеринга в первый раз. Это может быть связано с вычислением количества чисел в сочетании с тем, что конструктор класса LineChart рендерится в отдельном потоке от Swing.
     

  • На этом этапе вы можете четко использовать этот подход, создать набор компонентов JavaFX и поместить их не только в сцены Visual Library, но и в любой TopComponent платформы Netbeans. Ниже приведен скриншот этой модели, расширенный до нескольких диаграмм, интегрированных с инструментом палитры:

    Этот скриншот был также замечен в статье в блоге Geertjan .

    Это пример того, как JavaFX можно использовать для улучшения и без того великолепной платформы NetBeans на основе Swing. Я думаю, что в будущем те, кто заинтересован в демонстрации или использовании JavaFX в своих приложениях Swing и NetBeans Platform, должны вначале глубоко взглянуть на JavaFX и то, что он делает лучше, чем Swing. В Платформе NetBeans существует множество вариантов использования, таких как приведенные выше, где это можно использовать.