Получение LocalDate для отображения в табличном представлении в Javafx - PullRequest
0 голосов
/ 08 мая 2018

Я работал над частью моего приложения, которое позволяет пользователю вводить выходные дни, которые его школа снимает, и сохраняет их в файл. Название праздника и дата (хранящиеся в виде объекта LocalDate) считываются из файла, сохраняются в объекте праздника и помещаются в наблюдаемый массив.

Это главный контроллер:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Optional;


public class Controller {
    @FXML
    private BorderPane mainGridPane;
    private ArrayList<Job> jobs = new ArrayList<>();
    private static XSSFRow row;
    private boolean fileClosed = false;
    @FXML
    private DatePicker employeeStartDate;
    @FXML
    private Label evaluation40;
    @FXML
    private Label evaluation80;
    @FXML
    private Label evaluation120;
    @FXML
    private DatePicker summerStart;
    @FXML
    private DatePicker summerEnd;
    @FXML
    private DatePicker fallStart;
    @FXML
    private DatePicker fallEnd;
    @FXML
    private TableView<Holiday> tableView;

    private HolidayData data;

    public void initialize() throws IOException {
        data = new HolidayData();
        data.loadHolidays();
        tableView.setItems(data.getHolidays());

    }

Это праздник класса:

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import java.time.LocalDate;

public class Holiday {
    private SimpleStringProperty name = new SimpleStringProperty();
    private ObjectProperty<LocalDate> date;


    public Holiday(String name, LocalDate date) {
        this.name.set(name);
        this.date = new SimpleObjectProperty<>(date);
    }

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

    public SimpleStringProperty nameProperty() {
        return name;
    }

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

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

Это класс HolidayData, который отвечает за чтение данных в файлы и из файлов, а также за создание и наблюдение за массивом:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;

public class HolidayData {
     private static HolidayData instance = new HolidayData();
     private static String fileName = "schoolHolidays.txt";
     private DateTimeFormatter formatter;

     private ObservableList<Holiday> holidays;

     public HolidayData(){
         formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
     }

    public static HolidayData getInstance() {
        return instance;
    }

    public static String getFileName() {
        return fileName;
    }

    public ObservableList<Holiday> getHolidays(){
         return holidays;
    }

    public void addHoliday(Holiday holiday){
         holidays.add(holiday);
    }


    public void loadHolidays() throws IOException {
        holidays = FXCollections.observableArrayList();
        Path path = Paths.get(fileName);
        BufferedReader br = Files.newBufferedReader(path);

        String input;
        try{
            while((input = br.readLine()) != null){
                String[] holidayPieces = input.split("\t");

                String name = holidayPieces[0];
                String dateString =  holidayPieces[1];

                LocalDate date = LocalDate.parse(dateString, formatter);
                Holiday holiday = new Holiday(name, date);
                holidays.add(holiday);
            }
        }catch(IOException e) {
            e.printStackTrace();
        } finally{
            if(br != null){
                br.close();
            }
        }
    }

    public void storeHolidays()throws IOException{
        Path path = Paths.get(fileName);
        BufferedWriter bw = Files.newBufferedWriter(path);
        try {
            Iterator<Holiday> iter = holidays.iterator();
            while(iter.hasNext()){
                Holiday holiday = iter.next();
                bw.write(String.format("%s\t%s", holiday.getName(), holiday.getDate().format(formatter)));
                bw.newLine();
            }
        }finally {
            if (bw != null){
                bw.close();
            }

        }
    }

    public void deleteHoliday(Holiday holiday){
        holidays.remove(holiday);
    }
}

Это часть tableView файла FXML:

<TableView fx:id="tableView">
    <columnResizePolicy>
        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
    </columnResizePolicy>
    <columns>
        <TableColumn text="Holiday Name">
            <cellFactory>
                <PropertyValueFactory property="name"/>
            </cellFactory>
        </TableColumn>
        <TableColumn text="Date">
            <cellFactory>
                <PropertyValueFactory property="date" />
            </cellFactory>
        </TableColumn>
    </columns>
</TableView>

Проблема, с которой я сталкиваюсь, заключается в том, что java.lang.ClassCastException говорит, что столбец таблицы не может быть приведен к определенному объекту, который, я думаю, является объектом LocalDate. Я использую SimpleObjectProperty для привязки данных к файлу FXML, но я не могу заставить его работать. Единственное, что я могу сделать, это просто сделать дату в Holiday a SimpleStringProperty и преобразовать ее в LocalDate во всех других частях моего кода, но это кажется ненужным. Я ценю любую информацию, которую кто-нибудь может мне дать.

1 Ответ

0 голосов
/ 08 мая 2018

TL; версия DR: вы путаете cellValueFactory с cellFactory. См., Например, этот учебник для хорошего объяснения разницы, которая обобщена для этого конкретного примера ниже.

Столбец таблицы cellValueFactory - это объект, который сообщает столбцу , какие значения отображать в ячейках, или, точнее, как получить эти значения из объектов, представляющих каждую строку. Это представлено Callback<CellDataFeatures<Holiday, LocalDate>, ObservableProperty<LocalDate>>, то есть функцией, отображающей CellDataFeatures<Holiday, LocalDate> в ObservableValue<LocalDate>. Так что в коде Java вы должны сделать

dateColumn.setCellValueFactory(holidayRowData -> holidayRowData.getValue().dateProperty());

или, если вы предпочитаете использовать (несколько устаревший) класс PropertyValueFactory, вы можете сделать

dateColumn.setCellValueFactory(new PropertyValueFactory<>("date"));

Последняя версия имеет (много недостатков, но единственное) преимущество, заключающееся в том, что это можно сделать и в FXML. Обратите внимание, что вы хотите cellValueFactory, а не cellFactory. Так что ваш FXML должен быть

<TableView fx:id="tableView">
    <columnResizePolicy>
        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
    </columnResizePolicy>
    <columns>
        <TableColumn text="Holiday Name">
            <cellValueFactory>
                <PropertyValueFactory property="name"/>
            </cellValueFactory>
        </TableColumn>
        <TableColumn text="Date">
            <cellValueFactory>
                <PropertyValueFactory property="date" />
            </cellValueFactory>
        </TableColumn>
    </columns>
</TableView>

cellFactory, напротив, является объектом, который указывает столбцу , как отображать данные. Он представлен Callback<TableColumn<Holiday, LocalDate>, TableCell<Holiday, LocalDate>>, то есть функцией, отображающей TableColumn<Holiday, LocalDate> в TableCell<Holiday, LocalDate>. ClassCastException происходит потому, что заданная вами фабрика ячеек будет передана TableColumn, но ожидает получения CellDataFeatures, и поэтому, когда она пытается обработать ее как таковую, приведение не выполняется.

Возможно, вы захотите указать здесь фабрику ячеек, в дополнение к фабрике значений ячеек, чтобы вы могли контролировать как отображать дату (например, управлять форматом, используемым для нее). Если вы зададите для столбца даты fx:id, скажем <TableColumn fx:id="dateColumn"> и вставите его в контроллер с помощью

@FXML
private TableColumn<Holiday, LocalDate> dateColumn ;

затем в методе инициализации контроллера вы можете сделать:

public void initialize() throws IOException {
    data = new HolidayData();
    data.loadHolidays();
    tableView.setItems(data.getHolidays());

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
    dateColumn.setCellFactory(column -> new TableCell<Holiday, LocalDate>() {
        @Override
        protected void updateItem(LocalDate date, boolean empty) {
            super.updateItem(date, empty);
            if (empty) {
                setText("");
            } else {
                setText(formatter.format(date));
            }
        }
    });
}
...