Как оптимизировать повторное использование шаблона ListCell в JavaFX
В рамках упражнения "Приложение TODO List" хочу создать такой вот список карточек:
Каждая карточка инициализируется таким образом:
public class TasksListController {
private static class TaskCell extends ListCell<ObservableTask> {
@FXML private HBox rootElement;
@FXML private Label titleLabel;
@FXML private Label noteLabel;
public TaskCell() {
this.loadFXML();
}
private void loadFXML() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/Presenter/Components/TaskList/TaskCard.fxml"));
loader.setController(this);
loader.load();
} catch (IOException exception) {
System.out.println("Failed to load 'TaskCard.fxml' component:\n" + exception.getMessage());
}
}
@Override
public void updateItem(ObservableTask observableTask, boolean doesNotRepresentAnyDomainData) {
super.updateItem(observableTask, doesNotRepresentAnyDomainData);
if (doesNotRepresentAnyDomainData) {
return;
}
this.setGraphic(this.rootElement);
this.titleLabel.setText(observableTask.getTitle());
if (observableTask.getNote() != null) {
this.noteLabel.setText(observableTask.getNote());
}
}
}
// ...
}
При создании каждой карточки происходит чтение файла TaskCard.fxml, а такое расточительное использование вычислительных ресурсов недопустимо. Но как это можно оптимизировать?
Мои размышления
Концептуально, решение такое:
- Читаем FXML-файл один раз и сохраняем его в статическое полe
- Для каждой
ListCellделаем копию этого файла. - Добиться того, чтобы изменения данных таска отражались только на индивидуальной копии FXML-шаблона.
При реализации образуются следующие вопросы:
- Насколько мне известно,
getResourceвозвращает URL. Но происходит ли при этом чтение файла? Если да, то это неочевидно из названия метода, но если нет, то URL шаблонаTaskCard.fxmlможно смело выносить в статическое поле. - Может ли быть инстанс
FXMLLoaderодин на все карточки? Наверное нет, потому что мы будем вызыватьloader.setController(this);при создании каждойListCell. - Не совсем понятно, в какой момент происходит чтение файла. Скорее всего, это
loader.load();. Но что он возвращает?
Ответы (1 шт):
от new FXMLLoader() не получится никуда уйти, т.к. он хранит в себе иерархию и биндинги конкретного элемента. Кстати код можно немного улучшить, loader.load() возвращает корневой элемент, который можно вставить в setGraphic().
Чтение файла происходит в методе load(), он из InputStream забирает данные. Т.к. ресурсные данные не лежат в памяти, то каждый раз происходит чтение из диска. Логично, что вместо файла можно подставить свой поток данных, например:
private static byte[] xml;
public TaskCell() {
final FXMLLoader loader = new FXMLLoader();
try {
if(xml == null) {
xml = getClass()
.getResource("/Presenter/Components/TaskList/TaskCard.fxml")
.openStream()
.readAllBytes();
}
loader.setController(this);
rootElement = loader.load(new ByteArrayInputStream(xml));
} catch (IOException exception) {
System.out.println("Failed to load 'TaskCard.fxml' component:\n" + exception.getMessage());
}
}
