JavaFX LineChart / AreaChart在x轴上的分类值错误(CategoryAxis)

时间:2013-10-09 06:43:32

标签: java sorting charts javafx linechart

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

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

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

示例:

执行 StartDate:01.09.2013 EndDate:25.09.2013 间隔:周

x轴上的值:

01.09.2013 / 08.09.2013 / 15.09.2013 / 22.09.2013

点击" Days" 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,负责根据x值按字母顺序对dataPoints进行排序:

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轴中被错误排序,以防它们已经出现在旧图表中?

非常感谢帮助解决这个&#34;问题&#34;。

7 个答案:

答案 0 :(得分:4)

我遇到了同样的问题。无论我如何设置绘图,通过使用新的数据点重新绘制,排序总是会搞砸。唯一的解决方法是使用java 1.8。

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

答案 1 :(得分:3)

我只是想说,我用折线图得到(几乎)相同的问题!

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

当我为“user1”加载数据时,一切看起来都很好: Chart 1 with clear data.

但是当我用第二个用户加载数据时,它看起来像这样: Chart 2 with strange data.

我真的无法弄清楚为什么会这样,因为我在从数据库中选择数据时对数据进行排序:

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

所以...这就是我所能说的......我在从数据库中获取数据时对数据进行排序,对于一个用户来说它是有效的,对于另一个用户它没有!它真的吓坏了我!

我刚发现任何人建议下载JRE和JDK 8的“早期访问版本”的帖子 - 但在我看来,我不建议这样做是为了一个高效的系统!

希望有人能解决这个令人讨厌的问题!

在任何人愤怒之前,这不是答案 - 我知道!

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

由于(有时奇怪的stackoverflow权限偏好)我不能发表评论,直到我获得更高的声誉值!

我不想问一个新问题,而这已经在这里讨论了!

希望这没关系...

迎接

答案 2 :(得分:2)

我遇到类似的问题,线图试图添加日期点,而在调试时我想出了一些东西:

我在ObservableList中添加了一个Series-Object,其中包含3个日期点:2014年1月1日,2014年1月2日,2014年2月2日,以及Y轴。

(到目前为止一直很好,折线图显示应该如此)。

之后我添加了第二个带日期点的系列对象:2014年1月1日,2014年1月2日,2014年3月3日。

再次:折线图有效。

如果我首先添加第二个 Series-Object,我的线图将显示如下的X轴值:
01.01.2014
2014年1月2日
03.02.2014
02.02.2014
它以错误的顺序破坏我的线图。

在尝试寻找解决方案的过程中,我找到了一个解决方法&#39;:
添加包含所有日期值的系列对象,这些日期值稍后在其他Series对象上用作Observable List中的第一个Series-Object。 它可以隐藏&#39; as&#34;平均价值&#34;或者这样但是  我对它并不是很满意,但它至少可以按照正确的顺序显示图表

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

答案 3 :(得分:0)

有关不一致呈现的错误https://javafx-jira.kenai.com/browse/RT-26321。此错误对LineChart具有相同的效果。

答案 4 :(得分:0)

我发现了一个“临时”解决方案。

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

添加i变量,该变量随数据增加,可防止数据被破坏。

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

答案 5 :(得分:0)

这是一种解决方法,每次都重新创建整个图形,它不是最干净的方式,但它可以工作。在将系列添加到图表之前,只需调用即可 graph.getData().clear()然后graph.layout();得到了答案 Recreate bar chart without it remembering data

答案 6 :(得分:0)

我在使用 JDK 14 的 JavaFX 11 中遇到了同样的问题。它是一个 LineChart,其中 CategoryAxisxAxis,其中填充了可排序格式的日期 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 上明确设置类别(字符串格式的日期列表)。