【发布时间】:2016-01-31 01:30:34
【问题描述】:
我是 JavaFX 的新用户,我的实习项目遇到了一些问题。我必须制作一个 GUI 来绘制一个光谱并在每个峰的顶部添加标签(x 值)。用户可以放大图表,因此必须更新标签。
我使用第一种方法添加基于此帖子的标签:write text on a chart 但我遇到了一些麻烦,只有一半的标签可见(见图)并且缩放后标签位置的更新不好。我检查了每个标签的值和位置,它们都很好,如果我在图表上应用一个动作,标签的后半部分就会出现,所以我不知道这种误解的原因。我的更新(标签位置)功能是缩放上限 yaxis 的侦听器,但是为了获得良好的位置,我需要手动使用更新按钮,如果有人有想法,我找不到解决方案?
我目前使用anchorpane,使用stackpane更好吗?
我放了一部分代码:
public class PaneController implements Initializable{
@FXML
private LineChart<Number, Number> lineChart;
private ObservableList<Annotation> annotationNodes = FXCollections.observableArrayList();
@Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
lineChart.setLegendVisible(false);
lineChart.setCreateSymbols(false);
lineChart.setData(createData());
//create labels
createLabels(lineChart.getData());
//frame listener
((AnchorPane)lineChart.getParent()).heightProperty().addListener((num)-> update());
//axis listener
((NumberAxis)lineChart.getYAxis()).upperBoundProperty().addListener((num)-> update());
//update listener on middle button
lineChart.getParent().addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( event.getButton() == MouseButton.MIDDLE){
update();
}
}
});
MyJFXChartUtil.setupZooming( lineChart );
}
/** create data for the line chart */
public ObservableList<Series<Number,Number>> createData(){
ObservableList<Series<Number,Number>> data = FXCollections.observableArrayList();
for(int i=1;i<=10;i++){
XYChart.Series<Number, Number> peakSelect = new XYChart.Series<>();
peakSelect.getData().add(new XYChart.Data<Number, Number>(i*2,0));
peakSelect.getData().add(new XYChart.Data<Number, Number>(i*2,i));
data.add(peakSelect);
}
return data;
}
/** place a x-value label on each peak */
private void createLabels(ObservableList<Series<Number,Number>> data){
Pane pane = (Pane) lineChart.getParent();
//clear old annotations
pane.getChildren().clear();
pane.getChildren().add(lineChart);
for(int i=0;i<lineChart.getData().size();i++){
for(XYChart.Data<Number, Number> value:lineChart.getData().get(i).getData()){
if(value.getYValue().intValue()>0){
//massAnnot
Annotation massAnnot = new Annotation(new Label(""+value.getXValue()), value.getXValue().doubleValue(), value.getYValue().doubleValue());
annotationNodes.add(massAnnot);
//labelAnnot
Annotation labelAnnot = new Annotation(new Label(""+(i+1)), value.getXValue().doubleValue(), 11);
annotationNodes.add(labelAnnot);
}
}
}
//add node to parent
for (Annotation annot : annotationNodes) {
pane.getChildren().add(annot.getLabel());
}
}
/** update position of labels */
private void update(){
//clear nodes and add the linechart as children
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
System.out.println(xAxis.getUpperBound()+" update "+yAxis.getUpperBound());
Pane pane = (Pane) lineChart.getParent();
pane.getChildren().clear();
pane.getChildren().add(lineChart);
int i = 0;
for (Annotation annot : annotationNodes) {
Node node = annot.getLabel();
if(i%2==0){//massAnnot
double x = lineChart.getXAxis().localToParent(xAxis.getDisplayPosition(annot.getX()), 0).getX() + lineChart.getPadding().getLeft();
double y = yAxis.localToParent(0,yAxis.getDisplayPosition(annot.getY())).getY() + lineChart.getPadding().getTop();
node.setLayoutX(x);
node.setLayoutY(y - node.prefHeight(Integer.MAX_VALUE));
}
else{//labelAnnot
double x = lineChart.getXAxis().localToParent(xAxis.getDisplayPosition(annot.getX()), 0).getX() + lineChart.getPadding().getLeft();
double y = yAxis.localToParent(0,yAxis.getDisplayPosition(annot.getY())).getY() + lineChart.getPadding().getTop();
node.setLayoutX(x);
node.setLayoutY(y - node.prefHeight(Integer.MAX_VALUE));
}
node.autosize();
pane.getChildren().add(node);
}
}
class Annotation{
private Label label;
private double x;
private double y;
public Annotation(Label label, double x, double y) {
this.label = label;
this.x = x;
this.y = y;
}
public Label getLabel(){
return this.label;
}
public double getX(){
return this.x;
}
public double getY(){
return this.y;
}
}
}
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Pane.fxml"));
AnchorPane rootPane = (AnchorPane) loader.load();
Scene scene = new Scene(rootPane);
primaryStage.setScene(scene);
primaryStage.setTitle("Test");
PaneController controller = loader.getController();
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.PaneController">
<children>
<LineChart fx:id="lineChart" prefHeight="400.0" prefWidth="500.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<xAxis>
<NumberAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
</children>
</AnchorPane>
【问题讨论】:
-
能否请您发布一个简短的示例? sscce.org
-
我通过 MVCE 编辑代码,我无法重现只看到一半的标签,但您可以测试它们的位置,根据上限 y 轴在开始时自动进行更新,但是这是错的。如果你按下鼠标中键,你会调用更新函数并看到标签移动。但是,如果您调整框架的大小,则会出现同样的问题。抱歉我的英语不好,感谢您的帮助。
-
我很困惑,看起来您的代码没有任何问题。问题可能出在缩放代码上,你能把它也包括进去吗?
-
jfxutils library提供的zoom代码,示例实现难度较大,需要在构建路径中添加库。
-
我不想使用按钮来更新标签的位置,我希望在缩放或调整框架大小时自动更新。