Одним из решений является создание Region
подкласса, который просто оборачивает ImageView
. Это лучше, чем привязка свойств подгонки ширины / высоты ImageView
к его родительскому элементу, поскольку привязка дочерних измерений к родительским измерениям может вызвать проблемы с системой макетов.
Вот пример:
import javafx.beans.DefaultProperty;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;
@DefaultProperty("image")
public class ResizableImageView extends Region {
/* *********************************************************************
* *
* Properties *
* *
***********************************************************************/
// -- image property
private final ObjectProperty<Image> image =
new SimpleObjectProperty<>(this, "image") {
@Override
protected void invalidated() {
imageView.setImage(get());
}
};
public final void setImage(Image image) {
this.image.set(image);
}
public final Image getImage() {
return image.get();
}
public final ObjectProperty<Image> imageProperty() {
return image;
}
// -- preserveRatio property
private final BooleanProperty preserveRatio =
new SimpleBooleanProperty(this, "preserveRatio") {
@Override
protected void invalidated() {
imageView.setPreserveRatio(get());
}
};
public final void setPreserveRatio(boolean preserveRatio) {
this.preserveRatio.set(preserveRatio);
}
public final boolean isPreserveRatio() {
return preserveRatio.get();
}
public final BooleanProperty preserveRatioProperty() {
return preserveRatio;
}
/* *********************************************************************
* *
* Instance Fields *
* *
***********************************************************************/
private final ImageView imageView = new ImageView();
/* *********************************************************************
* *
* Constructors *
* *
***********************************************************************/
public ResizableImageView() {
getStyleClass().add("resizable-image-view");
getChildren().add(imageView);
}
public ResizableImageView(Image image) {
this();
setImage(image);
}
/* *********************************************************************
* *
* Methods *
* *
***********************************************************************/
@Override
protected void layoutChildren() {
double x = snappedLeftInset();
double y = snappedTopInset();
double w = getWidth() - x - snappedRightInset();
double h = getHeight() - y - snappedRightInset();
imageView.setFitWidth(w);
imageView.setFitHeight(h);
positionInArea(imageView, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
}
@Override
protected double computePrefWidth(double height) {
double prefWidth = snappedLeftInset() + snappedRightInset();
var image = getImage();
if (image != null) {
double requestedWidth = image.getRequestedWidth();
prefWidth += (requestedWidth > 0.0 ? requestedWidth : image.getWidth());
}
return snapSizeX(prefWidth);
}
@Override
protected double computePrefHeight(double width) {
double prefHeight = snappedTopInset() + snappedBottomInset();
var image = getImage();
if (image != null) {
double requestedHeight = image.getRequestedHeight();
prefHeight += (requestedHeight > 0.0 ? requestedHeight : image.getHeight());
}
return snapSizeY(prefHeight);
}
}
Вы можете установить свойства minWidth
, minHeight
, maxWidth
и maxHeight
, чтобы контролировать, насколько маленьким или большим может быть изображение.
Вы можете сделать почти то же самое, используя BackgroundImage
.
var image = new Image(...);
var bgImage = new BackgroundImage(
image,
BackgroundRepeat.NO_REPEAT,
BackgroundRepeat.NO_REPEAT,
BackgroundPosition.CENTER,
new BackgroundSize(1.0, 1.0, true, true, false, false)
);
var region = new Region();
region.setBackground(new Background(bgImage));
. Это приведет к заполнению всего изображения Region
, как если бы preserveRatio
было установлено на false
в примере ResizableImageView
. Если вы хотите, чтобы он вел себя так, как если бы preserveRatio
был установлен на true
, используйте следующее BackgroundSize
:
new BackgroundSize(1.0, 1.0, true, true, true, false);
Разница в пятом аргументе contain
, который был изменен сfalse
до true
. Ознакомьтесь с документацией , чтобы лучше понять, что делает каждый аргумент.
Обратите внимание, что использование BackgroundImage
таким образом означает, что Region
не будет вычислять свои предпочтительные измерения на основеобраз. Это может или не может вызвать проблемы для вас. Например, при первоначальном изменении размера макета он не будет учитывать изображение. Однако после этого все должно работать так же, как ResizableImageView
- так как Region
изменяется, как и изображение.
A BackgroundImage
также можно установить с помощью CSS:
.your-region-class {
-fx-background-image: url(...);
-fx-background-repeat: no-repeat;
-fx-background-position: center;
-fx-background-size: contain; /* or 100% 100% to act as if preserveRatio is false */
}