2014-09-07 6 views
2

Я впервые использую JavaFX Combobox ... и у меня есть более 2000 значков в combobox (их можно фильтровать через AutoCompleteComboBoxListener, которые я нашел из StackOverflow).JavaFx Combobox lazy loading images

Я планирую использовать ExecutorService для извлечения изображений из zip-файла.

Проблема заключается в том, как я могу определить видимые в данный момент элементы в Combobox?

Я устанавливаю пользовательский ListCellFactory для ComboBox, и у меня есть собственный класс ListCell, который также отображает значок. Можно ли каким-либо образом проверить из объекта ListCell, отображается ли элемент «показывается»?

Спасибо.

+0

Зачем вам нужно это проверить? –

+0

Как я уже сказал, в всплывающем окне ComboBox может быть возможно 2000 значков, и мой план состоял в том, чтобы загружать и отображать те, которые видны в окне просмотра всплывающего окна ComboBox, поскольку пользователь прокручивает всплывающие окна всплывающих окон. – user2499946

+0

Клеточный механизм позаботится об этом (более или менее). Добавлен ответ. –

ответ

4

Просто отметим, что если вы загружаете изображения из отдельных файлов, а не из почтового индекса файла, существует механизм, который избавляет от необходимости работать непосредственно с любым видом резьбы в всего:

ComboBox<MyDataType> comboBox = new ComboBox<>(); 
comboBox.setCellFactory(listView -> new ListCell<MyDataType>() { 

    private ImageView imageView = new ImageView(); 

    @Override 
    public void updateItem(MyDataType item, boolean empty) { 
     super.updateItem(item, empty); 
     if (empty) { 
      setGraphic(null); 
     } else { 
      String imageURL = item.getImageURL(); 
      Image image = new Image(imageURL, true); // true means load in background 
      imageView.setImage(image); 
      setGraphic(imageView); 
     } 
    } 
}); 

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

(Update. Примечание: Я изменил это слушать изменения в itemProperty() клетки, вместо того, чтобы использовать метод updateItem(...) Метод updateItem(...) можно назвать чаще, чем когда фактический элемент отображается с помощью клеточных изменений, так что подход позволяет избежать ненужной "перезагрузки" изображений)

Что-то вроде:.

ExecutorService exec = ... ; 
ComboBox<MyDataType> comboBox = new ComboBox<>(); 
comboBox.setCellFactory(listView -> { 

    ListCell<MyDataType> cell = new ListCell<MyDataType>() ; 

    ImageView imageView = new ImageView(); 
    ObjectProperty<Task<Image>> loadingTask = new SimpleObjectProperty<>(); 

    cell.emptyProperty().addListener((obs, wasEmpty, isNotEmpty) -> { 
     if (isNowEmpty) { 
      cell.setGraphic(null); 
      cell.setText(null); 
     } else { 
      cell.setGraphic(imageView); 
     } 
    }); 

    cell.itemProperty().addListener((obs, oldItem, newItem) -> { 
     if (loadingTask.get() != null && 
      loadingTask.get().getState() != Worker.State.SUCCEEDED && 
      loadingTask.get().getState() != Worker.State.FAILED) { 

      loadingTask.get().cancel(); 
     } 
     loadingTask.set(null) ; 
     if (newItem != null) { 
      Task<Image> task = new Task<Image>() { 
       @Override 
       public Image call() throws Exception { 
        Image image = ... ; // retrieve image for item 
        return image ; 
       } 
      }; 
      loadingTask.set(task); 
      task.setOnSucceeded(event -> imageView.setImage(task.getValue())); 
      exec.submit(task); 

      cell.setText(...); // some text from item... 
     } 
    }); 

    return cell ; 
}); 

Некоторые мысли о производительности здесь:

Во-первых, «виртуализировать d "механизма ComboBox означает, что будет создано только небольшое количество этих ячеек, поэтому вам не нужно беспокоиться о том, что вы сразу начинаете тысячи потоков, загружающих изображения, или действительно, что у вас когда-либо будет тысячи изображений в памяти.

Когда пользователь прокручивает список, itemProperty(...) может изменяться довольно часто, поскольку ячейки повторно используются для новых элементов. Важно убедиться, что вы не используете изображения из запущенных потоков, но не заканчиваете, прежде чем элемент снова изменится; это цель отмены существующей задачи в начале прослушивателя изменения позиции. Отмена задачи будет препятствовать вызову обработчика onSucceeded. Тем не менее, вы по-прежнему будете иметь эти потоки, поэтому, если возможно, реализация вашего call() метода должна проверять флаг isCancelled() и прерывать его как можно быстрее, если он возвращает true. Это может быть сложно реализовать: я бы экспериментировал и посмотрел, как это работает с простой реализацией в первую очередь.

+0

Большое спасибо за исчерпывающий ответ. Я понятия не имел, что изображение может делать фоновую загрузку из коробки. И javadoc of Image указывает, что он также ищет из класса, и мой zip-файл связан с моим приложением, поэтому у меня не было проблем с добавлением его в путь к классам ... У меня все работает нормально. – user2499946

0

Текущий видимый элемент означает текущий выбранный элемент в поле со списком. Вы можете получить выбранный элемент с помощью

comboboxname.getSelectionModel().getSelectedItem(); 
+0

Ну, я имел в виду видимые в данный момент элементы во всплывающем окне Combobox. – user2499946

+0

какое всплывающее окно? вы имеете в виду автозаполнение всплывающего окна? –

+0

Извините, если я был неясен. Я имею в виду всплывающее окно, когда вы нажимаете кнопку со стрелкой вниз в выпадающем списке, а видимыми пунктами - объекты, которые в настоящее время отображаются в ScrollPane всплывающего окна. – user2499946

1

Даже если ваш список содержит 2000 элементов, javafx будет создавать объекты списка только для видимых ячеек (плюс один или два для полу видимых ячеек), поэтому на вас не очень много TODO. Изображения ленивы - просто загружайте их, когда updateItem вызывается - и, возможно, кеш уже загружен Изображения в кэше lifo, так что не все они остаются в памяти

+0

Прокрутка может быть довольно уродливой, если вы извлекаете изображение из zip-файла по требованию в приложении приложения FX, хотя нет? –