Можно ли отгрузить загруженный контент Image, а затем загрузить его снова? Можно ли загрузить его по требованию?Как загрузить javafx.scene.image.Image по запросу?
У меня есть ImageView, который загружает изображение только на шоу?
Можно ли отгрузить загруженный контент Image, а затем загрузить его снова? Можно ли загрузить его по требованию?Как загрузить javafx.scene.image.Image по запросу?
У меня есть ImageView, который загружает изображение только на шоу?
Нет, таких функций нет в договоре Image
. Изображение может загружаться в фоновом режиме, но после загрузки оно не может быть выгружено.
При использовании ImageView
, то вы должны назначить Image
к нему явно, но JavaFX не предоставляет способ для вас, чтобы знать, когда ImageView
фактически показано.
Чтобы реализовать требуемый близко к ImageView
, я должен был его разветвить и использовать устаревший API с Prism
, включая класс NGImageView
.
Но вы можете использовать 'ImageView', чтобы сделать то же самое. –
Если вы знаете, тогда ответьте. Не играйте в hide-n-seek. – Dims
Работа над этим ... Это не тривиально и потребуется некоторое время, чтобы написать. –
Лучше всего загружать изображения перед их отображением!
Для того, чтобы загрузить изображение, просто установите изображение в значение null! Но у вас будет возможность повторно инициализировать это изображение, чтобы иметь возможность просматривать! Я не рекомендую это!
Если вы будете повторно использовать это изображение, просто сохраните его в памяти! Загрузите его один раз и используйте его на неограниченном изображении.
Класс Image
по существу неизменен по отношению к его данным изображения в том смысле, что вы можете указать источник данных изображения во время построения, а затем не можете изменять его через API впоследствии.
Класс ImageView
обеспечивает функциональность отображения изображения в пользовательском интерфейсе. Класс ImageView
изменен, в том смысле, что вы можете изменить отображаемое изображение.
Основная стратегия, необходимая для реализации функций «черепичных изображений», заключается в создании виртуального контейнера , который имеет коллекцию «ячеек» или «плиток», которые повторно используются для отображения различного контента. По сути, такие средства управления, как ListView
, TableView
и TreeView
, реализованы в JavaFX. Вы также можете быть заинтересованы в реализации Tomas Mikula's Flowless такой же идеи.
Чтобы реализовать функциональность «черепичные изображения», вы можете использовать массив из ImageView
s как «ячейки» или «плитки». Вы можете поместить их в панель и реализовать панорамирование/прокрутку в панели, а когда просмотры изображений прокручиваются вне поля зрения, повторно используйте ImageView
s, перемещая изображения с одного изображения на другой, загружая новые изображения только для необходимых ему фрагментов , Очевидно, что изображения, которые больше не ссылаются ни на какое изображение, будут иметь право на сбор мусора обычным способом.
Возможно, существуют другие способы достижения этой цели, такие как использование WritableImage
и использование PixelWriter
для обновления данных пикселя при необходимости. Что лучше всего работает, вероятно, в некоторой степени зависит от того, что наиболее удобно для фактического формата, который у вас есть для данных изображения; существует, вероятно, небольшая разница в производительности между различными стратегиями.
Если вы загружаете изображения с сервера или базы данных, вы должны сделать это в фоновом режиме. Если изображение загружено с URL-адреса, класс Image
предоставляет функциональные возможности для этого.Если вы загружаетесь из входного потока (например, из поля BLOB базы данных), вам нужно будет реализовать фоновый поток.
Здесь пока основная идея (без резьбы):
import java.util.Random;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class PanningTilesExample extends Application {
private static final int TILE_WIDTH = 100;
private static final int TILE_HEIGHT = 100;
private static final int PANE_WIDTH = 800;
private static final int PANE_HEIGHT = 800;
// amount scrolled left, in pixels:
private final DoubleProperty xOffset = new SimpleDoubleProperty();
// amount scrolled right, in pixels:
private final DoubleProperty yOffset = new SimpleDoubleProperty();
// number of whole tiles shifted to left:
private final IntegerProperty tileXOffset = new SimpleIntegerProperty();
// number of whole tiles shifted up:
private final IntegerProperty tileYOffset = new SimpleIntegerProperty();
private final Pane pane = new Pane();
// for enabling dragging:
private double mouseAnchorX;
private double mouseAnchorY;
// array of ImageViews:
private ImageView[][] tiles;
private final Random rng = new Random();
@Override
public void start(Stage primaryStage) {
// update number of tiles offset when number of pixels offset changes:
tileXOffset.bind(xOffset.divide(TILE_WIDTH));
tileYOffset.bind(yOffset.divide(TILE_HEIGHT));
// create the images views, etc. This method could be called
// when the pane size changes, if you want a resizable pane with fixed size tiles:
build();
// while tile offsets change, allocate new images to existing image views:
tileXOffset.addListener(
(obs, oldOffset, newOffset) -> rotateHorizontal(oldOffset.intValue() - newOffset.intValue()));
tileYOffset.addListener(
(obs, oldOffset, newOffset) -> rotateVertical(oldOffset.intValue() - newOffset.intValue()));
// Simple example just has a fixed size pane:
pane.setMinSize(PANE_WIDTH, PANE_HEIGHT);
pane.setPrefSize(PANE_WIDTH, PANE_HEIGHT);
pane.setMaxSize(PANE_WIDTH, PANE_HEIGHT);
// enable panning on pane (just update offsets when dragging):
pane.setOnMousePressed(e -> {
mouseAnchorX = e.getSceneX();
mouseAnchorY = e.getSceneY();
});
pane.setOnMouseDragged(e -> {
double deltaX = e.getSceneX() - mouseAnchorX;
double deltaY = e.getSceneY() - mouseAnchorY;
xOffset.set(xOffset.get() + deltaX);
yOffset.set(yOffset.get() + deltaY);
mouseAnchorX = e.getSceneX();
mouseAnchorY = e.getSceneY();
});
// display in stage:
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
private void build() {
// create array of image views:
int numTileCols = (int) (PANE_WIDTH/TILE_WIDTH + 2);
int numTileRows = (int) (PANE_HEIGHT/TILE_HEIGHT + 2);
tiles = new ImageView[numTileCols][numTileRows];
// populate array:
for (int colIndex = 0; colIndex < numTileCols; colIndex++) {
final int col = colIndex;
for (int rowIndex = 0; rowIndex < numTileRows; rowIndex++) {
final int row = rowIndex;
// create actual image view and initialize image:
ImageView tile = new ImageView();
tile.setImage(getImage(col - tileXOffset.get(), row - tileYOffset.get()));
tile.setFitWidth(TILE_WIDTH);
tile.setFitHeight(TILE_HEIGHT);
// position image by offset, and register listeners to keep it updated:
xOffset.addListener((obs, oldOffset, newOffset) -> {
double offset = newOffset.intValue() % TILE_WIDTH + (col - 1) * TILE_WIDTH;
tile.setLayoutX(offset);
});
tile.setLayoutX(xOffset.intValue() % TILE_WIDTH + (col - 1) * TILE_WIDTH);
yOffset.addListener((obs, oldOffset, newOffset) -> {
double offset = newOffset.intValue() % TILE_HEIGHT + (row - 1) * TILE_HEIGHT;
tile.setLayoutY(offset);
});
tile.setLayoutY(yOffset.intValue() % TILE_HEIGHT + (row - 1) * TILE_HEIGHT);
// add image view to pane:
pane.getChildren().add(tile);
// store image view in array:
tiles[col][row] = tile;
}
}
}
// tiles have been shifted off-screen in vertical direction
// need to reallocate images to image views, and get new images
// for tiles that have moved into view:
// delta represents the number of tiles we have shifted, positive for up
private void rotateVertical(int delta) {
for (int colIndex = 0; colIndex < tiles.length; colIndex++) {
if (delta > 0) {
// top delta rows have shifted off-screen
// shift top row images by delta
// add new images to bottom rows:
for (int rowIndex = 0; rowIndex + delta < tiles[colIndex].length; rowIndex++) {
// stop any background loading we no longer need
if (rowIndex < delta) {
Image current = tiles[colIndex][rowIndex].getImage();
if (current != null) {
current.cancel();
}
}
// move image up from lower rows:
tiles[colIndex][rowIndex].setImage(tiles[colIndex][rowIndex + delta].getImage());
}
// fill lower rows with new images:
for (int rowIndex = tiles[colIndex].length - delta; rowIndex < tiles[colIndex].length; rowIndex++) {
tiles[colIndex][rowIndex].setImage(getImage(-tileXOffset.get() + colIndex, -tileYOffset.get() + rowIndex));
}
}
if (delta < 0) {
// similar to previous case...
}
}
}
// similarly, rotate images horizontally:
private void rotateHorizontal(int delta) {
// similar to rotateVertical....
}
// get a new image for tile represented by column, row
// this implementation just snapshots a label, but this could be
// retrieved from a file, server, or database, etc
private Image getImage(int column, int row) {
Label label = new Label(String.format("Tile [%d,%d]", column, row));
label.setPrefSize(TILE_WIDTH, TILE_HEIGHT);
label.setMaxSize(TILE_WIDTH, TILE_HEIGHT);
label.setAlignment(Pos.CENTER);
label.setBackground(new Background(new BackgroundFill(randomColor(), CornerRadii.EMPTY , Insets.EMPTY)));
// must add label to a scene for background to work:
new Scene(label);
return label.snapshot(null, null);
}
private Color randomColor() {
return Color.rgb(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
}
public static void main(String[] args) {
launch(args);
}
}
Полный код (с обработкой потоков) here, полная версия без резьбы в previous revision
Существует, очевидно, больше функциональных возможностей (и повышение производительности), которые могут быть добавлены здесь, например, вы можете разрешить изменение размера панели (обновление: последняя версия связанного выше gist делает это), а также создавать или удалять плитки, когда панель меняет размер и т. д. Но это должно функционировать как базовый шаблон для этой функции.
Периодически обновлял gists, я рекомендую просмотреть некоторые из [ревизий] (https://gist.github.com/james-d/a249470377fb3c58784a9349a22641c4/revisions). –
Это не совсем понятно, о чем вы просите. «Изображение» представляет собой фиксированный набор данных изображения, если это то, что вы имеете в виду. «WritableImage» представляет изображение фиксированного размера, но данные пикселя могут быть изменены после создания экземпляра через его «PixelWriter». Я понятия не имею, что вы подразумеваете под «Могу ли я иметь ImageView, который загружает изображение только на шоу?». –
@James_D, потому что они заставили 'Image' загружаться с задержкой. См. Методы типа 'cancel()' и 'isBackgroundLoading()'. Странно, что этот процесс может идти только в одном направлении, как в законе термодинамики. – Dims
Хм, не совсем уверен, что вы имеете в виду. Однако решение вашей проблемы состоит в том, чтобы сохранить фиксированный набор «ImageView's» (достаточно, чтобы отобразить экран под любыми значениями смещений прокрутки) и обновить их свойства «Image» по мере необходимости. Вызов 'setImage()' в представлении изображения заставит его перерисовать; если вы используете фоновое скачивание, оно будет перекрашиваться, когда загрузка будет завершена, и это то, что вы хотите. Было бы разумно называть 'cancel()' на текущем 'Image' перед заменой его, чтобы предотвратить ненужную работу в фоновом режиме. Замененные изображения будут gc'd обычным способом. –