简体   繁体   English

按字符串日期对 javaFX 中的 ObservableList 进行排序

[英]Sorting ObservableList in javaFX by String Date

I have a String containing a Date in the form "dd-MM-yyyy" I want the dates to be sorted when displayed in the table view.我有一个包含“dd-MM-yyyy”形式的日期的字符串,我希望在表格视图中显示日期时对日期进行排序。

Controller Class Controller Class

private final ObservableList<Stock>stockList = FXCollections.observableArrayList();
public class StocksController implements Initializable {
    @FXML
    private TableColumn<Stock, String> date;

    public void initialize(URL url, ResourceBundle resourceBundle){
        addToCSV.setOnAction(event -> {
            try {
                writeCSV();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        dollarButton.setOnAction(event -> {changeDollar();});
        percentButton.setOnAction(event -> {changePercent();});
        price.setCellValueFactory(new PropertyValueFactory<>("price"));
        date.setCellValueFactory(new PropertyValueFactory<>("dateRN"));
        checker.setCellValueFactory(new PropertyValueFactory<>("checker"));
        stockView.setItems(stockList);
        search();
        readCSV();
    }
    public void writeCSV() throws IOException {
        String stockNames = ("$" + stockName.getText()).trim().toUpperCase();
        Double stockPrices = Double.parseDouble((stockPrice.getText()).trim());
        String stockDates = stockDate.getValue().format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
        FileWriter fw = new FileWriter("src/stocks.csv",true);
        BufferedWriter bw = new BufferedWriter(fw);
        PrintWriter pw = new PrintWriter(bw);
        pw.println(stockNames + ',' + stockDates + ',' + stockPrices);
        pw.flush();
        pw.close();
        stockList.clear();
        readCSV();
    }
    public void readCSV(){
        String csv;
        csv = "src/stocks.csv";
        String delimiter = ",";
        try{
            System.out.println(new FileReader(csv));
            BufferedReader br = new BufferedReader(new FileReader(csv));
            String line;
            while((line = br.readLine()) != null) {
                String[] values = line.split(delimiter);
                String stockNames = values[0];
                String stockDates = values[1];
                Double stockPrices = Double.parseDouble(values[2]);
                Stock stock = new Stock(stockNames, stockPrices,stockDates);
                stockList.add(stock);
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(StocksController.class.getName())
                    .log(Level.FINE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(StocksController.class.getName())
                    .log(Level.FINE, null, ex);
        }
    }
}

Stock Class现货 Class

public class Stock {
    private String dateRN;

    public Stock(){
        this.dateRN = "";
    }
    public Stock(String ticker, Double price, String dateRN){
        this.ticker = ticker;
        this.price = price;
        this.dateRN = dateRN;
        this.checker = new CheckBox();
    }
    public String getDateRN() {
        return dateRN;
    }
    public void setDateRN(String dateRN) {
        this.dateRN = dateRN;
    }
}

I've tried creating a comparator for Stock but it's not working so I gave up on that method is there anyway I could just create a function inside my controller sorting it directly from the observableList itself?我尝试为 Stock 创建一个比较器,但它不起作用所以我放弃了该方法无论如何我可以在我的 controller 中创建一个 function 直接从 observableList 本身排序? or even when I read in the CSV?甚至当我阅读 CSV 时?

这就是现在的样子,但是,我希望对日期进行排序

The application class, StockTable应用 class, StockTable

import java.net.URL;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class StockTable extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = getClass().getResource("stoktabl.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        BorderPane root = loader.load();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

The FXML file, stoktabl.fxml FXML 文件stoktabl.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="jfxtests.StocksController">
    <center>
        <TableView fx:id="table">
            <columns>
                <TableColumn fx:id="dateColumn" text="Date">
                </TableColumn>
                <TableColumn fx:id="priceColumn" text="Price">
                </TableColumn>
                <TableColumn fx:id="checkBoxColumn" text="Check Box">
                </TableColumn>
            </columns>
        </TableView>
    </center>
</BorderPane>

FXML controller class, StocksController FXML controller class, StocksController

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.StringConverter;

public class StocksController extends StringConverter<LocalDate> {
    private static final DateTimeFormatter  FORMATTER = DateTimeFormatter.ofPattern("dd-MM-yyyy", Locale.ENGLISH);
    private static final String  DELIMITER = ",";

    @FXML
    TableColumn<Stock, Boolean>  checkBoxColumn;

    @FXML
    TableColumn<Stock, LocalDate>  dateColumn;

    @FXML
    TableColumn<Stock, BigDecimal>  priceColumn;

    @FXML
    TableView<Stock>  table;

    @Override // javafx.util.StringConverter
    public String toString(LocalDate object) {
        return object.format(FORMATTER);
    }

    @Override // javafx.util.StringConverter
    public LocalDate fromString(String string) {
        return LocalDate.parse(string, FORMATTER);
    }

    @FXML
    private void initialize() {
        table.setEditable(true);
        checkBoxColumn.setCellValueFactory(f -> f.getValue().checkedProperty());
        checkBoxColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkBoxColumn));
        dateColumn.setCellValueFactory(f -> f.getValue().dateProperty());
        dateColumn.setCellFactory(TextFieldTableCell.forTableColumn(this));
        priceColumn.setCellValueFactory(f -> f.getValue().priceProperty());
        ObservableList<Stock> items = FXCollections.observableArrayList();
        InputStream is = getClass().getResourceAsStream("stocks.csv");
        try (InputStreamReader isr = new InputStreamReader(is);
             BufferedReader br = new BufferedReader(isr)) {
            String line = br.readLine();
            while (line != null) {
                String[] fields = line.split(DELIMITER);
                BigDecimal price = new BigDecimal(fields[2]);
                LocalDate date = LocalDate.parse(fields[1], FORMATTER);
                String name = fields[0];
                Stock stock = new Stock(price, date, name);
                items.add(stock);
                line = br.readLine();
            }
            items = items.sorted();
            table.setItems(items);
        }
        catch (IOException xIo) {
            throw new RuntimeException(xIo);
        }
    }
}

The "model" class, Stock “型号”class, Stock

import java.math.BigDecimal;
import java.time.LocalDate;

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;

public class Stock implements Comparable<Stock> {
    private SimpleObjectProperty<BigDecimal>  price;
    private SimpleBooleanProperty  checked;
    private SimpleObjectProperty<LocalDate>  date;
    private SimpleStringProperty  name;

    public Stock(BigDecimal price, LocalDate date, String name) {
        this.price = new SimpleObjectProperty<BigDecimal>(this, "price", price);
        this.date = new SimpleObjectProperty<LocalDate>(this, "date", date);
        this.name = new SimpleStringProperty(this, "name", name);
        checked = new SimpleBooleanProperty(false);
    }

    public SimpleBooleanProperty checkedProperty() {
        return checked;
    }

    public boolean getChecked() {
        return checked.get();
    }

    public void setChecked(boolean check) {
        checked.set(check);
    }

    public SimpleObjectProperty<LocalDate> dateProperty() {
        return date;
    }

    public LocalDate getDate() {
        return date.get();
    }

    public void setDate(LocalDate ld) {
        date.set(ld);
    }

    public SimpleStringProperty nameProperty() {
        return name;
    }

    public String getName() {
        return name.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public SimpleObjectProperty<BigDecimal> priceProperty() {
        return price;
    }

    public BigDecimal getPrice() {
        return price.get();
    }

    public void setPrice(BigDecimal cost) {
        price.set(cost);
    }

    @Override
    public int compareTo(Stock o) {
        int result;
        if (o == null) {
            result = 1;
        }
        else {
            if (o.date == null) {
                result = 1;
            }
            else {
                if (o.date.get() == null) {
                    result = 1;
                }
                else {
                    if (date == null) {
                        result = -1;
                    }
                    else {
                        if (date.get() == null) {
                            result = -1;
                        }
                        else {
                            if (date.get().isBefore(o.date.get())) {
                                result = -1;
                            }
                            else if (date.get().isAfter(o.date.get())) {
                                result = 1;
                            }
                            else {
                                result = 0;
                            }
                        }
                    }
                }
            }
        }
        return result;
    }
}

The CSV file, stocks.csv CSV 文件, stocks.csv

first,05-08-2022,1.22
second,03-08-2022,1.35
third,07-08-2022,67.0
last,06-08-2022,4.5
  • Class Stock implements comparable to allow items to be sorted by natural order . Class Stock实现了比较允许items自然顺序排序 Refer to [default] method sorted in interface javafx.collections.ObservableList .参考在接口javafx.collections.ObservableListsorted的 [default] 方法。
  • The TableView is made editable so as to allow selecting the check boxes. TableView是可编辑的,以便允许选择复选框。
  • Values are read from the CSV file and converted to the appropriate type in order to create Stock instances.从 CSV 文件中读取值并转换为适当的类型以创建Stock实例。
  • A StringConverter is used to both format and parse values in the date column. StringConverter用于格式化和解析date列中的值。
  • Files stoktabl.fxml and stocks.csv are located in the same folder as file StockTable.class文件stoktabl.fxmlstocks.csv与文件StockTable.class位于同一文件夹中
  • All above [Java] classes are in the same package (even though I omitted the package statements from the above code.以上所有 [Java] 类都在同一个 package 中(尽管我从上面的代码中省略了 package 语句。
  • Controller classes no longer need to implement interface javafx.fxml.Initializable . Controller 类不再需要实现接口javafx.fxml.Initializable They may, optionally, declare method initialize that takes no parameters and returns void .他们可以选择声明不带参数并返回void的方法initialize

Here is a screen capture:这是一个屏幕截图:

屏幕截图

@Abra has shown how to effect sorting by implementing Comparable<Stock> , and @Dan has shown how to leverage LocalDate::compareTo , provided by its implementation of Comparable<ChronoLocalDate> . @Abra展示了如何通过实现Comparable<Stock>来实现排序,@Dan展示了如何利用由其实现Comparable<ChronoLocalDate>提供的LocalDate::compareTo The example below focuses on the benefit of using LocalDate directly in your model.下面的示例侧重于在 model 中直接使用LocalDate的好处。 In particular, you can simply add() the date column to the sort order.特别是,您可以简单地将日期列add()到排序顺序。 As shown in the TableView API, a simple binding can restore the original, unsorted order.TableView API 所示,简单的绑定可以恢复原始的、未排序的顺序。

日期排序

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.transformation.SortedList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 * @see https://stackoverflow.com/a/72437984/230513
 * @see https://stackoverflow.com/a/72437984/230513
 */
public class LocalDateTest extends Application {

    private record Model(LocalDate date, String name) {}

    @Override
    public void start(Stage stage) {
        stage.setTitle(this.getClass().getName());
        var model = FXCollections.observableArrayList(
            new Model(LocalDate.now().minusDays(1),"Yesterday"),
            new Model(LocalDate.now(), "Today"),
            new Model(LocalDate.now().plusDays(1), "Tommorrow"),
            new Model(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()), "Deadline")
        );
        SortedList sortedList = new SortedList(model);
        var table = new TableView(sortedList);
        sortedList.comparatorProperty().bind(table.comparatorProperty());

        TableColumn<Model, LocalDate> date = new TableColumn<>("Date");
        date.setCellValueFactory(cd -> new SimpleObjectProperty(cd.getValue().date()));
        table.getColumns().add(date);
        table.getSortOrder().add(date);

        TableColumn<Model, String> name = new TableColumn<>("Name");
        name.setCellValueFactory(cd -> new SimpleStringProperty(cd.getValue().name()));
        table.getColumns().add(name);

        StackPane root = new StackPane(table);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

As it has been pointed out in the comments, I second @dan1st's suggestion of using a LocalDate instead of a plain String to represent a date and order your Stock instances according to their dateRN .正如评论中所指出的,我赞同@dan1st 的建议,即使用LocalDate而不是普通的String来表示日期并根据它们的dateRN对您的Stock实例进行排序。

However, If for some reason you're actually bound to a String implementation, then you could resort to a LocalDate conversion within the compareTo definition and still implement the Comparable interface in your Stock class.但是,如果由于某种原因您实际上绑定到String实现,那么您可以在compareTo定义中使用LocalDate转换,并且仍然在Stock class 中实现Comparable接口。

Here is a rough implementation:这是一个粗略的实现:

public class Stock implements Comparable<Stock> {
    private String ticker;
    private Double price;
    private String dateRN;

    public Stock() {
        this.dateRN = "";
    }

    public Stock(String ticker, Double price, String dateRN) {
        this.ticker = ticker;
        this.price = price;
        this.dateRN = dateRN;
    }

    //... getters, setters & rest of class implementation

    @Override
    public int compareTo(Stock o) {
        return LocalDate.parse(dateRN, DateTimeFormatter.ofPattern("dd-MM-yyyy")).compareTo(LocalDate.parse(o.dateRN, DateTimeFormatter.ofPattern("dd-MM-yyyy")));
    }

    @Override
    public String toString() {
        return "Stock{dateRN='" + dateRN + '\'' +'}';
    }
}
public class Main {
    public static void main(String[] args) {
        List<Stock> list = new ArrayList<>(List.of(
                new Stock("test1", 1.5, "05-08-2022"),
                new Stock("test2", 2.5, "03-08-2022"),
                new Stock("test3", 3.5, "07-08-2022"),
                new Stock("test4", 4.5, "06-08-2022")
        ));

        System.out.println(list);

        Collections.sort(list);
        System.out.println("\n" + list);
    }
}

Here is a link to test the code above:这是测试上述代码的链接:

https://www.jdoodle.com/iembed/v0/tLI https://www.jdoodle.com/iembed/v0/tLI

Output Output

Original and sorted output based on the sample data of your question.根据您问题的样本数据,原始并排序 output。

[Stock{dateRN='05-08-2022'}, Stock{dateRN='03-08-2022'}, Stock{dateRN='07-08-2022'}, Stock{dateRN='06-08-2022'}]

[Stock{dateRN='03-08-2022'}, Stock{dateRN='05-08-2022'}, Stock{dateRN='06-08-2022'}, Stock{dateRN='07-08-2022'}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM