So first off I am trying to color a cell red, or have the cell outlined in red based on another column's value, same row. For example, there is a "Enrolled" Date column and a "DeadlineToEnrollBy" Date column. This is a generic example.
So If The Deadline date is tomorrow, the cell will not be in red because the student still has time, but if the Deadline was yesterday and the student still has not enrolled, then the "Enrolled" Date cell will be in red, indicating immediate attention to that student. I am actually able to do this but the table acts "funky" at times.
If I scroll to fast to the bottom, I get an error. And at times when scrolling up and down, the red cells will be in other places, or all the cells in the column will be red even though it shouldn't be. I believe even uploading new data, hence refreshing the table too causes the red cells to be off.
The error that I get is
Exception in thread "JavaFX Application Thread"
java.lang.IndexOutOfBoundsException
on this line of code
Person student= ClassPanelView.retrieveTable().getItems.get(getIndex());
Here is the flow of code that's relevant
In ClassPanelView
createTable(){
.
.
TableColumn<Person, Date> enrolledBy = new TableColumn<>("Enrolled");
enrolledBy.setCellValueFactory(new PropertyValueFactory<>("dateEnrolled"));
enrolledBy.setCellFactory(column -> {
return new util.EditEnrolledDateCell<Person, Date>();
});
table.getColumns().addAll(enrolledBy, ..etc);
}
public static TableView<Person> retrieveTable() {
return table;
}
The other class/Cell the column calls/returns
public class EditEnrolledDateCell<S,T> extends TextFieldTableCell<Person, Date> {
private Date now = new Date();
...
...
@Override
public void updateItem(Date item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
if(this.getIndex() > -1) {
/*int currentIndex = indexProperty().getValue() < 0 ? 0
: indexProperty().getValue(); */
Person student = ClassPanelView.retrieveTable().getItems().get(getIndex()); //<==== This line is the problem
if(student.getDeadline != null && student.getDeadline.before(now)) {
setStyle("-fx-border-color: #f40404;\n"
+ "-fx-border-width: 1 1 1 1;\n");
}
}
}
else { //if there is something here, format it
setStyle("");
setText(GuiUtils.monthFirstDateFormat.format(item));
}
}
}
Any tips/knowledge would be greatly appreciated! Thank you
You never reset the style of the cell for persons not crossing the deadline. You need to do this kind of update. Furthermore there may be empty cells at the end of the table that have a non-negative index. (You try to retrieve the person only if the cell is empty.)
Also to make the cell more reuseable I recommend using TableCell.getTableView
instead of accessing a static
field.
@Override
public void updateItem(Date item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setStyle(null);
setText("");
} else {
Person student = getTableView().getItems().get(getIndex());
setStyle(student.getDeadline != null && !now.after(student.getDeadline)
? null
: "-fx-border-color: #f40404; -fx-border-width: 1 1 1 1;");
// I recommend passing the formatter in a constructor to make the cell type easier to reuse
setText(item == null ? "" : GuiUtils.monthFirstDateFormat.format(item));
}
}
Note: You should also rename the field getDeadline
to deadline
. getDeadline
by convention is the name of the getter method for the deadline
property and I cannot think of a reason where naming a field in a way you'd usually use for a getter would be good practice.
Also I currently don't see a reason to add type parameters to your custom cell type. Those types are never used.
public class EditEnrolledDateCell extends TextFieldTableCell<Person, Date>
There may be a scenario where you'd want to keep those. If you want to use the cell with a TableView<T>
where T
is a subtype of Person
or use a value type in the column that is a subtype of Date
, but this requires a different declaration:
public class EditEnrolledDateCell<S extends Person, T extends Date> extends TextFieldTableCell<S, T> {
...
@Override
public void updateItem(T item, boolean empty) {
...
Moreover I recommend using LocalDate
instead of Date
, if possible, since this type is more modern/easier to use.