JavaFX中如何按日期排序图表的X轴

5

如何按日期对我的折线图 X 轴进行排序?

现在我的折线图看起来像这样

未排序的 X 轴折线图

我尝试截取日期并将其转换为整数,但现在我不知道该怎么办了...

    datesToCompare.addAll(LastHoursAndDates.keySet()); // dates in String eg. 2015-12-25
    List<Integer> year = new ArrayList<Integer>(); // list for year after split
    List<Integer> month = new ArrayList<Integer>();// list for month after split
    List<Integer> days = new ArrayList<Integer>(); // list for days after split

    for(int i =0;i < LastHoursAndDates.size();i++)
    {
            sorting = datesToCompare.get(i).split("-");
            year.add(Integer.valueOf(sorting[0]));
            month.add(Integer.valueOf(sorting[1]));
            days.add(Integer.valueOf(sorting[2]));

                for(int j =0;j < LastHoursAndDates.size();j++)
                {
                    if(year.get(i) == year.get(j))
                    {
                        if(month.get(i) == month.get(j))
                        {
                            //???????
                        }
                    }
                }



}//for

也许有人遇到过相同的问题吗?

你可能正在错误的方式处理它,应该使用与你的系列数据相当的可比值(例如Instant),并使用TickLabelFormatter格式化日期。请注意我实际上还没有尝试过。 - jewelsea
可能有更简单的解决方案,例如使用类别轴,并按顺序添加日期字符串作为类别。请参见创建线图类别 - jewelsea
1
或者将系列中的数据设置为具有适当比较器的SortedList - James_D
@jewelsea 谢谢你,但我没有添加那个,这些日期来自DatePicker。我的应用程序用户在之前选择了它。 - Paweł Stanecki
1个回答

3

请注意,您使用的格式(YYYY-MM-DD)具有非常好的属性,即格式化字符串的字典顺序与日期的顺序相同。因此,在这里只需要按照 x 轴上的字符串顺序排序即可。我能想到的最简单的方法是使用Category轴,并为图表系列提供一个SortedList <Data<String,Number>>。然后,当您向底层列表中添加任何数据时,该数据在图表中保持正确的顺序。

以下是一个 SSCCE。在此示例中,对于任何给定日期,我只有一个值。如果不存在该日期的数据点,则addData(...)方法会创建一个新的数据点,否则将y值添加到现有数据点中。

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Random;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Spinner;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class LineChartWithDatesAsStrings extends Application {

    private DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE ;

    @Override
    public void start(Stage primaryStage) {
        CategoryAxis xAxis = new CategoryAxis();
        NumberAxis yAxis = new NumberAxis();

        ObservableList<Data<String, Number>> data = FXCollections.observableArrayList() ;
        SortedList<Data<String, Number>> sortedData = new SortedList<>(data, (data1, data2) -> 
                data1.getXValue().compareTo(data2.getXValue()));

        LineChart<String, Number> chart = new LineChart<>(xAxis, yAxis);
        chart.getData().add(new Series<>(sortedData));
        chart.setAnimated(false);

        final int dayRange = 60 ;
        LocalDate today = LocalDate.now() ;
        Random rng = new Random();

        for (int i = 0; i < 20 ; i++) {
            LocalDate date = today.minusDays(rng.nextInt(dayRange));
            String formattedDate = formatter.format(date);
            double value = rng.nextDouble() ;

            addData(data, formattedDate, value);
        }

        DatePicker datePicker = new DatePicker();
        Spinner<Double> valuePicker = new Spinner<>(0.0, 1.0, 0, 0.1);
        valuePicker.setEditable(true);

        Button addButton = new Button("Add");
        addButton.setOnAction(e -> addData(data, formatter.format(datePicker.getValue()), valuePicker.getValue()));

        HBox controls = new HBox(5, datePicker, valuePicker, addButton);
        controls.setAlignment(Pos.CENTER);
        controls.setPadding(new Insets(5));

        BorderPane root = new BorderPane(chart, null, null, controls, null);
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

    private void addData(ObservableList<Data<String, Number>> data, String formattedDate, double value) {
        Data<String, Number> dataAtDate = data.stream()
            .filter(d -> d.getXValue().equals(formattedDate))
            .findAny()
            .orElseGet(() -> {
                Data<String, Number> newData = new Data<String, Number>(formattedDate, 0.0);
                data.add(newData);
                return newData ;
            }) ;
        dataAtDate.setYValue(dataAtDate.getYValue().doubleValue() + value);
    }

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

如果您想使用没有很好的顺序保留属性的格式,只需要在比较器中将字符串解析为日期,并比较日期即可。例如,如果您有类似以下内容:

formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) ;

它将与之配合工作

SortedList<Data<String, Number>> sortedData = new SortedList<>(data, (data1, data2) -> {
    LocalDate date1 = LocalDate.parse(data1.getXValue(), formatter);
    LocalDate date2 = LocalDate.parse(data2.getXValue(), formatter);
    return date1.compareTo(date2);
});

这个问题的“正确”解决方法是把数据类型定义为LocalDate,即你会得到一个LineChart<LocalDate, Number>:然而,定义一个Axis<LocalDate>似乎需要做更多的工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接