Search code examples
javaimagejavafximageviewresize

Binding child width and height to parents makes it unable to become smaller when resizing


When I bind an ImageView to it's container:

imageView.fitWidthProperty().bind(widthProperty());
imageView.fitHeightProperty().bind(heightProperty());

It functions as intended when making the container bigger (the imageview scales accordingly), but when I try to make the container smaller, neither the container or the imageview becomes smaller. Strangely, when I set the imageview's image before the stage.show() method, it does function as intended, but again faces the same issue when changing the image after the stage.show() method.

Instead of bindings i've tried using listeners (both ChangeListener and InvalidationListener), changing the layoutChildren() method in the container/parent class and making a wrapperclass for the imageview, but they didn't solve the issue.

My project doesn't use FXML, so I would like to find a solution that doesn't need it.


Solution

  • When you try to shrink it, the container is (probably) going to remain large enough to hold the ImageView at its current size, and will be clipped by its container just to show the visible portion. Binding to dimensions is never a particularly good approach; to create custom layouts, subclass Region and override layoutChildren().

    Here is a quick example:

    import javafx.scene.image.ImageView;
    import javafx.scene.layout.Region;
    
    public class ImageContainer extends Region {
        private final ImageView imageView ;
    
        public ImageContainer(ImageView imageView) {
            this.imageView = imageView;
            getChildren().add(imageView);
        }
    
        @Override
        protected void layoutChildren() {
            // usable width and height:
            double width = getWidth() - snappedLeftInset() - snappedRightInset();
            double height = getHeight() - snappedTopInset() - snappedBottomInset();
            imageView.setFitWidth(Math.max(1, width));
            imageView.setFitHeight(Math.max(1, height));
            double imageWidth = imageView.getBoundsInLocal().getWidth();
            double imageHeight = imageView.getBoundsInLocal().getHeight();
            // center image (can also make this more complex and support alignment):
            double x = snappedLeftInset() + (width - imageWidth) / 2;
            double y = snappedTopInset() + (height - imageHeight) / 2;
            imageView.relocate(x , y);
        }
    
    }
    

    and

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.scene.control.SplitPane;
    import javafx.scene.image.ImageView;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class HelloApplication extends Application {
    
        private final String ioColor = "https://images-assets.nasa.gov/image/PIA00738/PIA00738~orig.jpg";
        private final String ioBW = "https://images-assets.nasa.gov/image/PIA01217/PIA01217~orig.jpg";
    
        @Override
        public void start(Stage stage) throws IOException {
            ImageView leftImage = new ImageView(ioBW);
            leftImage.setPreserveRatio(true);
            ImageView rightImage = new ImageView(ioColor);
            rightImage.setPreserveRatio(true);
            SplitPane splitter = new SplitPane(new ImageContainer(leftImage), new ImageContainer(rightImage));
            Scene scene = new Scene(splitter, 800, 500);
            stage.setScene(scene);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    }