在JavaFX图表中添加一行 [英] Adding a line in a JavaFX chart

查看:304
本文介绍了在JavaFX图表中添加一行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在JavaFX中的定义位置添加一行时遇到问题。该行必须是一条恒定的行,如下所示:



重要的部分是关于顶端。我希望在y = 60行上有这条线。 RadioBoxes的左侧部分是VBox。带有(Scatter-)图表的部分是StackPane(因为我希望它填充宽度的其余部分)。 StackPane内部是图表和组。该集团唯一的子女就是该行。



我认为问题是,StackPane将集团的中心放在图表上方。但我无法获得
的布局组合1.拉伸图表
2.将线条设置在图表上方
3.不会使线条居中



我尝试了很多组合,但我无法按照我想要的方式得到它。有没有人有想法?!

解决方案

不幸的是,XYCharts不支持ValueMarkers(这可能是层次结构中的位置应该这样做(Mis-)使用具有常数值的数据是一种在某些情况下可能(或不可)接受/可能的黑客攻击。



更清洁的出路是支持此类标记的自定义图表。 F.i.自定义ScatterChart,如:

 公共类ScatterXChart< X,Y>扩展ScatterChart< X,Y> {

//定义水平标记的数据,忽略xValues
private ObservableList< Data< X,Y>> horizo​​ntalMarkers;

public Sc​​atterXChart(Axis< X> xAxis,Axis< Y> yAxis){
super(xAxis,yAxis);
//通知更改yValue属性的列表
horizo​​ntalMarkers = FXCollections.observableArrayList(d - > new Observable [] {d.YValueProperty()});
//收听列表更改并重新绘制
horizo​​ntalMarkers.addListener((InvalidationListener)observable - > layoutPlotChildren());
}

/ **
*添加水平值标记。标记的Y值用于在绘图区域绘制
*水平线,忽略其X值。
*
* @param标记不得为空。
* /
public void addHorizo​​ntalValueMarker(Data< X,Y> marker){
Objects.requireNonNull(marker,marker不能为null);
if(horizo​​ntalMarkers.contains(marker))返回;
Line line = new Line();
marker.setNode(line);
getPlotChildren()。add(line);
horizo​​ntalMarkers.add(marker);
}

/ **
*删除水平值标记。
*
* @param horizo​​ntalMarker不能为null
* /
public void removeHorizo​​ntalValueMarker(Data< X,Y> marker){
Objects.requireNonNull(marker, 标记不得为空);
if(marker.getNode()!= null){
getPlotChildren()。remove(marker.getNode());
marker.setNode(null);
}
horizo​​ntalMarkers.remove(marker);
}

/ **
*重写以布置值标记。
* /
@Override
protected void layoutPlotChildren(){
super.layoutPlotChildren();
for(Data< X,Y> horizo​​ntalMarker:horizo​​ntalMarkers){
double lower =((ValueAxis)getXAxis())。getLowerBound();
X lowerX = getXAxis()。toRealValue(lower);
double upper =((ValueAxis)getXAxis())。getUpperBound();
X upperX = getXAxis()。toRealValue(upper);
Line line =(Line)horizo​​ntalMarker.getNode();
line.setStartX(getXAxis()。getDisplayPosition(lowerX));
line.setEndX(getXAxis()。getDisplayPosition(upperX));
line.setStartY(getYAxis()。getDisplayPosition(horizo​​ntalMarker.getYValue()));
line.setEndY(line.getStartY());

}
}
}

一个片段测试图表(插入到oracle教程中的在线示例中):

  //实例化图表
NumberAxis xAxis =新的NumberAxis(0,10,1);
NumberAxis yAxis = new NumberAxis(-100,500,100);
ScatterXChart< Number,Number> sc = new ScatterXChart<>(xAxis,yAxis);
// ..填写一些数据
...
// ui添加/更改/删除值标记
数据<数字,数字> horizo​​ntalMarker = new Data<>(0,110);
按钮添加=新按钮(添加标记);
add.setOnAction(e - > sc.addHorizo​​ntalValueMarker(horizo​​ntalMarker));
Slider move = new Slider(yAxis.getLowerBound(),yAxis.getUpperBound(),0);
move.setShowTickLabels(true);
move.valueProperty()。bindBidirectional(horizo​​ntalMarker.YValueProperty());
按钮删除=新按钮(删除标记);
remove.setOnAction(e - > sc.removeHorizo​​ntalValueMarker(horizo​​ntalMarker));

附录:



虽然我不会'建议在相关问题中采用方法(将标记线添加到图表的父级并在外部管理其位置/长度) 可以在可调整大小的容器中使用它。让它发挥作用至关重要:




  • 听取图表的大小/位置变化并适当更新线

  • 在代码中将标记的托管属性设置为false



(updateShift是原始中计算yShift / lineX的部分) :

  Pane pane = new StackPane(chart); 
chart.widthProperty()。addListener(o - > updateShift(chart));
chart.heightProperty()。addListener(o - > updateShift(chart));
valueMarker.setManaged(false);
pane.getChildren()。add(valueMarker);


I have problems with adding a line at a defined position in JavaFX. The line has to be line a constant line as shown here: How to add a value marker to JavaFX chart?

My problem is, that my layout definition is a bit more complicated. Have a look:

The important part is the one on the top. I want to have the line on the y=60 line. The left part with the RadioBoxes is a VBox. The part with the (Scatter-)Chart is a StackPane (because I want it to fill the rest of the width). Inside this StackPane is the chart and a Group. The only child of the Group is the line.

I think the problem is, that the StackPane centers the Group with the line above the chart. But I can't get the combination of layouts which 1. stretches the chart 2. sets the Line above the chart 3. doesn't center the line

I tried lots of combinations but I just can't get it the way I want. Does anybody have an idea?!

解决方案

Unfortunately, ValueMarkers are not supported in XYCharts (that's probably the place in the hierarchy were it should be done) (Mis-)using data with constant value is a hack which might (or not) be acceptable/possible in some contexts.

A cleaner way out is a custom Chart that supports such markers. F.i. a custom ScatterChart like:

public class ScatterXChart<X, Y> extends ScatterChart<X, Y> {

    // data defining horizontal markers, xValues are ignored
    private ObservableList<Data<X, Y>> horizontalMarkers;

    public ScatterXChart(Axis<X> xAxis, Axis<Y> yAxis) {
        super(xAxis, yAxis);
        // a list that notifies on change of the yValue property
        horizontalMarkers = FXCollections.observableArrayList(d -> new Observable[] {d.YValueProperty()});
        // listen to list changes and re-plot
        horizontalMarkers.addListener((InvalidationListener)observable -> layoutPlotChildren());
    }

    /**
     * Add horizontal value marker. The marker's Y value is used to plot a
     * horizontal line across the plot area, its X value is ignored.
     * 
     * @param marker must not be null.
     */
    public void addHorizontalValueMarker(Data<X, Y> marker) {
        Objects.requireNonNull(marker, "the marker must not be null");
        if (horizontalMarkers.contains(marker)) return;
        Line line = new Line();
        marker.setNode(line );
        getPlotChildren().add(line);
        horizontalMarkers.add(marker);
    }

    /**
     * Remove horizontal value marker.
     * 
     * @param horizontalMarker must not be null
     */
    public void removeHorizontalValueMarker(Data<X, Y> marker) {
        Objects.requireNonNull(marker, "the marker must not be null");
        if (marker.getNode() != null) {
            getPlotChildren().remove(marker.getNode());
            marker.setNode(null);
        }
        horizontalMarkers.remove(marker);
    }

    /**
     * Overridden to layout the value markers.
     */
    @Override
    protected void layoutPlotChildren() {
        super.layoutPlotChildren();
        for (Data<X, Y> horizontalMarker : horizontalMarkers) {
            double lower = ((ValueAxis) getXAxis()).getLowerBound();
            X lowerX = getXAxis().toRealValue(lower);
            double upper = ((ValueAxis) getXAxis()).getUpperBound();
            X upperX = getXAxis().toRealValue(upper);
            Line line = (Line) horizontalMarker.getNode();
            line.setStartX(getXAxis().getDisplayPosition(lowerX));
            line.setEndX(getXAxis().getDisplayPosition(upperX));
            line.setStartY(getYAxis().getDisplayPosition(horizontalMarker.getYValue()));
            line.setEndY(line.getStartY());

        }
    }
}

A snippet testing the chart (f.i. insert into the online example in oracle's tutorial):

// instantiate chart
NumberAxis xAxis = new NumberAxis(0, 10, 1);
NumberAxis yAxis = new NumberAxis(-100, 500, 100);        
ScatterXChart<Number,Number> sc = new ScatterXChart<>(xAxis,yAxis);
// .. fill with some data
...
// ui to add/change/remove a value marker
Data<Number, Number> horizontalMarker = new Data<>(0, 110);
Button add = new Button("Add Marker");  
add.setOnAction(e -> sc.addHorizontalValueMarker(horizontalMarker));
Slider move = new Slider(yAxis.getLowerBound(), yAxis.getUpperBound(), 0);
move.setShowTickLabels(true);
move.valueProperty().bindBidirectional(horizontalMarker.YValueProperty());
Button remove = new Button("Remove Marker");
remove.setOnAction(e -> sc.removeHorizontalValueMarker(horizontalMarker));

Addendum:

While I wouldn't recommend the approach in the related question (adding the marker line to the chart's parent and managing its position/length externally) it is possible to use it in resizable containers. The crucial thingies to make it work:

  • listen to chart's size/location changes and update the line appropriately
  • set the marker's managed property to false

in code (updateShift is the part in the original that calculates the yShift/lineX):

Pane pane = new StackPane(chart);
chart.widthProperty().addListener(o -> updateShift(chart));
chart.heightProperty().addListener(o -> updateShift(chart));
valueMarker.setManaged(false);
pane.getChildren().add(valueMarker);

这篇关于在JavaFX图表中添加一行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆