XYData中的自定义节点
我已经创建了我的自定义图表,它扩展了javafx中的LineChart。我想要做的是操纵用于渲染XYData的节点。所以我忽略了以下内容:XYData中的自定义节点
@Override
protected void dataItemAdded(Series<X, Y> series, int itemIndex,
Data<X, Y> item) {
super.dataItemAdded(series, itemIndex, item);
StackPane itemNode = new StackPane();
itemNode.prefHeight(0.5);
itemNode.prefWidth(0.5);
item.setNode(itemNode);
itemNode.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// TODO Auto-generated method stub
LOG.info("node clicked");
}
});
}
但它似乎并没有工作。代码是否正确?是否可以设置为较小的高度和宽度?创建透明(不可见)节点的最佳方式是什么,但仍然可以单击它?
子类LineChart
可能是这里糟糕的选择。问题是您需要确切知道LineChart
实现如何设置数据上的节点。 (让我们忽略了可怕的设计决定了JavaFX队在这里取得:?为什么数据有一个参考,以它在所有视觉节点应该有一个Callback<Data<X,Y>, Node> symbolFactory
财产属于图表代替)
事实证明,如果你该系列添加到图表,然后再添加数据系列,每个数据点的dataItemAdded
方法被调用,因此,在这种使用情况下,你的方法的工作原理:
import java.util.Random;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.event.EventHandler;
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.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class LineChartClickableDataPoints extends Application {
private static final Logger LOG = Logger.getLogger("LineChartClickableDataPoints");
@Override
public void start(Stage primaryStage) {
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis, yAxis) {
@Override
protected void dataItemAdded(Series<Number, Number> series, int itemIndex,
Data<Number, Number> item) {
super.dataItemAdded(series, itemIndex, item);
StackPane itemNode = new StackPane();
itemNode.setPrefHeight(0.5);
itemNode.setPrefWidth(0.5);
item.setNode(itemNode);
itemNode.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
LOG.info("node clicked");
}
});
}
};
Random rng = new Random();
Series<Number, Number> data = new Series<>();
chart.getData().add(data);
data.setName("Data");
data.getData().addAll(IntStream.rangeClosed(1, 10)
.mapToObj(x -> new Data<Number, Number>(x, rng.nextDouble()))
.collect(Collectors.toList()));
BorderPane root = new BorderPane(chart);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
在另一方面,如果您将数据添加到系列,然后将该系列添加到图表,然后dataItemAdded
不会被调用。为此,您需要覆盖seriesAdded
。所以你可以这样做,但更深层的问题是你依靠LineChart
的实现,而不是仅仅依赖于它的规范。因此,即使您阅读了source code并且现在涵盖了所有可能性,但如果未来版本中的实现发生更改,您不能保证代码仍然有效。总之,"Inheritance violates encapsulation"。
相反,你可以设置数据的节点,当您创建数据:
import java.util.Random;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.event.EventHandler;
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.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class LineChartClickableDataPoints extends Application {
private static final Logger LOG = Logger.getLogger("LineChartClickableDataPoints");
@Override
public void start(Stage primaryStage) {
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis) ;
Random rng = new Random();
Series<Number, Number> data = new Series<>();
data.setName("Data");
data.getData().addAll(IntStream.rangeClosed(1, 10)
.mapToObj(x -> new Data<Number, Number>(x, rng.nextDouble()))
.peek(this::addNodeToItem)
.collect(Collectors.toList()));
chart.getData().add(data);
BorderPane root = new BorderPane(chart);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void addNodeToItem(Data<Number, Number> item) {
StackPane itemNode = new StackPane();
itemNode.setPrefHeight(0.5);
itemNode.setPrefWidth(0.5);
item.setNode(itemNode);
itemNode.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
LOG.info(String.format("node clicked: [%.2f, %.2f]%n",
item.getXValue().doubleValue(), item.getYValue().doubleValue()));
}
});
}
public static void main(String[] args) {
launch(args);
}
}
这种方法适用于任何一种情况下。
据我所知,对节点的大小没有要求。需要注意的是默认的样式表应用以下样式规则:
.chart-symbol { /* solid circle */
-fx-background-color: CHART_COLOR_1;
-fx-background-radius: 5px;
-fx-padding: 5px;
}
所以,只要节点具有应用到它的CSS,它会添加到它的填充的5个像素。你当然可以在外部样式表中定义你自己的css设置。
最后一个问题什么是Chart_color_1 – Apostolos
为什么重写该方法?你可以调用['setNode(...)'](http://docs.oracle.com/javase/8/javafx/api/javafx/scene/chart/XYChart.Data.html#setNode-javafx.scene .Node-),当你创建它时,直接在'Data'上。 –
:)虽然这不能回答我的问题,但你是正确的。我已经改变了实现并将我的自定义图表中的覆盖生存。大小的确与它有关。有人知道数据节点的大小是否有限制? – Apostolos
我不认为大小很重要,但当然如果它太小,无法点击。覆盖不起作用的原因是如何以及何时将默认节点添加到数据中。例如,如果首先将数据添加到系列,然后将该系列添加到图表,则不调用'dataItemAdded'方法;你需要重写'seriesAdded'来处理这个用例。 OTOH如果您将该系列添加到图表,然后将数据添加到该系列,您的方法可以正常工作。这是子类的一般问题:您需要知道实现。 “子类喙封装” –