JavaFX自定义图表如何将节点layoutX/Y绑定到NumberAxis的显示位置
实现方案
你要的绑定效果完全可以实现,NumberAxis没有直接暴露displayPositionProperty,但可以通过自定义绑定实现需求:
1. 核心绑定逻辑
利用JavaFX的Bindings.createDoubleBinding()自定义计算逻辑,绑定NumberAxis的三个会影响显示位置的可观察属性即可:
lowerBoundProperty():X轴最小值upperBoundProperty():X轴最大值widthProperty():X轴宽度
对应你需要的X轴数值pointXValue的位置绑定代码如下:
import javafx.beans.binding.Bindings; // 替换原来的plotPoint调用和所有相关的监听器 double pointXValue = 3D; point.layoutXProperty().bind( Bindings.createDoubleBinding( () -> xAxis.getDisplayPosition(pointXValue), xAxis.lowerBoundProperty(), xAxis.upperBoundProperty(), xAxis.widthProperty() ) ); // layoutY固定的话直接赋值即可,不需要动态更新 point.setLayoutY(80D);
添加这段绑定后,窗口缩放、X轴区间调整时都会自动重新计算K线位置,不需要你手动写任何事件监听逻辑。
2. 效率说明
属性绑定和你当前手动监听的效率本质是一致的,甚至在多属性同时变更的场景下性能更好:
- 绑定是懒触发机制,只有依赖的属性发生变更时才会执行位置计算,无额外冗余开销
- 同一时刻多个依赖属性同时更新(比如同时修改X轴上下限),绑定只会触发一次重计算,比你多监听器分别触发的逻辑性能更高
- 不需要手动调用
Platform.runLater(),减少了额外的线程调度开销
针对数千根K线的场景,该方案的资源占用和你当前的实现基本持平,甚至更低,完全可以满足需求。
3. 修正后的完整MVCE代码
package test023; import javafx.application.Application; import javafx.beans.binding.Bindings; 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); // 绑定X坐标,替换所有手动更新逻辑 double pointXValue = 3D; point.layoutXProperty().bind( Bindings.createDoubleBinding( () -> xAxis.getDisplayPosition(pointXValue), xAxis.lowerBoundProperty(), xAxis.upperBoundProperty(), xAxis.widthProperty() ) ); point.setLayoutY(80D); // 缩放按钮不需要再手动调用更新逻辑 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); }); 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(); } public static void main(String args[]){ launch(args); } }
内容的提问来源于stack exchange,提问作者GreenZebra
相关产品推荐
相关产品推荐

