在图表上添加文本字段
Add TextField on Chart
我在 Swing
中使用 JavaFX
所以我有方法:
private Scene createScene() {
Group root = new Group();
Scene scene = new Scene(root);
BorderPane borderPane = new BorderPane();
...
//adding data series to Linechart baseChart
...
StackChart chart = new StackChart(baseChart); //returns StackPane with LineChart-s on it
borderPane.setCenter(chart);
root.getChildren().add(borderPane);
return scene;
}
如何在 Linechart
上放置可编辑的文本,如 here?
我尝试在ChartStack中添加到LineChart
(chartBackground是StackPane
中的第一个LineChart
):
chartBackground.setOnMouseClicked(event -> {
if (event.getTarget() == chartBackground) {
chartBackground.add(
new EditableDraggableText(event.getX(), event.getY())
);
super.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
}
});
但它绘制的文本不是在 LineChart
上而是在框架的左侧
所有 MCVE 是下一个
主要 Class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.function.Function;
public class GrinMain extends Application {
public static final int X_NUMBER = 800;
public double[] x;
public double[] y;
@Override
public void start(Stage primaryStage) throws Exception {
NumberAxis xAxis = new NumberAxis(0, X_NUMBER, 200);
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Series 1");
LineChart baseChart = new LineChart(xAxis, yAxis);
baseChart.getData().add(prepareSeries("Serie 1", (x) -> (double) x));
StackChart chart = new StackChart(baseChart, Color.RED);
chart.addSeries(prepareSeries("Serie 3", (x) -> (double) -2*x * x), Color.BLUE);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(chart);
borderPane.setBottom(chart.getLegend());
Scene scene = new Scene(borderPane, 1024, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private XYChart.Series<Number, Number> prepareSeries(String name, Function<Integer, Double> function) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.setName(name);
for (int i = 0; i < X_NUMBER; i++) {
series.getData().add(new XYChart.Data<>(i, function.apply(i)));
}
return series;
}
private XYChart.Series<Number, Number> prepareSeries(String name, double[] xValues, double[] yValues) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.setName(name);
for (int i = 0; i < yValues.length; i++){
series.getData().add(new XYChart.Data(xValues[i], yValues[i]));
}
return series;
}
public static void main(String[] args) {
launch(args);
}
}
堆栈图 class:
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
public class StackChart extends StackPane {
private final LineChart baseChart;
private final ObservableList<LineChart> backCharts = FXCollections.observableArrayList();
private final double yAxisWidth = 60;
private final AnchorPane details;
private final double yAxisSeparation = 20;
private double strokeWidth = 0.3;
public StackChart(LineChart baseChart, Color lineColor) {
this(baseChart, lineColor, null);
}
public StackChart(LineChart baseChart, Color lineColor, Double strokeWidth) {
if (strokeWidth != null) {
this.strokeWidth = strokeWidth;
}
this.baseChart = baseChart;
baseChart.setCreateSymbols(false);
baseChart.setLegendVisible(false);
baseChart.getXAxis().setAutoRanging(false);
baseChart.getXAxis().setAnimated(false);
baseChart.getXAxis().setStyle("-fx-font-size:" + 18);
baseChart.getYAxis().setAnimated(false);
baseChart.getYAxis().setStyle("-fx-font-size:" + 18);
setStyle(baseChart, lineColor);
setFixedAxisWidth(baseChart);
setAlignment(Pos.CENTER_LEFT);
backCharts.addListener((Observable observable) -> rebuildChart());
details = new AnchorPane();
bindMouseEvents(baseChart, this.strokeWidth);
rebuildChart();
}
private void bindMouseEvents(LineChart baseChart, Double strokeWidth) {
getChildren().add(details);
details.prefHeightProperty().bind(heightProperty());
details.prefWidthProperty().bind(widthProperty());
details.setMouseTransparent(true);
setOnMouseMoved(null);
setMouseTransparent(false);
final Axis xAxis = baseChart.getXAxis();
final Axis yAxis = baseChart.getYAxis();
final Line xLine = new Line();
final Line yLine = new Line();
yLine.setFill(Color.GRAY);
xLine.setFill(Color.GRAY);
yLine.setStrokeWidth(strokeWidth/2);
xLine.setStrokeWidth(strokeWidth/2);
xLine.setVisible(false);
yLine.setVisible(false);
final Node chartBackground = baseChart.lookup(".chart-plot-background");
for (Node n: chartBackground.getParent().getChildrenUnmodifiable()) {
if (n != chartBackground && n != xAxis && n != yAxis) {
n.setMouseTransparent(true);
}
}
chartBackground.setCursor(Cursor.CROSSHAIR);
chartBackground.setOnMouseEntered((event) -> {
chartBackground.getOnMouseMoved().handle(event);
xLine.setVisible(true);
yLine.setVisible(true);
details.getChildren().addAll(xLine, yLine);
});
chartBackground.setOnMouseExited((event) -> {
xLine.setVisible(false);
yLine.setVisible(false);
details.getChildren().removeAll(xLine, yLine);
});
chartBackground.setOnMouseMoved(event -> {
double x = event.getX() + chartBackground.getLayoutX();
double y = event.getY() + chartBackground.getLayoutY();
xLine.setStartX(65);
xLine.setEndX(details.getWidth()-10);
xLine.setStartY(y+5);
xLine.setEndY(y+5);
yLine.setStartX(x+5);
yLine.setEndX(x+5);
yLine.setStartY(12);
yLine.setEndY(details.getHeight()-28);
});
chartBackground.setOnMouseClicked(event -> {
if (event.getTarget() == chartBackground) {
super.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
}
});
}
private void setFixedAxisWidth(LineChart chart) {
chart.getYAxis().setPrefWidth(yAxisWidth);
chart.getYAxis().setMaxWidth(yAxisWidth);
}
private void rebuildChart() {
getChildren().clear();
getChildren().add(resizeBaseChart(baseChart));
for (LineChart lineChart : backCharts) {
getChildren().add(resizeBackgroundChart(lineChart));
}
getChildren().add(details);
}
private Node resizeBaseChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
return lineChart;
}
private Node resizeBackgroundChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
hBox.setMouseTransparent(true);
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.translateXProperty().bind(baseChart.getYAxis().widthProperty());
lineChart.getYAxis().setTranslateX((yAxisWidth + yAxisSeparation) * backCharts.indexOf(lineChart));
return hBox;
}
public void addSeries(XYChart.Series series, Color lineColor) {
NumberAxis yAxis = new NumberAxis();
NumberAxis xAxis = new NumberAxis();
// xAxis
xAxis.setAutoRanging(false);
xAxis.setVisible(false);
xAxis.setOpacity(0.0);
xAxis.lowerBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).lowerBoundProperty());
xAxis.upperBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).upperBoundProperty());
xAxis.tickUnitProperty().bind(((NumberAxis) baseChart.getXAxis()).tickUnitProperty());
// yAxis
yAxis.setSide(Side.RIGHT);
yAxis.setLabel(series.getName());
// create chart
LineChart lineChart = new LineChart(xAxis, yAxis);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
lineChart.getData().add(series);
styleBackChart(lineChart, lineColor);
setFixedAxisWidth(lineChart);
backCharts.add(lineChart);
}
private void styleBackChart(LineChart lineChart, Color lineColor) {
setStyle(lineChart, lineColor);
Node contentBackground = lineChart.lookup(".chart-content").lookup(".chart-plot-background");
contentBackground.setStyle("-fx-background-color: transparent;");
lineChart.setVerticalZeroLineVisible(false);
lineChart.setHorizontalZeroLineVisible(false);
lineChart.setVerticalGridLinesVisible(false);
lineChart.setHorizontalGridLinesVisible(false);
lineChart.setCreateSymbols(false);
lineChart.getXAxis().setStyle("-fx-font-size:" + 18);
lineChart.getYAxis().setStyle("-fx-font-size:" + 18);
}
private String toRGBCode(Color color) {
return String.format("#%02X%02X%02X",
(int) (color.getRed() * 255),
(int) (color.getGreen() * 255),
(int) (color.getBlue() * 255));
}
private void setStyle(LineChart chart, Color lineColor) {
chart.getYAxis().lookup(".axis-label").setStyle("-fx-text-fill: " + toRGBCode(lineColor) + "; -fx-font-size: 24;");
Node seriesLine = chart.lookup(".chart-series-line");
seriesLine.setStyle("-fx-stroke: " + toRGBCode(lineColor) + "; -fx-stroke-width: " + strokeWidth + ";");
}
public Node getLegend() {
HBox hbox = new HBox();
final CheckBox baseChartCheckBox = new CheckBox(baseChart.getYAxis().getLabel());
baseChartCheckBox.setSelected(true);
baseChartCheckBox.setDisable(true);
baseChartCheckBox.getStyleClass().add("readonly-checkbox");
baseChartCheckBox.setOnAction(event -> baseChartCheckBox.setSelected(true));
hbox.getChildren().add(baseChartCheckBox);
for (final LineChart lineChart : backCharts) {
CheckBox checkBox = new CheckBox(lineChart.getYAxis().getLabel());
checkBox.setSelected(true);
checkBox.setOnAction(event -> {
if (backCharts.contains(lineChart)) {
backCharts.remove(lineChart);
} else {
backCharts.add(lineChart);
}
});
hbox.getChildren().add(checkBox);
}
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(20);
hbox.setStyle("-fx-padding: 0 10 20 10");
return hbox;
}
}
EditableText Class:
import com.sun.javafx.tk.FontMetrics;
import com.sun.javafx.tk.Toolkit;
import javafx.application.Platform;
import javafx.scene.control.TextField;
class EditableText extends TextField {
// The right margin allows a little bit of space
// to the right of the text for the editor caret.
private final double RIGHT_MARGIN = 5;
EditableText(double x, double y) {
relocate(x, y);
getStyleClass().add("editable-text");
//** CAUTION: this uses a non-public API (FontMetrics) to calculate the field size
// the non-public API may be removed in a future JavaFX version.
// see: https://javafx-jira.kenai.com/browse/RT-8060
// Need font/text measurement API
FontMetrics metrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(getFont());
setPrefWidth(RIGHT_MARGIN);
textProperty().addListener((observable, oldTextString, newTextString) ->
setPrefWidth(metrics.computeStringWidth(newTextString) + RIGHT_MARGIN)
);
Platform.runLater(this::requestFocus);
}
}
EditableDraggableText Class:
import javafx.scene.Cursor;
import javafx.scene.layout.Pane;
class EditableDraggableText extends Pane {
private final double PADDING = 5;
private EditableText text = new EditableText(PADDING, PADDING);
EditableDraggableText(double x, double y) {
relocate(x - PADDING, y - PADDING);
getChildren().add(text);
getStyleClass().add("editable-draggable-text");
// if the text is empty when we lose focus,
// the node has no purpose anymore
// just remove it from the scene.
text.focusedProperty().addListener((observable, hadFocus, hasFocus) -> {
if (!hasFocus && getParent() != null && getParent() instanceof Pane &&
(text.getText() == null || text.getText().trim().isEmpty())) {
((Pane) getParent()).getChildren().remove(this);
}
});
enableDrag();
}
public EditableDraggableText(int x, int y, String text) {
this(x, y);
this.text.setText(text);
}
// make a node movable by dragging it around with the mouse.
private void enableDrag() {
final Delta dragDelta = new Delta();
setOnMousePressed(mouseEvent -> {
this.toFront();
// record a delta distance for the drag and drop operation.
dragDelta.x = mouseEvent.getX();
dragDelta.y = mouseEvent.getY();
getScene().setCursor(Cursor.MOVE);
});
setOnMouseReleased(mouseEvent -> getScene().setCursor(Cursor.HAND));
setOnMouseDragged(mouseEvent -> {
double newX = getLayoutX() + mouseEvent.getX() - dragDelta.x;
if (newX > 0 && newX < getScene().getWidth()) {
setLayoutX(newX);
}
double newY = getLayoutY() + mouseEvent.getY() - dragDelta.y;
if (newY > 0 && newY < getScene().getHeight()) {
setLayoutY(newY);
}
});
setOnMouseEntered(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.HAND);
}
});
setOnMouseExited(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.DEFAULT);
}
});
}
// records relative x and y co-ordinates.
private class Delta {
double x, y;
}
}
问题存在是因为您试图在 .chart-plot-background
中添加 Text
,这是一个 Region
。 Region
适用于显示 Mouse Lines
等选项,它不是您应该添加 EditableDraggableText
.
的确切位置
在 .chart-content
中添加 EditableDraggableText
这是一个 Pane
并且包含
- 图表背景
- X 数轴
- Y 轴
使用以下代码:
Pane chartContent = (Pane)baseChart.lookup(".chart-content");
chartContent.setOnMouseClicked(event -> {
chartContent.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
});
Great. But, how do you avoid creating TextFields outside the graph-plot?
将事件的 X 和 Y 坐标与 chart-plot-background
的坐标进行比较,结果有效:)
Pane chartContent = (Pane)baseChart.lookup(".chart-content");
chartContent.setOnMouseClicked(event -> {
if(event.getX() >= chartBackground.getBoundsInParent().getMinX() &&
event.getX() <= chartBackground.getBoundsInParent().getMaxX() &&
event.getY() >= chartBackground.getBoundsInParent().getMinY() &&
event.getY() <= chartBackground.getBoundsInParent().getMaxY()) {
chartContent.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
}
});
我在 Swing
中使用 JavaFX
所以我有方法:
private Scene createScene() {
Group root = new Group();
Scene scene = new Scene(root);
BorderPane borderPane = new BorderPane();
...
//adding data series to Linechart baseChart
...
StackChart chart = new StackChart(baseChart); //returns StackPane with LineChart-s on it
borderPane.setCenter(chart);
root.getChildren().add(borderPane);
return scene;
}
如何在 Linechart
上放置可编辑的文本,如 here?
我尝试在ChartStack中添加到LineChart
(chartBackground是StackPane
中的第一个LineChart
):
chartBackground.setOnMouseClicked(event -> {
if (event.getTarget() == chartBackground) {
chartBackground.add(
new EditableDraggableText(event.getX(), event.getY())
);
super.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
}
});
但它绘制的文本不是在 LineChart
上而是在框架的左侧
所有 MCVE 是下一个 主要 Class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.function.Function;
public class GrinMain extends Application {
public static final int X_NUMBER = 800;
public double[] x;
public double[] y;
@Override
public void start(Stage primaryStage) throws Exception {
NumberAxis xAxis = new NumberAxis(0, X_NUMBER, 200);
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Series 1");
LineChart baseChart = new LineChart(xAxis, yAxis);
baseChart.getData().add(prepareSeries("Serie 1", (x) -> (double) x));
StackChart chart = new StackChart(baseChart, Color.RED);
chart.addSeries(prepareSeries("Serie 3", (x) -> (double) -2*x * x), Color.BLUE);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(chart);
borderPane.setBottom(chart.getLegend());
Scene scene = new Scene(borderPane, 1024, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private XYChart.Series<Number, Number> prepareSeries(String name, Function<Integer, Double> function) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.setName(name);
for (int i = 0; i < X_NUMBER; i++) {
series.getData().add(new XYChart.Data<>(i, function.apply(i)));
}
return series;
}
private XYChart.Series<Number, Number> prepareSeries(String name, double[] xValues, double[] yValues) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.setName(name);
for (int i = 0; i < yValues.length; i++){
series.getData().add(new XYChart.Data(xValues[i], yValues[i]));
}
return series;
}
public static void main(String[] args) {
launch(args);
}
}
堆栈图 class:
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
public class StackChart extends StackPane {
private final LineChart baseChart;
private final ObservableList<LineChart> backCharts = FXCollections.observableArrayList();
private final double yAxisWidth = 60;
private final AnchorPane details;
private final double yAxisSeparation = 20;
private double strokeWidth = 0.3;
public StackChart(LineChart baseChart, Color lineColor) {
this(baseChart, lineColor, null);
}
public StackChart(LineChart baseChart, Color lineColor, Double strokeWidth) {
if (strokeWidth != null) {
this.strokeWidth = strokeWidth;
}
this.baseChart = baseChart;
baseChart.setCreateSymbols(false);
baseChart.setLegendVisible(false);
baseChart.getXAxis().setAutoRanging(false);
baseChart.getXAxis().setAnimated(false);
baseChart.getXAxis().setStyle("-fx-font-size:" + 18);
baseChart.getYAxis().setAnimated(false);
baseChart.getYAxis().setStyle("-fx-font-size:" + 18);
setStyle(baseChart, lineColor);
setFixedAxisWidth(baseChart);
setAlignment(Pos.CENTER_LEFT);
backCharts.addListener((Observable observable) -> rebuildChart());
details = new AnchorPane();
bindMouseEvents(baseChart, this.strokeWidth);
rebuildChart();
}
private void bindMouseEvents(LineChart baseChart, Double strokeWidth) {
getChildren().add(details);
details.prefHeightProperty().bind(heightProperty());
details.prefWidthProperty().bind(widthProperty());
details.setMouseTransparent(true);
setOnMouseMoved(null);
setMouseTransparent(false);
final Axis xAxis = baseChart.getXAxis();
final Axis yAxis = baseChart.getYAxis();
final Line xLine = new Line();
final Line yLine = new Line();
yLine.setFill(Color.GRAY);
xLine.setFill(Color.GRAY);
yLine.setStrokeWidth(strokeWidth/2);
xLine.setStrokeWidth(strokeWidth/2);
xLine.setVisible(false);
yLine.setVisible(false);
final Node chartBackground = baseChart.lookup(".chart-plot-background");
for (Node n: chartBackground.getParent().getChildrenUnmodifiable()) {
if (n != chartBackground && n != xAxis && n != yAxis) {
n.setMouseTransparent(true);
}
}
chartBackground.setCursor(Cursor.CROSSHAIR);
chartBackground.setOnMouseEntered((event) -> {
chartBackground.getOnMouseMoved().handle(event);
xLine.setVisible(true);
yLine.setVisible(true);
details.getChildren().addAll(xLine, yLine);
});
chartBackground.setOnMouseExited((event) -> {
xLine.setVisible(false);
yLine.setVisible(false);
details.getChildren().removeAll(xLine, yLine);
});
chartBackground.setOnMouseMoved(event -> {
double x = event.getX() + chartBackground.getLayoutX();
double y = event.getY() + chartBackground.getLayoutY();
xLine.setStartX(65);
xLine.setEndX(details.getWidth()-10);
xLine.setStartY(y+5);
xLine.setEndY(y+5);
yLine.setStartX(x+5);
yLine.setEndX(x+5);
yLine.setStartY(12);
yLine.setEndY(details.getHeight()-28);
});
chartBackground.setOnMouseClicked(event -> {
if (event.getTarget() == chartBackground) {
super.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
}
});
}
private void setFixedAxisWidth(LineChart chart) {
chart.getYAxis().setPrefWidth(yAxisWidth);
chart.getYAxis().setMaxWidth(yAxisWidth);
}
private void rebuildChart() {
getChildren().clear();
getChildren().add(resizeBaseChart(baseChart));
for (LineChart lineChart : backCharts) {
getChildren().add(resizeBackgroundChart(lineChart));
}
getChildren().add(details);
}
private Node resizeBaseChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth+yAxisSeparation)*backCharts.size()));
return lineChart;
}
private Node resizeBackgroundChart(LineChart lineChart) {
HBox hBox = new HBox(lineChart);
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.prefHeightProperty().bind(heightProperty());
hBox.prefWidthProperty().bind(widthProperty());
hBox.setMouseTransparent(true);
lineChart.minWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.prefWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.maxWidthProperty().bind(widthProperty().subtract((yAxisWidth + yAxisSeparation) * backCharts.size()));
lineChart.translateXProperty().bind(baseChart.getYAxis().widthProperty());
lineChart.getYAxis().setTranslateX((yAxisWidth + yAxisSeparation) * backCharts.indexOf(lineChart));
return hBox;
}
public void addSeries(XYChart.Series series, Color lineColor) {
NumberAxis yAxis = new NumberAxis();
NumberAxis xAxis = new NumberAxis();
// xAxis
xAxis.setAutoRanging(false);
xAxis.setVisible(false);
xAxis.setOpacity(0.0);
xAxis.lowerBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).lowerBoundProperty());
xAxis.upperBoundProperty().bind(((NumberAxis) baseChart.getXAxis()).upperBoundProperty());
xAxis.tickUnitProperty().bind(((NumberAxis) baseChart.getXAxis()).tickUnitProperty());
// yAxis
yAxis.setSide(Side.RIGHT);
yAxis.setLabel(series.getName());
// create chart
LineChart lineChart = new LineChart(xAxis, yAxis);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
lineChart.getData().add(series);
styleBackChart(lineChart, lineColor);
setFixedAxisWidth(lineChart);
backCharts.add(lineChart);
}
private void styleBackChart(LineChart lineChart, Color lineColor) {
setStyle(lineChart, lineColor);
Node contentBackground = lineChart.lookup(".chart-content").lookup(".chart-plot-background");
contentBackground.setStyle("-fx-background-color: transparent;");
lineChart.setVerticalZeroLineVisible(false);
lineChart.setHorizontalZeroLineVisible(false);
lineChart.setVerticalGridLinesVisible(false);
lineChart.setHorizontalGridLinesVisible(false);
lineChart.setCreateSymbols(false);
lineChart.getXAxis().setStyle("-fx-font-size:" + 18);
lineChart.getYAxis().setStyle("-fx-font-size:" + 18);
}
private String toRGBCode(Color color) {
return String.format("#%02X%02X%02X",
(int) (color.getRed() * 255),
(int) (color.getGreen() * 255),
(int) (color.getBlue() * 255));
}
private void setStyle(LineChart chart, Color lineColor) {
chart.getYAxis().lookup(".axis-label").setStyle("-fx-text-fill: " + toRGBCode(lineColor) + "; -fx-font-size: 24;");
Node seriesLine = chart.lookup(".chart-series-line");
seriesLine.setStyle("-fx-stroke: " + toRGBCode(lineColor) + "; -fx-stroke-width: " + strokeWidth + ";");
}
public Node getLegend() {
HBox hbox = new HBox();
final CheckBox baseChartCheckBox = new CheckBox(baseChart.getYAxis().getLabel());
baseChartCheckBox.setSelected(true);
baseChartCheckBox.setDisable(true);
baseChartCheckBox.getStyleClass().add("readonly-checkbox");
baseChartCheckBox.setOnAction(event -> baseChartCheckBox.setSelected(true));
hbox.getChildren().add(baseChartCheckBox);
for (final LineChart lineChart : backCharts) {
CheckBox checkBox = new CheckBox(lineChart.getYAxis().getLabel());
checkBox.setSelected(true);
checkBox.setOnAction(event -> {
if (backCharts.contains(lineChart)) {
backCharts.remove(lineChart);
} else {
backCharts.add(lineChart);
}
});
hbox.getChildren().add(checkBox);
}
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(20);
hbox.setStyle("-fx-padding: 0 10 20 10");
return hbox;
}
}
EditableText Class:
import com.sun.javafx.tk.FontMetrics;
import com.sun.javafx.tk.Toolkit;
import javafx.application.Platform;
import javafx.scene.control.TextField;
class EditableText extends TextField {
// The right margin allows a little bit of space
// to the right of the text for the editor caret.
private final double RIGHT_MARGIN = 5;
EditableText(double x, double y) {
relocate(x, y);
getStyleClass().add("editable-text");
//** CAUTION: this uses a non-public API (FontMetrics) to calculate the field size
// the non-public API may be removed in a future JavaFX version.
// see: https://javafx-jira.kenai.com/browse/RT-8060
// Need font/text measurement API
FontMetrics metrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(getFont());
setPrefWidth(RIGHT_MARGIN);
textProperty().addListener((observable, oldTextString, newTextString) ->
setPrefWidth(metrics.computeStringWidth(newTextString) + RIGHT_MARGIN)
);
Platform.runLater(this::requestFocus);
}
}
EditableDraggableText Class:
import javafx.scene.Cursor;
import javafx.scene.layout.Pane;
class EditableDraggableText extends Pane {
private final double PADDING = 5;
private EditableText text = new EditableText(PADDING, PADDING);
EditableDraggableText(double x, double y) {
relocate(x - PADDING, y - PADDING);
getChildren().add(text);
getStyleClass().add("editable-draggable-text");
// if the text is empty when we lose focus,
// the node has no purpose anymore
// just remove it from the scene.
text.focusedProperty().addListener((observable, hadFocus, hasFocus) -> {
if (!hasFocus && getParent() != null && getParent() instanceof Pane &&
(text.getText() == null || text.getText().trim().isEmpty())) {
((Pane) getParent()).getChildren().remove(this);
}
});
enableDrag();
}
public EditableDraggableText(int x, int y, String text) {
this(x, y);
this.text.setText(text);
}
// make a node movable by dragging it around with the mouse.
private void enableDrag() {
final Delta dragDelta = new Delta();
setOnMousePressed(mouseEvent -> {
this.toFront();
// record a delta distance for the drag and drop operation.
dragDelta.x = mouseEvent.getX();
dragDelta.y = mouseEvent.getY();
getScene().setCursor(Cursor.MOVE);
});
setOnMouseReleased(mouseEvent -> getScene().setCursor(Cursor.HAND));
setOnMouseDragged(mouseEvent -> {
double newX = getLayoutX() + mouseEvent.getX() - dragDelta.x;
if (newX > 0 && newX < getScene().getWidth()) {
setLayoutX(newX);
}
double newY = getLayoutY() + mouseEvent.getY() - dragDelta.y;
if (newY > 0 && newY < getScene().getHeight()) {
setLayoutY(newY);
}
});
setOnMouseEntered(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.HAND);
}
});
setOnMouseExited(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.DEFAULT);
}
});
}
// records relative x and y co-ordinates.
private class Delta {
double x, y;
}
}
问题存在是因为您试图在 .chart-plot-background
中添加 Text
,这是一个 Region
。 Region
适用于显示 Mouse Lines
等选项,它不是您应该添加 EditableDraggableText
.
在 .chart-content
中添加 EditableDraggableText
这是一个 Pane
并且包含
- 图表背景
- X 数轴
- Y 轴
使用以下代码:
Pane chartContent = (Pane)baseChart.lookup(".chart-content");
chartContent.setOnMouseClicked(event -> {
chartContent.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
});
Great. But, how do you avoid creating TextFields outside the graph-plot?
将事件的 X 和 Y 坐标与 chart-plot-background
的坐标进行比较,结果有效:)
Pane chartContent = (Pane)baseChart.lookup(".chart-content");
chartContent.setOnMouseClicked(event -> {
if(event.getX() >= chartBackground.getBoundsInParent().getMinX() &&
event.getX() <= chartBackground.getBoundsInParent().getMaxX() &&
event.getY() >= chartBackground.getBoundsInParent().getMinY() &&
event.getY() <= chartBackground.getBoundsInParent().getMaxY()) {
chartContent.getChildren().addAll(new EditableDraggableText(event.getX(), event.getY()));
}
});