javafx 中的滚动窗格在显示大量项目和视图时会对 RAM 造成负担
Scrollpane in javafx is a burden on the RAM when displaying a big collection of items and views
我正在使用 javafx 开发一个图书馆应用程序,用户可以在其中导入 epub 文件,他得到一个带书架的图书馆,每个书架最多包含 6 本书。我在其中使用了一个滚动窗格,一个 VBox 包含额外的 VBox(每个都像一个架子),每个 VBox 都包含一个图像(这是架子),在它上面是一个包含书籍封面图像的 HBox。我尝试使用列表视图,但它不起作用,因为列表视图列出了您单击其中一个的项目列表,在我的情况下,该项目将是包含几本书的整个书架(我想处理点击每本书单独)。抱歉,描述太长了。
This is the image represention of my work
对此有很多选择;但可能最好的方法是按照评论建议使用 ListView
。
下面的示例应用程序将演示执行此操作的一种方法。但是,我还没有对 ListView
的样式进行任何处理。主要是因为我自己 CSS 不是很精通(我欢迎编辑和建议),但也因为这超出了这个相当模糊的问题的范围。
将 ListView
与自定义 CellFactory
相结合,您可以为库中的每个 "shelf" 构建布局; ListView
将使用该布局显示每一行。
下面的代码中有额外的注释。
Library Example MCVE:
LibraryExample.java:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.List;
public class LibraryExample extends Application {
// Our list of shelves that will be displayed in the ListView
private final ObservableList<Shelf> shelves = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Build a list of 100 sample books. This list could come from a database or other outside source, of course
List<Book> books = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
books.add(new Book("Book #" + i, new ImageView("sample/generic-cover.png")));
}
// We will now create our shelves for the books. We will limit the number of books to 6 per shelf. This uses
// the subList method of our List to grab every 6 books until we run out.
int index = 0;
while (index < books.size()) {
// Make sure there are at least 6 books remaining, otherwise, we need to get the subList up to the size of
// the original list.
final int numToAdd = (index + 6 <= books.size() ? index + 6 : books.size());
shelves.addAll(new Shelf(books.subList(index, numToAdd)));
index += 6;
}
// Now, let's create our ListView that will hold our shelves.
ListView<Shelf> listView = new ListView<>();
VBox.setVgrow(listView, Priority.ALWAYS);
// Now for the magic. We will override the CellFactory for the ListView so we can provide our own layout
// for each row
listView.setCellFactory(new Callback<ListView<Shelf>, ListCell<Shelf>>() {
@Override
public ListCell<Shelf> call(ListView<Shelf> param) {
return new ShelfListCell();
}
});
listView.setItems(shelves);
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(700);
primaryStage.setHeight(600);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
class ShelfListCell extends ListCell<Shelf> {
@Override
protected void updateItem(Shelf shelf, boolean empty) {
super.updateItem(shelf, empty);
if (shelf == null || empty) {
setGraphic(null);
} else {
// Here, we will build our layout for each shelf
VBox root = new VBox(5);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(5));
HBox hBox = new HBox(20);
hBox.setAlignment(Pos.CENTER);
hBox.setPadding(new Insets(5));
// Add image for each each book on this shelf to the layout
for (Book book : shelf.getBooks()) {
// Get the image of the book and add a simple click listener
ImageView cover = book.getCoverImage();
cover.setPreserveRatio(true);
cover.setFitHeight(100);
cover.setOnMouseClicked(event -> System.out.println("Clicked " + book.getTitle()));
hBox.getChildren().add(book.getCoverImage());
}
root.getChildren().addAll(hBox, new Separator(Orientation.HORIZONTAL));
// Set the cell to display our layout
setGraphic(root);
}
}
}
Book.java:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.image.ImageView;
public class Book {
private final StringProperty title = new SimpleStringProperty();
private final ObjectProperty<ImageView> coverImage = new SimpleObjectProperty<>();
public Book(String title, ImageView coverImage) {
this.title.set(title);
this.coverImage.set(coverImage);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public ImageView getCoverImage() {
return coverImage.get();
}
public ObjectProperty<ImageView> coverImageProperty() {
return coverImage;
}
public void setCoverImage(ImageView coverImage) {
this.coverImage.set(coverImage);
}
}
Shelf.java:
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.List;
public class Shelf {
// Set max number of books per shelf
private final static int MAX_BOOKS = 6;
// Our observable list of books
private final ListProperty<Book> books = new SimpleListProperty<>(FXCollections.observableArrayList());
public Shelf(List<Book> books) {
this.books.addAll(books);
}
public void addBooks(Book... books) {
this.books.addAll(books);
}
public static int getMaxBooks() {
return MAX_BOOKS;
}
public ObservableList<Book> getBooks() {
return books.get();
}
public ListProperty<Book> booksProperty() {
return books;
}
public void setBooks(ObservableList<Book> books) {
this.books.set(books);
}
}
The Result:
我正在使用 javafx 开发一个图书馆应用程序,用户可以在其中导入 epub 文件,他得到一个带书架的图书馆,每个书架最多包含 6 本书。我在其中使用了一个滚动窗格,一个 VBox 包含额外的 VBox(每个都像一个架子),每个 VBox 都包含一个图像(这是架子),在它上面是一个包含书籍封面图像的 HBox。我尝试使用列表视图,但它不起作用,因为列表视图列出了您单击其中一个的项目列表,在我的情况下,该项目将是包含几本书的整个书架(我想处理点击每本书单独)。抱歉,描述太长了。 This is the image represention of my work
对此有很多选择;但可能最好的方法是按照评论建议使用 ListView
。
下面的示例应用程序将演示执行此操作的一种方法。但是,我还没有对 ListView
的样式进行任何处理。主要是因为我自己 CSS 不是很精通(我欢迎编辑和建议),但也因为这超出了这个相当模糊的问题的范围。
将 ListView
与自定义 CellFactory
相结合,您可以为库中的每个 "shelf" 构建布局; ListView
将使用该布局显示每一行。
下面的代码中有额外的注释。
Library Example MCVE:
LibraryExample.java:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.List;
public class LibraryExample extends Application {
// Our list of shelves that will be displayed in the ListView
private final ObservableList<Shelf> shelves = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Build a list of 100 sample books. This list could come from a database or other outside source, of course
List<Book> books = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
books.add(new Book("Book #" + i, new ImageView("sample/generic-cover.png")));
}
// We will now create our shelves for the books. We will limit the number of books to 6 per shelf. This uses
// the subList method of our List to grab every 6 books until we run out.
int index = 0;
while (index < books.size()) {
// Make sure there are at least 6 books remaining, otherwise, we need to get the subList up to the size of
// the original list.
final int numToAdd = (index + 6 <= books.size() ? index + 6 : books.size());
shelves.addAll(new Shelf(books.subList(index, numToAdd)));
index += 6;
}
// Now, let's create our ListView that will hold our shelves.
ListView<Shelf> listView = new ListView<>();
VBox.setVgrow(listView, Priority.ALWAYS);
// Now for the magic. We will override the CellFactory for the ListView so we can provide our own layout
// for each row
listView.setCellFactory(new Callback<ListView<Shelf>, ListCell<Shelf>>() {
@Override
public ListCell<Shelf> call(ListView<Shelf> param) {
return new ShelfListCell();
}
});
listView.setItems(shelves);
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(700);
primaryStage.setHeight(600);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
class ShelfListCell extends ListCell<Shelf> {
@Override
protected void updateItem(Shelf shelf, boolean empty) {
super.updateItem(shelf, empty);
if (shelf == null || empty) {
setGraphic(null);
} else {
// Here, we will build our layout for each shelf
VBox root = new VBox(5);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(5));
HBox hBox = new HBox(20);
hBox.setAlignment(Pos.CENTER);
hBox.setPadding(new Insets(5));
// Add image for each each book on this shelf to the layout
for (Book book : shelf.getBooks()) {
// Get the image of the book and add a simple click listener
ImageView cover = book.getCoverImage();
cover.setPreserveRatio(true);
cover.setFitHeight(100);
cover.setOnMouseClicked(event -> System.out.println("Clicked " + book.getTitle()));
hBox.getChildren().add(book.getCoverImage());
}
root.getChildren().addAll(hBox, new Separator(Orientation.HORIZONTAL));
// Set the cell to display our layout
setGraphic(root);
}
}
}
Book.java:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.image.ImageView;
public class Book {
private final StringProperty title = new SimpleStringProperty();
private final ObjectProperty<ImageView> coverImage = new SimpleObjectProperty<>();
public Book(String title, ImageView coverImage) {
this.title.set(title);
this.coverImage.set(coverImage);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public ImageView getCoverImage() {
return coverImage.get();
}
public ObjectProperty<ImageView> coverImageProperty() {
return coverImage;
}
public void setCoverImage(ImageView coverImage) {
this.coverImage.set(coverImage);
}
}
Shelf.java:
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.List;
public class Shelf {
// Set max number of books per shelf
private final static int MAX_BOOKS = 6;
// Our observable list of books
private final ListProperty<Book> books = new SimpleListProperty<>(FXCollections.observableArrayList());
public Shelf(List<Book> books) {
this.books.addAll(books);
}
public void addBooks(Book... books) {
this.books.addAll(books);
}
public static int getMaxBooks() {
return MAX_BOOKS;
}
public ObservableList<Book> getBooks() {
return books.get();
}
public ListProperty<Book> booksProperty() {
return books;
}
public void setBooks(ObservableList<Book> books) {
this.books.set(books);
}
}
The Result: