Как получить низкоуровневый доступ к диаграмме JavaFX, чтобы можно было нарисовать изображение? - PullRequest
0 голосов
/ 17 января 2020

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

public class ChartBgImageTest {
    public ChartBgImageTest() {

        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
        final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);

        BufferedImage bufferedImage = GraphicsEnvironment
                .getLocalGraphicsEnvironment()
                .getDefaultScreenDevice()
                .getDefaultConfiguration()
                .createCompatibleImage(600, 400);


        BufferedImage img = new BufferedImage(600, 400, BufferedImage.TYPE_INT_RGB);
        img.createGraphics().fillRect(0, 0, 50, 50);
        // Fill img code here

        Graphics g2d = bufferedImage.createGraphics();

        g2d.drawImage(img, 0, 0, null);

        // TODO How to draw the img to lineChart background.
        // So that any grids or lines on the chart are drawn above the img.

    }
}

1 Ответ

3 голосов
/ 17 января 2020

Диаграммы в пакете javafx.scene.chart не используют Canvas для рисования диаграмм. Вместо этого они используют граф сцены. В своем вопросе вы создаете изображение на лету, но, если у вас уже есть созданное изображение, возможно, как встроенный ресурс, вы можете использовать CSS, чтобы добавить изображение к фону диаграммы. Взглянув на раздел XYChart JavaFX CSS Справочное руководство , вы увидите, что одна из подструктур помечена chart-plot-background и является Region, что означает, что фоновое изображение можно применить к это через CSS. Например:

Приложение. java:

import java.util.Random;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    var chart = new LineChart<>(new NumberAxis(), new NumberAxis(), createChartData());
    chart.getXAxis().setLabel("X");
    chart.getYAxis().setLabel("Y");

    var scene = new Scene(chart, 1000, 650);
    scene.getStylesheets().add(getClass().getResource("/style.css").toString());

    primaryStage.setScene(scene);
    primaryStage.show();
  }

  private static ObservableList<Series<Number, Number>> createChartData() {
    var random = new Random();

    var chartData = FXCollections.<Series<Number, Number>>observableArrayList();
    for (int i = 1; i <= 3; i++) {
      var data = FXCollections.<Data<Number, Number>>observableArrayList();
      for (int j = 0; j <= 15; j++) {
        data.add(new Data<>(j, random.nextInt(250)));
      }
      chartData.add(new Series<>("Series #" + i, data));
    }
    return chartData;
  }
}

стиль. css:

.chart-plot-background {
    -fx-background-image: url(/* your URL */);
    -fx-background-size: cover;
}

Это только нарисует изображение позади фактического содержимого диаграммы (то есть данных). На оси, легенде и окружающем пространстве не будет изображения. Если вы хотите, чтобы вся диаграмма имела фоновое изображение, вы можете использовать тот факт, что Chart, от которого наследуются все реализации диаграммы, начинается с Region. Измените CSS на:

.chart {
    -fx-background-image: url(/* your URL */);
    -fx-background-size: cover;
}

.chart-plot-background,
.chart-legend {
    -fx-background-color: null;
}

Если вы хотите что-то между фоном графика и всей диаграммой, вы можете добавить изображение к .chart-content (задокументировано здесь ).


Если вам нужно оставаться в коде (например, потому что вы создаете изображение на лету), вам нужно получить ссылку на необходимый Region. Для этого вы можете использовать Node#lookup(String). Имейте в виду, однако, что вам может потребоваться подождать, пока диаграмма не будет отображена на экране, прежде чем вызывать lookup, поскольку возможно, что нужный узел-потомок не был создан и добавлен в граф сцены заранее (это особенно верно для элементов управления ).

import java.util.Random;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.Region;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    var chart = new LineChart<>(new NumberAxis(), new NumberAxis(), createChartData());
    chart.setTitle("Example Chart");
    chart.getXAxis().setLabel("X");
    chart.getYAxis().setLabel("Y");

    var scene = new Scene(chart, 1000, 650);

    primaryStage.setScene(scene);
    primaryStage.show();

    var plotBackground = (Region) chart.lookup(".chart-plot-background");
    plotBackground.setBackground(
        new Background(
            new BackgroundImage(
                new Image(/* your URL */),
                null,
                null,
                BackgroundPosition.CENTER,
                new BackgroundSize(0, 0, false, false, true, false))));
  }

  private static ObservableList<Series<Number, Number>> createChartData() {
    var random = new Random();

    var chartData = FXCollections.<Series<Number, Number>>observableArrayList();
    for (int i = 1; i <= 3; i++) {
      var data = FXCollections.<Data<Number, Number>>observableArrayList();
      for (int j = 0; j <= 15; j++) {
        data.add(new Data<>(j, random.nextInt(250)));
      }
      chartData.add(new Series<>("Series #" + i, data));
    }
    return chartData;
  }
}

Обратите внимание, что вы не можете использовать BufferedImage напрямую с API JavaFX. Если вам действительно нужно нарисовать код Image, у вас есть несколько вариантов:

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