5

尝试构建包含可更新的应用程序LineChartAreaChart我发现奇怪的行为时 - 可能是由于应用程序逻辑中的错误?

目标是在单击“生成”按钮时将数据填充到图表中或更新图表。用户必须输入图表的开始时间和结束时间,此外还必须选择间隔小时/天/周(通过使用 a RadioGroup)。

初始图表的创建没有任何问题。重新生成图表也可以正常工作,但前提是之前的图表中不存在数据点。如果两个图表(旧的和更新的)中都包含具有相同 x 值的数据点,则排序不再是升序。

例子:

执行开始日期:01.09.2013 结束日期:25.09.2013 间隔:周

x 轴上的值:

01.09.2013 / 08.09.2013 / 15.09.2013 / 22.09.2013

单击“天”RadioButton重新生成图表会在 x 轴上产生以下值:

01.09.2013 / 08.09.2013 / 15.09.2013 / 22.09.2013 / 02.09.2013 / 03.09.2013 / 04.09.2013 / ...

(值应为 01.09.2013 / 02.09.2013 / 03.09.2013 / ...)

所有已经显示在第一个图表中并且也在第二个图表中的值,在新图表的开头排序(而不是按升序排列)

这是实现技巧的代码(方法代码initializeTimeline仅用于测试目的(肯定有点可优化;))):

public class ChartDesignController implements Initializable {
@FXML
private AreaChart chartOne;
@FXML
private TextField startDate;
@FXML
private TextField endDate;
@FXML
private RadioButton radioHours;
@FXML
private RadioButton radioDays;
@FXML
private RadioButton radioWeeks;
@FXML
private ToggleGroup timeUnit;
@FXML
private Label msgBox;

@FXML
private void generateGraph(ActionEvent event) {
    String timeUnit = getTimeUnit();
    Series s = initializeTimeline(startDate.getText(), endDate.getText(), timeUnit);
    chartOne.getData().setAll(s);
}

private String getTimeUnit() {
    String timeUnitForQuery = "DD";
    RadioButton selectedRadio = (RadioButton) timeUnit.getSelectedToggle();
    if (selectedRadio == radioHours) {
        //System.out.println("RadioHours was selected");
        timeUnitForQuery = "HH24";
    }
    if (selectedRadio == radioDays) {
        //System.out.println("RadioDays was selected");
        timeUnitForQuery = "DD";
    }
    if (selectedRadio == radioWeeks) {
        //System.out.println("RadioWeeks was selected");
        timeUnitForQuery = "IW";
    }
    //System.out.println("Time unit changed");
    return timeUnitForQuery;
}

@Override
public void initialize(URL url, ResourceBundle rb) {
}

private Series initializeTimeline(String startTime, String endTime, String timeUnit) {
    msgBox.setText("");
    long delta;
    int nrOfTicks = 0;
    Data<String, Integer> dp;
    Series s = new Series();
    ArrayList<Data<String, Integer>> dataPoints = new ArrayList();
    SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
    Date startDate = new Date();
    Date endDate = new Date();
    GregorianCalendar startTimeGC = new GregorianCalendar();
    GregorianCalendar endTimeGC = new GregorianCalendar();
    if (timeUnit.equalsIgnoreCase("HH24")) {
        sdf = new SimpleDateFormat("dd.MM.yyyy HH");
    }
    try {
        startDate = sdf.parse(startTime);
        endDate = sdf.parse(endTime);
    } catch (ParseException ex) {
        msgBox.setText(ex.getMessage() + "\n" + "Format expected: " + sdf.toPattern());
    }
    startTimeGC.setTimeInMillis(startDate.getTime());
    endTimeGC.setTimeInMillis(endDate.getTime());
    delta = endTimeGC.getTimeInMillis() - startTimeGC.getTimeInMillis();
    if (timeUnit.equalsIgnoreCase("HH24")) {
        nrOfTicks = (int) (delta / 1000 / 60 / 60) + 1;
    } else if (timeUnit.equalsIgnoreCase("DD")) {
        nrOfTicks = (int) (delta / 1000 / 60 / 60 / 24) + 1;
    } else if (timeUnit.equalsIgnoreCase("IW")) {
        nrOfTicks = (int) (delta / 1000 / 60 / 60 / 24 / 7) + 1;
    }
    for (int i = 0; i < nrOfTicks; i++) {
        dp = new Data(sdf.format(startTimeGC.getTime()), 0);
        dataPoints.add(dp);
        if (timeUnit.equalsIgnoreCase("HH24")) {
            startTimeGC.add(GregorianCalendar.HOUR_OF_DAY, 1);
        } else if (timeUnit.equalsIgnoreCase("DD")) {
            startTimeGC.add(GregorianCalendar.DAY_OF_MONTH, 1);
        } else if (timeUnit.equalsIgnoreCase("IW")) {
            startTimeGC.add(GregorianCalendar.WEEK_OF_YEAR, 1);;
        }
    }
    dataPoints.sort(new DataComparator());
    s.setData(FXCollections.observableList(dataPoints));
    return s;
 }
}

下面,DataComparator负责dataPoints根据 x 值按字母顺序对它们进行排序:

public class DataComparator implements Comparator<Data> {
@Override
public int compare(Data d1, Data d2) {
    String d1Xval = (String) d1.getXValue();
    String d2Xval = (String) d2.getXValue();
    return d1Xval.compareTo(d2Xval);
    }
}

调试时,程序值在ArrayList dataPoints.

任何想法为什么值在图表 x 轴中排序不正确,以防它们已经出现在旧图表中?

非常感谢对这个“问题”的帮助。

4

6 回答 6

4

我有同样的问题。无论我如何设置绘图,排序总是会因为重新绘制新的数据点而变得混乱。唯一的修复是使用 java 1.8。

这是当时最新的 fx 2.2.51-b13。必须使用 fx8 早期访问版本。

于 2014-02-05T18:31:54.103 回答
3

我只是想说,我在折线图上遇到了(几乎)同样的问题!

我从数据库中加载一些数据以显示在折线图中。数据仅代表一个月的总和。

当我为“user1”加载数据时,一切看起来都很好: 图表1,数据清晰。

但是当我用我的第二个用户加载数据时,它看起来像这样: 带有奇怪数据的图表 2。

我真的不知道为什么会这样,因为我在从数据库中选择时对数据进行排序,如下所示:

...NTING.CUSTOMER = '"+Customer+"' GROUP BY MONTH ORDER BY MONTH ASC;

所以......这就是我能说的......我在从数据库中获取数据的同时订购数据,对于一个用户它可以工作,对于另一个用户它不!它真的把我吓坏了!

我刚刚在任何地方发现有人建议下载 JRE 和 JDK 8 的“早期访问版本”的帖子 - 但在我看来,我不建议将其用于生产系统!

希望任何人都能解决这个讨厌的问题!

就在有人对此大发雷霆之前,这不是答案——我知道!

但我认为收集尽可能多的信息似乎也是解决方案的一部分。

而且由于(有时奇怪的 stackoverflow 特权偏好),在我获得更高的声誉值之前,我无法发表评论!

我不想问一个新问题,虽然这里已经讨论过了!

希望这没问题……</p>

问候

于 2013-11-09T18:42:49.923 回答
3

我在尝试添加日期点的折线图时遇到了类似的问题,在调试时我发现了一些东西:

我在 ObservableList 中添加了一个 Series-Object,其中包含 3 个日期点:01.01.2014、02.01.2014、02.02.2014
和一些双倍的 y 轴。


(到目前为止一切顺利,折线图按应有的方式显示)。

在此之后,我添加了第二个带有日期点的系列对象:
01.01.2014、02.01.2014、03.02.2014 。

再次:折线图有效。

如果我首先添加第二个系列对象,我的折线图将显示 X 轴值如下:
01.01.2014
02.01.2014
03.02.2014
02.02.2014
以错误的顺序破坏了我的折线图。

在我试图找到解决方案的路上,我想出了一个“解决方法”:
添加一个包含所有日期值的系列对象,这些日期值稍后用于其他系列对象作为可观察列表中的第一个系列对象。
它可以“隐藏”为“平均值”等,但我对此并不满意,但它至少可以以正确的顺序显示图表

顺便说一句,我在这里的第一篇文章只是试图帮助至少找到一个解决方案,因为我来到这里寻找同样的问题。

于 2014-08-28T07:51:10.917 回答
1

我在使用 JDK 14 的 JavaFX 11 中遇到了同样的问题。它是一个LineChartwith CategoryAxis,因为xAxis它填充了可排序格式的日期yyyy-MM-dd HH:mm:ss,但是在添加多个系列时它们没有自动排序。

我发现由于某种原因无法识别类别(类别为空):

    CategoryAxis xAxis = (CategoryAxis) lineChart.getXAxis();
    ObservableList<String> categories = xAxis.getCategories();

我仍然不确定它开箱即用无法正常工作的根本原因是什么,但这就是我想出的解决方案:

    lineChart.setData(chartSeriesList);
    CategoryAxis xAxis = (CategoryAxis) lineChart.getXAxis();
    ObservableList<String> categories = FXCollections.observableArrayList(allDates);
    Collections.sort(categories);
    xAxis.setAutoRanging(true);
    xAxis.setCategories(categories);

基本上在xAxis.

于 2021-02-18T08:22:44.437 回答
1

这是一种解决方法,每次都重新创建整个图表,这不是最干净的方法,但它可以工作。在将系列添加到图表之前,只需调用 graph.getData().clear()然后,从重新创建条形图graph.layout();得到答案 而不记住数据

于 2016-06-22T22:34:08.820 回答
0

我找到了一个关于这个的“临时”解决方案。

series.getData().add(new BarChart.Data<>(rs.getString(1)+" "+i,rs.getDouble(2)));

添加一个i随数据增加的变量可防止数据被破坏。

另一种解决方案是使用 JDK 8。

于 2014-08-28T08:50:59.767 回答