JavaFx 2.x:如何在图表上编写文本?

编程入门 行业动态 更新时间:2024-10-16 00:26:31
本文介绍了JavaFx 2.x:如何在图表上编写文本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

通过在图表上单击鼠标左键,我想通过创建文本区域矩形来写文本,以便能够调整大小和移动.

By left mouse click on a chart I would like to write text by creating a text area rectangle so to be able to resize and move.

任何帮助都非常感激

编辑:您好,sarcan非常感谢您的答复.

Edit: Hi sarcan thank you very much for your kind reply.

我尝试了您的代码,它会编译并绘制带有注释的面积图,非常棒!

I tried your code, it compiles and it plots an area chart with annotation, very great work!

我现在需要更改您的代码,以便能够在单击鼠标左键后立即用键盘输入,而不是现在打印注释.

I now need to change your code in a way to be able to type with key keyboard once left mouse clicked instead of printing annotations as of now.

下面是您的完整代码

import javafx.application.Application; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.chart.AreaChart; import javafx.scene.chart.Axis; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Series; import javafx.scene.control.Label; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; /** * * @author sarcan */ public class SampleApp extends Application { public class SampleChart extends AreaChart<Number, Number> { public SampleChart() { super(new NumberAxis(), new NumberAxis()); getXAxis().setLabel("X"); getYAxis().setLabel("Y"); final Series<Number, Number> data = new Series<Number, Number>(); data.setName("Dummy data"); data.getData().addAll( new Data<Number, Number>(0,4), new Data<Number, Number>(1,5), new Data<Number, Number>(2,6), new Data<Number, Number>(3,5), new Data<Number, Number>(4,5), new Data<Number, Number>(5,7), new Data<Number, Number>(6,8), new Data<Number, Number>(7,9), new Data<Number, Number>(8,7) ); getData().add(data); } } public class ChartAnnotationNode { private final Node _node; private double _x; private double _y; public ChartAnnotationNode(final Node node, final double x, final double y) { _node = node; _x = x; _y = y; } public Node getNode() { return _node; } public double getX() { return _x; } public double getY() { return _y; } public void setX(final double x) { _x = x; } public void setY(final double y) { _y = y; } } public class ChartAnnotationOverlay extends Pane { private ObservableList<ChartAnnotationNode> _annotationNodes; private XYChart<Number, Number> _chart; public ChartAnnotationOverlay(final XYChart<Number, Number> chart) { _chart = chart; /* Create a list to hold your annotations */ _annotationNodes = FXCollections.observableArrayList(); /* This will be our update listener, to be invoked whenever the chart changes or annotations are added */ final InvalidationListener listener = new InvalidationListener() { @Override public void invalidated(final Observable observable) { update(); } }; _chart.needsLayoutProperty().addListener(listener); _annotationNodes.addListener(listener); /* Add new annotations by shift-clicking */ setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(final MouseEvent mouseEvent) { if (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.isShiftDown()) addAnnotation(mouseEvent.getX(), mouseEvent.getY()); } }); } /** * Invoked whenever the chart changes or annotations are added. This basically does a relayout of the annotation nodes. */ private void update(){ getChildren().clear(); final Axis<Number> xAxis = _chart.getXAxis(); final Axis<Number> yAxis = _chart.getYAxis(); /* For each annotation, add a circle indicating the position and the custom node right next to it */ for (ChartAnnotationNode annotation : _annotationNodes) { final double x = xAxis.localToParent(xAxis.getDisplayPosition(annotation.getX()), 0).getX() + _chart.getPadding().getLeft(); final double y = yAxis.localToParent(0,yAxis.getDisplayPosition(annotation.getY())).getY() + _chart.getPadding().getTop(); final Circle indicator = new Circle(3); indicator.setStroke(Color.BLUEVIOLET); indicator.setCenterX(x); indicator.setCenterY(y); getChildren().add(indicator); final Node node = annotation.getNode(); getChildren().add(node); node.relocate(x + 10, y - node.prefHeight(Integer.MAX_VALUE) / 2); node.autosize(); } } /** * Add a new annotation for the given display coordinate. */ private void addAnnotation(final double displayX, final double displayY){ final Axis<Number> xAxis = _chart.getXAxis(); final Axis<Number> yAxis = _chart.getYAxis(); final double x = (xAxis.getValueForDisplay(xAxis.parentToLocal(displayX, 0).getX() - _chart.getPadding().getLeft())).doubleValue(); final double y = (yAxis.getValueForDisplay(yAxis.parentToLocal(0, displayY).getY() - _chart.getPadding().getTop())).doubleValue(); if (xAxis.isValueOnAxis(x) && yAxis.isValueOnAxis(y)) _annotationNodes.add(new ChartAnnotationNode(new Label("Annotation "+System.currentTimeMillis()), x, y)); } } @Override public void start(final Stage stage) throws Exception { final SampleChart chart = new SampleChart(); final ChartAnnotationOverlay overlay = new ChartAnnotationOverlay(chart); final StackPane stackPane = new StackPane(); stackPane.getChildren().addAll(chart, overlay); final Scene scene = new Scene(stackPane); stage.setScene(scene); stage.setWidth(800); stage.setHeight(600); stage.show(); } public static void main(String[] args) { Application.launch(args); } }

推荐答案

1)我首先将图表放在StackPane中.在图表的顶部,我将放置一个锚定窗格,单击鼠标即可保留文本字段.

1) I'd start by putting the chart within a StackPane. On top of the chart I'd place an anchor pane holding the text field upon mouse click.

2)当用户单击图表时,我将使用图表的轴来确定单击是否位于绘图区域内以及单击了哪个值"(使用NumberAxis#getValueForDisplay().

2) When the user clicks the chart, I'd use the chart's axes to determine whether the click was inside the plot area and which 'value' was clicked (using NumberAxis#getValueForDisplay().

3)然后,我将向图表添加侦听器,以便在发生任何更改(内容,宽度,高度...)时得到通知,并使文本区域的位置始终显示在相同的值附近.

3) I would then add listeners to the chart in order to be notified of any changes (content, width, height...) and adapt the text area's position to always display near the same value.

调整大小/简单明了,请告诉我们是否给您带来麻烦.

Resizing / straight-forward, please let us know if that gives you any trouble.

根据要求,下面是一些示例代码.下面的代码提供了一个简化的示例,允许您通过按住Shift键并单击将文本节点(我称其为注释)添加到图表中.拖动或编辑批注很简单,但是我想保持示例简洁.

As requested, here is some sample code. The code below provides a simplified example, allowing you to add text nodes (I'll call them annotations) to the chart by shift-clicking. Dragging or editing the annotations is straight-forward, but I wanted to keep the example concise.

让我们先定义一个样本图:

Lets start by defining a sample chart:

public class SampleChart extends AreaChart<Number, Number> { public SampleChart() { super(new NumberAxis(), new NumberAxis()); getXAxis().setLabel("X"); getYAxis().setLabel("Y"); final Series<Number, Number> data = new Series<Number, Number>(); data.setName("Dummy data"); data.getData().addAll( new Data<Number, Number>(0,4), new Data<Number, Number>(1,5), new Data<Number, Number>(2,6), new Data<Number, Number>(3,5), new Data<Number, Number>(4,5), new Data<Number, Number>(5,7), new Data<Number, Number>(6,8), new Data<Number, Number>(7,9), new Data<Number, Number>(8,7) ); getData().add(data); } }

到目前为止,我还没有幻想,我只是用一些随机的模拟数据创建了一个面积图.

Nothing fancy so far, I just create an area chart with some random mock data.

对于文本节点(或注解),我创建了一个简单的POJO,其中包含带注释的X/Y值(非显示位置)并采用要渲染的自定义节点:

For the text nodes (or annotations), I've created a simple POJO containing the annotated X/Y value (not display position) and taking a custom node to be rendered:

public class ChartAnnotationNode { private final Node _node; private double _x; private double _y; public ChartAnnotationNode(final Node node, final double x, final double y) { _node = node; _x = x; _y = y; } public Node getNode() { return _node; } public double getX() { return _x; } public double getY() { return _y; } public void setX(final double x) { _x = x; } public void setY(final double y) { _y = y; } }

有趣的事情发生在我称为叠加层的内部:一个透明的面板,它将放置在图表上方.请注意,我没有按照最初的建议选择AnchorPane,尽管那样也可以.此外,这种实现方法并不是最有效的方法,但我想使示例保持简单.

The interesting stuff happens within what I'll refer to as the overlay: a transparent panel which will be placed above the chart. Note that I did not, as originally advised, choose AnchorPane, though that would have worked as well. Furthermore, this implementation is not exactly the most efficient approach, but I wanted to keep the example simple.

public class ChartAnnotationOverlay extends Pane { private ObservableList<ChartAnnotationNode> _annotationNodes; private XYChart<Number, Number> _chart; public ChartAnnotationOverlay(final XYChart<Number, Number> chart) { _chart = chart; /* Create a list to hold your annotations */ _annotationNodes = FXCollections.observableArrayList(); /* This will be our update listener, to be invoked whenever the chart changes or annotations are added */ final InvalidationListener listener = new InvalidationListener() { @Override public void invalidated(final Observable observable) { update(); } }; _chart.needsLayoutProperty().addListener(listener); _annotationNodes.addListener(listener); /* Add new annotations by shift-clicking */ setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(final MouseEvent mouseEvent) { if (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.isShiftDown()) addAnnotation(mouseEvent.getX(), mouseEvent.getY()); } }); } /** * Invoked whenever the chart changes or annotations are added. This basically does a relayout of the annotation nodes. */ private void update(){ getChildren().clear(); final Axis<Number> xAxis = _chart.getXAxis(); final Axis<Number> yAxis = _chart.getYAxis(); /* For each annotation, add a circle indicating the position and the custom node right next to it */ for (ChartAnnotationNode annotation : _annotationNodes) { final double x = xAxis.localToParent(xAxis.getDisplayPosition(annotation.getX()), 0).getX() + _chart.getPadding().getLeft(); final double y = yAxis.localToParent(0,yAxis.getDisplayPosition(annotation.getY())).getY() + _chart.getPadding().getTop(); final Circle indicator = new Circle(3); indicator.setStroke(Color.BLUEVIOLET); indicator.setCenterX(x); indicator.setCenterY(y); getChildren().add(indicator); final Node node = annotation.getNode(); getChildren().add(node); node.relocate(x + 10, y - node.prefHeight(Integer.MAX_VALUE) / 2); node.autosize(); } } /** * Add a new annotation for the given display coordinate. */ private void addAnnotation(final double displayX, final double displayY){ final Axis<Number> xAxis = _chart.getXAxis(); final Axis<Number> yAxis = _chart.getYAxis(); final double x = (xAxis.getValueForDisplay(xAxis.parentToLocal(displayX, 0).getX() - _chart.getPadding().getLeft())).doubleValue(); final double y = (yAxis.getValueForDisplay(yAxis.parentToLocal(0, displayY).getY() - _chart.getPadding().getTop())).doubleValue(); if (xAxis.isValueOnAxis(x) && yAxis.isValueOnAxis(y)) _annotationNodes.add(new ChartAnnotationNode(new Label("Annotation "+System.currentTimeMillis()), x, y)); } }

棘手的部分是视图和显示之间的坐标转换.要获取给定值的显示位置,可以调用Axis#getDisplayPosition(...),但是返回的坐标将在轴的坐标空间中.调用Axis#localToParent会将其转换为图表的坐标空间.通常,您希望能够只使用这些坐标,但是图表的默认填充为5像素,由于某些原因,该填充将无法正确转换.

The tricky part is the coordinate translation between view and display. To get the display position for a given value, you can invoke Axis#getDisplayPosition(...), yet the coordinate returned will be in the axis' coordinate space. The call to Axis#localToParent translates this into the chart's coordinate space. Normally, you'd expect to be able to just use those coordinates, but the chart has a default padding of 5 pixels that for some reason will not be translated correctly.

这是一个将所有内容组合在一起的小型测试应用程序:

Here's a small test app putting it all together:

public class SampleApp extends Application { @Override public void start(final Stage stage) throws Exception { final SampleChart chart = new SampleChart(); final ChartAnnotationOverlay overlay = new ChartAnnotationOverlay(chart); final StackPane stackPane = new StackPane(); stackPane.getChildren().addAll(chart, overlay); final Scene scene = new Scene(stackPane); stage.setScene(scene); stage.setWidth(800); stage.setHeight(600); stage.show(); } public static void main(String[] args) { Application.launch(args); } }

现在您已经有了叠加代码+转换坐标背后的想法,拖动节点也应该很简单.拖动注释的节点时,获取其显示位置,添加拖动增量,将其转换为值并将其应用于注释实例.

Now that you have the overlay code + the idea behind translating coordinates, dragging the nodes should be simple as well. When an annotation's node is dragged, get its display position, add the drag delta, convert it to value and apply it to the annotation instance.

希望这会使事情变得更清楚.

Hope this makes things a bit more clear.

更多推荐

JavaFx 2.x:如何在图表上编写文本?

本文发布于:2023-07-15 12:57:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1111363.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:图表   文本   如何在   JavaFx

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!