【发布时间】:2020-08-11 16:53:45
【问题描述】:
我已经对 Java FX 折线图进行了编程更改,我需要一种编程方式来强制重新布局 JavaFX 图表。这个问题之前已经被问过/回答过,但不是在我的上下文中。
我已经尝试了作为该问题答案的典型方法(请参阅下面的完整、最小的示例代码以及在线尝试解决问题)。这个问题的典型解决方案都不起作用。
具体来说(sp 是 StackPane):
sp.requestLayout(); // does not work
和
sp.applyCss();
sp.layout(); // does not work
将上述代码放在.runLater() 中不起作用。
我知道我的更改显示在图表中,因为 (1) 当我手动调整图表大小时,我的变化突然出现 (2) 当我以编程方式使用“resize”方法时,我的更改出现但出现了不同的错误(加上只有父节点应该使用“resize”方法 - 而不是我们的程序员)。
下面是重现问题的最小完整代码集。当您运行代码时,我以编程方式将其中一个数据点更改为在显示图表时更大。此调整大小正常工作。当您右键单击图表时,会出现一个带有一个选项的上下文菜单(“调整所有点的大小”)。当您选择该单个选项时,我的代码会调整所有点的大小 - 但是 - 没有一个数据点会在视觉上调整大小。如果我通过拖动一侧手动调整图表大小,图表会重新布局,所有数据节点大小会立即在视觉上更改为正确的大小(我以编程方式将它们设置为的大小)。
如何强制重新布局以编程方式进行,而我可以手动强制进行?我不想做一个 hack(比如以编程方式将舞台大小设置为小 1 个像素,然后将其设置为大一个像素)。
注意:我已经读到在布局进行时尝试执行requestLayout() 将被忽略,因此可能会发生类似的事情。我认为runLater() 中的requestLayout() 可以解决正在进行的 Layout() 的问题,但这也不起作用。
更新:建议将缩放作为更改 StackPane 大小的替代方法。这个解决方案可能对某些人有帮助,但对我没有帮助。缩放符号的外观与更改区域大小并允许“符号”增长到该大小的外观不同。
总的来说,这是我的第一篇 stackoverflow 帖子。因此,感谢我过去在此论坛中使用过的所有示例,并在此先感谢您对此问题的回答。
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.LineChart.SortingPolicy;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class dummy extends Application {
@Override
public void start(Stage primaryStage) {
Random random = new Random();
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("X");
yAxis.setLabel("Y");
final LineChart<Number,Number> lineChart = new LineChart<Number,Number>(xAxis,yAxis);
Series<Number,Number> series = new Series<Number,Number>();
series.setName("Dummy Data");
// Generate data
double x = 0.0;
double y = 0.0;
for (int i = 0; i < 10; i++) {
Data<Number,Number> data = new Data(x += random.nextDouble(), y+=random.nextDouble());
series.getData().add(data);
}
lineChart.getData().add(series);
lineChart.setTitle("Random Data");
lineChart.setAxisSortingPolicy(SortingPolicy.NONE);
Scene scene = new Scene(lineChart,1200,600);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
// This resizes the first data point directly (this resize is displayed correctly when program is run)
Node node = series.getData().get(0).getNode();
setSize((StackPane)node,20);
// The context menu is invoked by a right click on the line Chart. It will resize the data point based on a context menu pick
// this resize does not work....unless I resize the window manually which causes a refresh/re-layout of the chart).
lineChart.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
if (MouseButton.SECONDARY.equals(mouseEvent.getButton())) {
Scene scene = ((Node)mouseEvent.getSource()).getScene();
ContextMenu menu = createMenu(lineChart);
menu.show(scene.getWindow(), mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
}
});
}
private void setSize(StackPane sp, int size) {
sp.setMinSize(size, size);
sp.setMaxSize(size, size);
sp.setPrefSize(size, size);
}
// this creates a context menu that will allow you to resize all the data point nodes
private ContextMenu createMenu(LineChart<Number,Number> lineChart) {
final ContextMenu contextMenu = new ContextMenu();
final MenuItem resize = new MenuItem("Resize ALL the points");
resize.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
for (Series<Number, Number> series : lineChart.getData()) {
for (Data<Number, Number> data : series.getData()) {
StackPane sp = (StackPane)data.getNode();
setSize (sp, 20);
// The above resizes do not take effect unless/until I manually resize the chart.
// the following two calls do not do anything;
sp.applyCss();
sp.layout();
// The request to layout the node does nothing
sp.requestLayout();
// Doing both of the above as runLaters does nothing
Platform.runLater(()->{sp.applyCss();sp.layout();});
Platform.runLater(()->{sp.requestLayout();});
// Going after the parent does nothing either
Group group = (Group)sp.getParent();
group.applyCss();
group.layout();
group.requestLayout();
// Going after the parent in a run later does nothing
Platform.runLater(()->{
group.applyCss();
group.layout();
group.requestLayout();
});
// note... doing a resize [commented out below] will work-ish.
// The documentation says NOT to use it thought that as it is for internal use only.
// By work-ish, the data points are enlarged BUT they are no longer centered on the data point
// When I resize the stage they get centered again - so this "solves" my original problem but causes a different problem
////////////////////////////////////
// sp.resize(20, 20); // Uncomment this line to see how it mostly works but introduces a new issue
////////////////////////////////////
}
}
}
});
contextMenu.getItems().add(resize);
return contextMenu;
}
public static void main(String[] args) {
Application.launch(args);
}
}
【问题讨论】:
-
在
for循环结束后添加一个虚拟样式表lineChart.getStylesheets().add("");就可以了。这有点骇人听闻。所以我不是特别喜欢。我希望会出现更好的。 -
看起来像一个错误