OutOfMemoryError и проблемы с ObservableList в ComboBox

Я делаю fxml файл в javafx2.

У меня есть объекты List of Person. Название этого списка Entries, У меня есть ObservableList, myObservableList, Внутри этого я хочу поставить этикетки. Каждый ярлык должен содержать пару изображений человека и текст его имени. Я пишу этот код:

for (int i=0; i<numberOfEntries; i++){                               
    currentEntry = Entries.get(i);
    name=currentEntry.getName();                                  
    image1 = new Image("file:"+currentEntry.getIcon());                    
    imageView1= new ImageView();
    imageView1.setFitHeight(50);
    imageView1.setFitWidth(70);
    imageView1.setImage(image1);                       
    label = new Label(name, imageView1);
    label.setFont(new Font("serif", 32));                         
    myObservableList.add(label);                   
}

Это работает нормально, но после нескольких вставок изображений JVM выдает мне следующее сообщение об ошибке:

Caused by: java.lang.OutOfMemoryError: Java heap space.

Эта ошибка происходит из строки кода image1 = new Image("file:"+currentEntry.getIcon());

Наконец, я хочу поместить все элементы myObservableList в элементы ComboBox. По этой причине в методе Initialize Java-контроллера я пишу:

    myComboBox.setItems(myObservableList);

    ListCell<Label> buttonCell = new ListCell<Label>() {
         @Override protected void updateItem(Label item, boolean isEmpty) {
         super.updateItem(item, isEmpty);
            setText(item==null ? "" : item.getText());                 
        }
    };

    myComboBox.setButtonCell(buttonCell);

Я уверен, что у меня недостаточно опыта в javafx, и я не знаю, как мне поступить, потому что у меня есть комбинированный список с парами значков и текста в одной ячейке для всех элементов.

Я хочу поблагодарить Питера Дунихо и ПаккуДона за помощь в улучшении английского языка в моем тексте.

1 ответ

Почти всегда ошибка использовать Node класс в качестве типа данных для ComboBox (или для любого другого контроля). Вы должны использовать класс, который представляет только данные, и зарегистрировать фабрику ячеек, чтобы настроить способ отображения данных.

В вашем случае, если вы включите изображение в данные, вы, скорее всего, столкнетесь с проблемами памяти. Каждое изображение, вероятно, будет представлено в памяти несколькими мегабайтами. Таким образом, ваш класс данных должен содержать имя изображения, а затем вы можете использовать ячейку в поле со списком для создания изображения.

Вот пример кода, чтобы дать вам идею:

Класс данных (Person.java):

public class Person {
    private final String name ;
    private final String imageFileName ;

    public Person(String name, String imageFileName) {
        this.name = name ;
        this.imageFileName = imageFileName ;
    }

    public String getName() {
        return name ;
    }

    public String getImageFileName() {
        return imageFileName ;
    }
}

Код пользовательского интерфейса для создания ComboBox из List<Person>:

List<Person> entries = ... ; // populated from DB

ComboBox<Person> comboBox = new ComboBox<>();
comboBox.getItems().addAll(entries);

comboBox.setCellFactory(new Callback<ListView<Person>, ListCell<Person>>() {
    @Override
    public ListCell<Person> call(ListView<Person> listCell) {

        return new ListCell<Person>() {
            private final ImageView = new ImageView();
            @Override
            public void updateItem(Person person, boolean empty) {
                super.updateItem(person, empty);
                if (empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    File imageFile = new File(person.getImageFileName());
                    String imageUrl = imageFile.toURI().toURL().toExternalForm();
                    Image image = new Image(imageUrl, 70, 50, 
                        // preserve ratio
                        true, 
                        // smooth resizing
                        true,
                        // load in background
                        true);
                    imageView.setImage(image);
                    setText(person.getName());
                    setGraphic(imageView);
                }
            }
        };
    }
});

Вы можете использовать то же самое ListCell реализация для ComboBox"s buttonCell,

Дело в том, что ячейки создаются только для видимых ячеек, поэтому изображения загружаются "по требованию" при отображении ячеек. С использованием Image Конструктор, который принимает параметры ширины и высоты, также уменьшает объем памяти, так как Image объект может изменить размер при загрузке.

Наконец, обратите внимание, что важно использовать флаг для загрузки изображений в фоновом режиме, что поддерживает адаптивный интерфейс. Если вы прокрутите быстро, вы, вероятно, увидите, что некоторые изображения не загружаются в течение короткого времени; клетки будут перекрашиваться соответствующим образом, как только изображение станет доступным.

Другие вопросы по тегам