【问题标题】:Scrolling XYChart with JavaFX使用 JavaFX 滚动 XYChart
【发布时间】:2014-08-19 12:34:34
【问题描述】:

我正在尝试实现类似于 Windows 中的网络使用情况图。我正在显示最后 30 秒的数据,新值出现在右侧并移动到左侧。

除了无法让垂直网格线随数据移动之外,我已经完成了所有工作。相反,主要刻度始终保持静止,而图表上的唯一一条线移动。

即在下图中,我希望主要刻度保持在 5 的刻度单位并显示在 55 和 60。65 处的标签将不在屏幕上。

  • 我已禁用 autoRanging,并手动更新轴上的 lowerBound 和 upperBound 属性。
  • 启用 autoRanging 后,您将获得 snake 样式效果,其中线条向 y 轴移动几秒钟,然后跳跃一个主要刻度距离,依此类推。
  • 我还尝试在图表和轴上启用动画属性。

我正在寻找任何基于现有内容的解决方案/解决方法,而不必从头开始编写。是否可以通过子类化来自定义行为?

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class StackOverflow25383566 extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        final NumberAxis xAxis = new NumberAxis();
        xAxis.setAutoRanging(false);
        xAxis.setForceZeroInRange(false);
        xAxis.setTickUnit(5);

        final NumberAxis yAxis = new NumberAxis(0, 15, 1);
        yAxis.setAutoRanging(false);

        final LineChart<Number, Number> chart = new LineChart<Number, Number>(
                xAxis, yAxis);
        chart.setAnimated(false);
        chart.setCreateSymbols(false);
        chart.setLegendVisible(false);
        final XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
        chart.getData().add(series);
        series.getData().addListener(
                new ListChangeListener<Data<Number, Number>>() {

                    @Override
                    public void onChanged(
                            ListChangeListener.Change<? extends Data<Number, Number>> arg0) {
                        ObservableList<Data<Number, Number>> data = series
                                .getData();
                        xAxis.setLowerBound(data.get(0).getXValue()
                                .doubleValue());
                        xAxis.setUpperBound(data.get(data.size() - 1)
                                .getXValue().doubleValue());
                    }

                });
        stage.setScene(new Scene(new BorderPane(chart), 800, 600));
        stage.show();

        final Runnable update = new Runnable() {
            private int clock;

            @Override
            public void run() {
                try {
                    ObservableList<Data<Number, Number>> data = series
                            .getData();
                    if (data.size() > 10) {
                        data.remove(0);
                    }
                    data.add(new XYChart.Data<Number, Number>(clock, clock % 13));
                    clock++;
                } catch (Throwable e) {
                    // executors silently swallow exceptions
                    e.printStackTrace();
                    throw e;
                }
            }

        };
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                new Runnable() {

                    @Override
                    public void run() {
                        Platform.runLater(update);
                    }

                }, 0, 500, TimeUnit.MILLISECONDS);
    }

    public static void main(String[] args) {
        launch(args);
    }
}

【问题讨论】:

  • 听起来您未显示的代码有问题 ;-) 只需在末尾添加数据项并在开头删除项目(自动量程开启和 forceZero 关闭)应该可以工作(并且通常可以)
  • 我很抱歉没有发布代码,我在一个未连接到外部世界的系统上工作。如果您想亲自查看,只需添加一个 SSCCE。我希望 X 轴刻度保持在 5、10、15、20 等间隔,而不是刻度线保持静态,标签只是改变,即下一步是 6、11、16、21 .. .
  • ahhh .. 误解了您的问题 - 感谢您的澄清 :-)

标签: javafx javafx-8


【解决方案1】:

我能够通过子类化 ValueAxis 来解决这个问题,以便它返回的刻度标记始终是刻度单元的倍数。

  • 自从开发此解决方案以来,我就知道 jfxutils 包含一个名为 StableTickAxis 的类,但它的 JavaDoc 声明“尚未准备好使用”

这张照片显示了我所追求的,即不一定从一个主要的勾号开始。

private class SpecialAxis extends ValueAxis<Number> {
    private SimpleObjectProperty<Double> tickUnitProperty = new SimpleObjectProperty<Double>(
            5d);

    @Override
    protected List<Number> calculateMinorTickMarks() {
        List<Number> ticks = new ArrayList<Number>();
        double tickUnit = tickUnitProperty.get() / getMinorTickCount();
        double start = Math.floor(getLowerBound() / tickUnit) * tickUnit;
        for (double value = start; value < getUpperBound(); value += tickUnit) {
            ticks.add(value);
        }
        return ticks;
    }

    @Override
    protected List<Number> calculateTickValues(double arg0, Object arg1) {
        List<Number> ticks = new ArrayList<Number>();
        double tickUnit = tickUnitProperty.get();
        double start = Math.floor(getLowerBound() / tickUnit) * tickUnit;
        for (double value = start; value < getUpperBound(); value += tickUnit) {
            ticks.add(value);
        }
        return ticks;
    }

    @Override
    protected Object getRange() {
        // not sure how this is used??
        return null;
    }

    @Override
    protected String getTickMarkLabel(Number label) {
        return label.toString();
    }

    @Override
    protected void setRange(Object range, boolean arg1) {
        // not sure how this is used??
    }

    public SimpleObjectProperty<Double> getTickUnitProperty() {
        return tickUnitProperty;
    }

    public void setTickUnit(double tickUnit) {
        tickUnitProperty.set(tickUnit);
    }

}

【讨论】:

    【解决方案2】:

    要获得网格也移动的移动图表,您可以尝试使用LineChart&lt;Number,Number&gt;。对于 x 轴,使用 NumberAxis,并设置适当的刻度标签格式化程序。由于您将删除数据,只需将 x 轴中的 forceZeroInRange 选项设置为 false,因为不应绘制较旧的时间值。这必须在 autoRanging 开启的情况下完成。

    使用类似这样的代码 sn-p:

    NumberAxis xAxis = new NumberAxis();
    xAxis.setAutoRanging(true);
    xAxis.setForceZeroInRange(false);
    xAxis.setTickLabelFormatter(new StringConverter<Number>(){
        @Override
        public String toString(Number t) {
            return new SimpleDateFormat("HH:mm:ss").format(new Date(t.longValue()));
        }
        @Override
        public Number fromString(String string) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    });
    
    NumberAxis yAxis = new NumberAxis();
    LineChart<Number,Number> chart = new LineChart<>(xAxis,yAxis);
    

    每当左侧的第一张主要标签消失时,您会看到垂直主要刻度从右向左移动。

    【讨论】:

    • 感谢您的回答,但这并不能解决问题。如果你想运行它,我已经用示例代码更新了我的问题......
    • 感谢示例代码。现在我看到您在以连续方式移动主要刻度之后。使用常规解决方案,它们仅在主要刻度离开图表时移动。由于图表上的数据很少,它们确实会移动,但经过一段时间后,唯一移动的是标签。子类的工作很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多