[英]JavaFX custom chart class - how to bind a node's layoutX and layoutY properties to the display positions of a NumberAxis?
I'm writing a rudimentary Candlestick chart class in which the candlesticks are created as Regions
and are plotted by setting their layoutX
and layoutY
values to the getDisplayPosition()
of the relevant axis.我正在编写一个基本的烛台图表类,其中烛台被创建为
Regions
,并通过将它们的layoutX
和layoutY
值设置为相关轴的getDisplayPosition()
来绘制。
For example, to plot a candlestick at value 3 on an X axis, I do this:例如,要在 X 轴上绘制值为 3 的烛台,我这样做:
candlestick.setLayoutX(xAxis.getDisplayPosition(3));
When the stage resizes or when the axes are zoomed in or out, the candlesticks' layout values have to be reset so that the chart renders correctly.当舞台调整大小或轴放大或缩小时,必须重置烛台的布局值,以便图表正确呈现。 I'm currently handling this via
ChangeListener
s for resize events and Button.setOnAction()
s for zooming.我目前正在通过
ChangeListener
s 处理调整大小事件和Button.setOnAction()
s 进行缩放。
However, I'd rather bind the candlesticks' layout properties to the axes' display positions than set/reset the layout values, but can't find a "displayPositionProperty" (or similar) for a NumberAxis
.但是,我宁愿将烛台的布局属性绑定到轴的显示位置,而不是设置/重置布局值,但找不到
NumberAxis
的“displayPositionProperty”(或类似的)。
Is it possible to do this?是否有可能做到这一点? Which
NumberAxis
property would I bind to?我将绑定到哪个
NumberAxis
属性? ie. IE。
candlestick.layoutXProperty().bind(xAxis.WHICH_PROPERTY?);
Also, would binding the properties be more efficient than resetting layout positions?另外,绑定属性会比重置布局位置更有效吗? Some of the charts could potentially have thousands of candlesticks but I can't test resource usage until I figure out how to code the bind.
有些图表可能有数千个烛台,但在我弄清楚如何对绑定进行编码之前,我无法测试资源使用情况。
I've experimented with scaling the candlesticks to the axes' scale but can't use that approach because scaling a Region
affects its border width.我已经尝试将烛台缩放到轴的比例,但不能使用这种方法,因为缩放
Region
会影响其边框宽度。 For certain types of candlesticks, that can change its meaning.对于某些类型的烛台,这可能会改变其含义。
I've also played with the Ensemble candlestick demo chart.我还使用了 Ensemble 烛台演示图表。 It was useful in giving me a start but is too simplistic for my needs.
它对我的开始很有用,但对于我的需求来说太简单了。
Here's a MVCE that demonstrates my approach.这是一个 MVCE,它展示了我的方法。 Any guidance re binding would be very much appreciated.
任何重新绑定的指导将不胜感激。
I'm using OpenJFX 17.我正在使用 OpenJFX 17。
package test023;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test023 extends Application {
@Override
public void start(Stage stage) {
NumberAxis xAxis = new NumberAxis(0D, 10D, 1D);
Pane pChart = new Pane();
Pane pAxis = new Pane();
VBox vb = new VBox();
BorderPane bp = new BorderPane();
pChart.setPrefHeight(100D);
pAxis.getChildren().add(xAxis);
xAxis.prefWidthProperty().bind(pAxis.widthProperty());
xAxis.setAnimated(false);
vb.setPadding(new Insets(10D));
vb.getChildren().addAll(pChart, pAxis);
Region point = new Region();
point.setPrefSize(5D, 5D);
point.setStyle("-fx-background-color: black;");
pChart.getChildren().add(point);
//Plot the point in its initial position (value 3 on the axis)
double pointXValue = 3D;
plotPoint(point, pointXValue, xAxis);
//*****************************************************************
//Can the listeners and button.setOnActions be replaced by binding
//the point's layout value to the axis display position?
//*****************************************************************
//Handle resize events
pChart.widthProperty().addListener((obs, oldVal, newVal) -> {
plotPoint(point, pointXValue, xAxis);
});
stage.maximizedProperty().addListener((obs, oldVal, newVal) -> {
plotPoint(point, pointXValue, xAxis);
});
//Handle zooming (hard-coded upper and lower bounds for the
//sake of simplicity)
Button btnZoomIn = new Button("Zoom in");
btnZoomIn.setOnAction((event) -> {
xAxis.setLowerBound(2D);
xAxis.setUpperBound(8D);
xAxis.layout();
plotPoint(point, pointXValue, xAxis);
});
Button btnZoomOut = new Button("Zoom out");
btnZoomOut.setOnAction((event) -> {
xAxis.setLowerBound(0D);
xAxis.setUpperBound(10D);
xAxis.layout();
plotPoint(point, pointXValue, xAxis);
});
bp.setCenter(vb);
bp.setTop(new HBox(btnZoomIn, btnZoomOut));
stage.setScene(new Scene(bp));
stage.setTitle("Test bind layoutX");
stage.setWidth(400D);
stage.setHeight(200D);
stage.show();
}
private void plotPoint(Region region, double axisPos, NumberAxis axis) {
Platform.runLater(() -> {
double posX = axis.getDisplayPosition(axisPos);
region.setLayoutX(posX);
region.setLayoutY(80D);
});
}
public static void main(String args[]){
launch(args);
}
}
Something like this would work像这样的东西会起作用
@Override
public void start(Stage stage) {
NumberAxis xAxis = new NumberAxis(0D, 10D, 1D);
Pane pChart = new Pane();
Pane pAxis = new Pane();
VBox vb = new VBox();
BorderPane bp = new BorderPane();
pChart.setPrefHeight(100D);
pAxis.getChildren().add(xAxis);
xAxis.prefWidthProperty().bind(pAxis.widthProperty());
xAxis.setAnimated(false);
vb.setPadding(new Insets(10D));
vb.getChildren().addAll(pChart, pAxis);
Region point = new Region();
point.setPrefSize(5D, 5D);
point.setStyle("-fx-background-color: black;");
pChart.getChildren().add(point);
//Plot the point in its initial position (value 3 on the axis)
double pointXValue = 3D;
point.setLayoutY(80D);
point.layoutXProperty().bind(Bindings.createDoubleBinding(()-> {
return xAxis.getDisplayPosition(pointXValue);
}, xAxis.lowerBoundProperty(), xAxis.upperBoundProperty(), pChart.widthProperty()));
//Handle zooming (hard-coded upper and lower bounds for the
//sake of simplicity)
Button btnZoomIn = new Button("Zoom in");
btnZoomIn.setOnAction((event) -> {
xAxis.setLowerBound(2D);
xAxis.setUpperBound(8D);
xAxis.layout();
});
Button btnZoomOut = new Button("Zoom out");
btnZoomOut.setOnAction((event) -> {
xAxis.setLowerBound(0D);
xAxis.setUpperBound(10D);
xAxis.layout();
});
bp.setCenter(vb);
bp.setTop(new HBox(btnZoomIn, btnZoomOut));
stage.setScene(new Scene(bp));
stage.setTitle("Test bind layoutX");
stage.setWidth(400D);
stage.setHeight(200D);
stage.show();
}
This creates a custom double binding with a function that calculates the value of the binding every time the dependencies are changed, see createDoubleBinding for more info.这将创建一个定制的双用计算的每一个依赖关系发生变化时绑定,请参阅值的函数结合createDoubleBinding获取更多信息。
+1 for @LukasOwen answer which actually you actual question related to bindings. +1 @LukasOwen 答案实际上是您与绑定相关的实际问题。
But as you are aware that every problem has more than one approach, I am suggesting mine, considering the scalability (adding many points) and too many bindings (for every point).但正如您所知,每个问题都有不止一种方法,我建议我考虑可扩展性(添加很多点)和太多绑定(对于每个点)。
The key things in this approach are:这种方法的关键是:
Below is the example of the approach:以下是该方法的示例:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Map;
public class Test023 extends Application {
Map<Double, Region> plotPoints = new HashMap<>();
double yOffset = 80D;
Pane pChart;
@Override
public void start(Stage stage) {
NumberAxis xAxis = new NumberAxis(0D, 10D, 1D);
xAxis.needsLayoutProperty().addListener((obs, old, needsLayout) -> {
if(!needsLayout) {
plotPoints.forEach((num, point) -> {
double posX = xAxis.getDisplayPosition(num);
point.setLayoutX(posX);
point.setLayoutY(yOffset);
});
}
});
pChart = new Pane();
Pane pAxis = new Pane();
VBox vb = new VBox();
BorderPane root = new BorderPane();
pChart.setPrefHeight(100D);
pAxis.getChildren().add(xAxis);
xAxis.prefWidthProperty().bind(pAxis.widthProperty());
xAxis.setAnimated(false);
vb.setPadding(new Insets(10D));
vb.getChildren().addAll(pChart, pAxis);
addPoint(3D, "black");
addPoint(4D, "red");
addPoint(5D, "blue");
//Handle zooming (hard-coded upper and lower bounds for the sake of simplicity)
Button btnZoomIn = new Button("Zoom in");
btnZoomIn.setOnAction((event) -> {
xAxis.setLowerBound(2D);
xAxis.setUpperBound(8D);
});
Button btnZoomOut = new Button("Zoom out");
btnZoomOut.setOnAction((event) -> {
xAxis.setLowerBound(0D);
xAxis.setUpperBound(10D);
});
root.setCenter(vb);
root.setTop(new HBox(btnZoomIn, btnZoomOut));
stage.setScene(new Scene(root));
stage.setTitle("Test bind layoutX");
stage.setWidth(400D);
stage.setHeight(200D);
stage.show();
}
private void addPoint(double num, String color) {
Region point = new Region();
point.setPrefSize(5D, 5D);
point.setStyle("-fx-background-color: " + color);
plotPoints.put(num, point);
pChart.getChildren().add(point);
}
public static void main(String args[]) {
launch(args);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.