У меня возникают серьезные проблемы с производительностью и зависание пользовательского интерфейса в моем приложении JavaFX при навигации по страницам в виде галереи.
Я запускаю это на AMD Ryzen со встроенной графикой.
Несмотря на использование задачи для фоновой загрузки, я наблюдаю значительное зависание и увеличение объема памяти. Вот логи моей консоли во время навигации:
PPSRenderer: scenario.effect - createShader: LinearConvolveShadow_4
Search: test
Growing pool D3D Vram Pool target to 294 896 105
Growing pool D3D Vram Pool target to 313 327 111
Growing pool D3D Vram Pool target to 365 021 337
Growing pool D3D Vram Pool target to 387 835 170
Growing pool D3D Vram Pool target to 434 904 361
Growing pool D3D Vram Pool target to 462 085 883
Growing pool D3D Vram Pool target to 504 627 097
Growing pool D3D Vram Pool target to 536 166 290
Вот мой код:
public class SearchView {
ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.getDefault());
private final AlbumService albumService;
private final SearchViewModel searchViewModel;
private ScrollPane scrollPane;
private FlowPane flowPane;
private FlowPane chipsPane;
private Button backButton = new Button("Back");
private TextField searchTextField = new TextField();
private Button clearButton = new Button("Clear");
private int currentPage = 1;
private boolean isLoading = false;
private String selectedOption = "Album";
public SearchView(AlbumService albumService, SearchViewModel searchViewModel) {
this.albumService = albumService;
this.searchViewModel = searchViewModel;
}
public VBox show() {
return createSearchBox();
}
private VBox createSearchBox() {
VBox searchBox = new VBox();
searchBox.setPadding(new Insets(10));
flowPane = new FlowPane();
flowPane.setHgap(15);
flowPane.setVgap(15);
flowPane.setCursor(Cursor.DEFAULT);
scrollPane = new ScrollPane(flowPane);
scrollPane.setFitToWidth(true);
scrollPane.setPannable(true);
scrollPane.setStyle("-fx-background-color: transparent;");
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
chipsPane = new FlowPane();
chipsPane.setPadding(new Insets(10));
chipsPane.setHgap(8);
chipsPane.setVgap(8);
refreshChips();
HBox chips = new HBox(chipsPane);
chips.setAlignment(Pos.CENTER_LEFT);
chips.setPadding(new Insets(0, 10, 0, 0));
chips.setFillHeight(false);
scrollPane.vvalueProperty().addListener((obs, oldVal, newVal) -> {
if (newVal.doubleValue() >= 1.0 && !isLoading) {
currentPage++;
performSearch(searchTextField.getText());
}
});
VBox.setVgrow(scrollPane, Priority.ALWAYS);
searchBox.getChildren().addAll(createSearchToolbar(), chips, scrollPane);
searchBox.setAlignment(Pos.TOP_CENTER);
return searchBox;
}
private HBox createSearchToolbar() {
HBox toolbar = new HBox();
toolbar.setAlignment(Pos.CENTER_LEFT);
toolbar.setSpacing(15);
Utils.iconLoader(backButton, "/png/back.png");
Utils.iconLoader(clearButton, "/png/close.png");
clearButton.setVisible(false);
clearButton.setManaged(false);
HBox customSearchField = new HBox();
customSearchField.setAlignment(Pos.CENTER_LEFT);
customSearchField.setSpacing(0);
customSearchField.setPadding(new Insets(5, 10, 5, 10));
customSearchField.setStyle("-fx-background-color: #3b3b3b; -fx-background-radius: 20;");
Image image = new Image(Objects.requireNonNull(SearchView.class.getResourceAsStream("/png/search.png")));
ImageView imageView = new ImageView(image);
imageView.setFitHeight(20);
imageView.setFitWidth(20);
imageView.setPreserveRatio(true);
searchTextField.setPromptText(bundle.getString("search"));
searchTextField.setStyle("-fx-font-size: 20; -fx-background-color: transparent; -fx-text-fill: #ffffff");
changeIconColor(imageView, Color.web("#ffffff"));
searchTextField.textProperty().addListener((observable, oldValue, newValue) -> {
boolean hasText = (newValue != null && !newValue.trim().isEmpty());
clearButton.setVisible(hasText);
clearButton.setManaged(hasText);
});
backButton.setOnAction(event -> ViewNavigator.loadView(new CollectionView(albumService).show()));
clearButton.setOnAction(event -> searchTextField.clear());
customSearchField.getChildren().addAll(imageView, searchTextField, clearButton);
HBox.setHgrow(customSearchField, Priority.ALWAYS);
HBox.setHgrow(searchTextField, Priority.ALWAYS);
HBox.setHgrow(clearButton, Priority.ALWAYS);
searchTextField.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
String query = searchTextField.getText();
if (!query.isEmpty()) {
currentPage = 1;
flowPane.getChildren().clear();
performSearch(query);
}
}
});
toolbar.getChildren().addAll(backButton, customSearchField);
return toolbar;
}
private void performSearch(String query) {
isLoading = true;
System.out.println("Search: " + query);
Task searchTask = new Task() {
@Override
protected List call() throws Exception {
List albums = albumService.searchAlbums(query, currentPage);
List nodes = new ArrayList();
for (SearchAlbum album : albums) {
VBox albumNode = AlbumItem.loadImage(
album.name(),
album.artist(),
album.image().get(2).text(),
() -> {
AlbumDetailView albumDetailView = new AlbumDetailView(
album.name(), album.artist(), albumService);
ViewNavigator.loadView(albumDetailView.show());
}
);
nodes.add(albumNode);
}
return nodes;
}
};
searchTask.setOnSucceeded(event -> {
flowPane.getChildren().addAll(searchTask.getValue());
isLoading = false;
});
searchTask.setOnFailed(event -> {
System.out.println("Something went wrong: " + searchTask.getException());
searchTask.getException().printStackTrace();
isLoading = false;
});
new Thread(searchTask).start();
}
private void refreshChips() {
chipsPane.getChildren().clear();
HBox albumChip = Chip.textChip("Album", "/png/check.png", () -> {
selectedOption = "Album";
refreshChips();
}, selectedOption.equals("Album"));
HBox artistChip = Chip.textChip(bundle.getString("artist"), "/png/check.png", () -> {
selectedOption = bundle.getString("artist");
refreshChips();
}, selectedOption.equals(bundle.getString("artist")));
chipsPane.getChildren().addAll(albumChip, artistChip);
}
}
public class AlbumItem {
public static VBox loadImage(String albumName, String artistName, String imageUrl, Runnable clickAction) {
Label albumLabel = new Label(albumName);
Label artistLabel = new Label(artistName);
ImageView albumImageView = new ImageView();
albumLabel.setMaxWidth(140);
artistLabel.setMaxWidth(140);
try {
if (imageUrl != null && !imageUrl.isEmpty()) {
Image image = new Image(imageUrl, true);
albumImageView.setImage(image);
} else {
setDefaultImage(albumImageView);
}
} catch (Exception ex) {
setDefaultImage(albumImageView);
}
albumImageView.setFitWidth(150);
albumImageView.setFitHeight(150);
albumImageView.setPreserveRatio(true);
roundedCorner(albumImageView);
VBox vBox = new VBox(5, albumImageView, albumLabel, artistLabel);
vBox.setAlignment(Pos.CENTER);
vBox.setOnMouseClicked(event -> {
if (clickAction != null) {
clickAction.run();
}
});
vBox.setCursor(Cursor.HAND);
return vBox;
}
private static void setDefaultImage(ImageView imageView) {
var resource = AlbumItem.class.getResourceAsStream("/png/album_error.png");
if (resource != null) {
Image defaultImage = new Image(resource);
imageView.setImage(defaultImage);
imageView.setFitHeight(150);
imageView.setFitWidth(150);
imageView.setPreserveRatio(false);
imageView.setSmooth(true);
imageView.setCache(true);
} else {
System.out.println("Błąd: Nie znaleziono pliku /png/album_error.png w folderze resources!");
}
}
public static void roundedCorner(ImageView imageView) {
Rectangle clip = new Rectangle(150, 150);
clip.setArcHeight(20);
clip.setArcWidth(20);
imageView.setClip(clip);
imageView.setEffect(new DropShadow(10, Color.rgb(0, 0, 0, 0.2)));
}
public static void roundedCorner(ImageView imageView, int v, int v1) {
Rectangle clip = new Rectangle(v, v1);
clip.setArcHeight(20);
clip.setArcWidth(20);
imageView.setClip(clip);
imageView.setEffect(new DropShadow(10, Color.rgb(0, 0, 0, 0.2)));
}
}