How to make canvas Resizable in javaFX? - javafx

In javaFX to resize a canvas there is no such method to do that, the only solution is to extends from Canvas.
class ResizableCanvas extends Canvas {
public ResizableCanvas() {
// Redraw canvas when size changes.
widthProperty().addListener(evt -> draw());
heightProperty().addListener(evt -> draw());
}
private void draw() {
double width = getWidth();
double height = getHeight();
GraphicsContext gc = getGraphicsContext2D();
gc.clearRect(0, 0, width, height);
}
#Override
public boolean isResizable() {
return true;
}
}
is extends from Canvas is the only solution to make canvas Resizable ?
because this solution work only if we don't want to use FXML, if we declare in fxml a canvas how can we make it resizable?
this is my code :
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
Controller controller;
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
AnchorPane root = loader.load(); // controller initialized
controller = loader.getController();
GraphicsContext gc = controller.canvas.getGraphicsContext2D();
gc.setFill(Color.AQUA);
gc.fillRect(0, 0, root.getPrefWidth(), root.getPrefHeight());
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(controller.Pane, controller.Pane.getPrefWidth(), controller.Pane.getPrefHeight()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Of all the answers given, none of them actually worked for me in terms of making the canvas automatically resize with its parent. I decided to take a crack at this and this is what I came up with:
import javafx.scene.canvas.Canvas;
public class ResizableCanvas extends Canvas {
#Override
public boolean isResizable() {
return true;
}
#Override
public double maxHeight(double width) {
return Double.POSITIVE_INFINITY;
}
#Override
public double maxWidth(double height) {
return Double.POSITIVE_INFINITY;
}
#Override
public double minWidth(double height) {
return 1D;
}
#Override
public double minHeight(double width) {
return 1D;
}
#Override
public void resize(double width, double height) {
this.setWidth(width);
this.setHeight(height);
}
}
This was the only one that actually made the canvas truly resizable.
My reasons for going with this approach is as follows:
I didn't want to break encapsulation by forcing the parent component to send us a width and height in the constructor which would also mean that the canvas cannot be used in FXML.
I also did not want to depend on the parent's width and height properties thus making the canvas the only child in it's parent, by taking up all the space.
Finally, the canvas needed to have it's drawing done in another class, which meant I could not use the current accepted answer which also included drawing to canvas via a draw method.
With this canvas, I do not need to bind to its parent width/height properties to make the canvas resize. It just resizes with whatever size the parent chooses. In addition, anyone using the canvas can just bind to its width/height properties and manage their own drawing when these properties change.

There's a guide that I think that you may find useful for setting up a resizable canvas:
JavaFx tip - resizable canvas
Piece of code from the guide:
/**
* Tip 1: A canvas resizing itself to the size of
* the parent pane.
*/
public class Tip1ResizableCanvas extends Application {
class ResizableCanvas extends Canvas {
public ResizableCanvas() {
// Redraw canvas when size changes.
widthProperty().addListener(evt -> draw());
heightProperty().addListener(evt -> draw());
}
private void draw() {
double width = getWidth();
double height = getHeight();
GraphicsContext gc = getGraphicsContext2D();
gc.clearRect(0, 0, width, height);
gc.setStroke(Color.RED);
gc.strokeLine(0, 0, width, height);
gc.strokeLine(0, height, width, 0);
}
#Override
public boolean isResizable() {
return true;
}
#Override
public double prefWidth(double height) {
return getWidth();
}
#Override
public double prefHeight(double width) {
return getHeight();
}
}

Taken from http://werner.yellowcouch.org/log/resizable-javafx-canvas/: To make a JavaFx canvas resizable all that needs to be done is override the min/pref/max methods. Make it resizable and implement the resize method.
With this method no width/height listeners are necessary to trigger a redraw. It is also no longer necessary to bind the size of the width and height to the container.
public class ResizableCanvas extends Canvas {
#Override
public double minHeight(double width)
{
return 64;
}
#Override
public double maxHeight(double width)
{
return 1000;
}
#Override
public double prefHeight(double width)
{
return minHeight(width);
}
#Override
public double minWidth(double height)
{
return 0;
}
#Override
public double maxWidth(double height)
{
return 10000;
}
#Override
public boolean isResizable()
{
return true;
}
#Override
public void resize(double width, double height)
{
super.setWidth(width);
super.setHeight(height);
<paint>
}

The canvas class just needs to override isResizable() (everything else, which is suggested in other examples, is actually not necessary) :
public class ResizableCanvas extends Canvas
{
public boolean isResizable()
{
return true;
}
}
And in the Application the width and height properties of the canvas have to be bound to the canvas' parent:
#Override
public void start(Stage primaryStage) throws Exception
{
...
StackPane pane = new StackPane();
ResizableCanvas canvas = new ResizableCanvas(width, height);
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
pane.getChildren().add(_canvas);
...
}
Listeners can be added to the width in height properties, in order to redraw the canvas, when it is resized (but if you need that and where to place it, depends on your application):
widthProperty().addListener(this::paint);
heightProperty().addListener(this::paint);

I found that the above solutions did not work when the canvas is contained in a HBox, as the HBox would not shrink when window is resized because it would clip the canvas. Thus the HBox would expand, but never grow any smaller.
I used the following code to make the canvas fit the container:
public class ResizableCanvas extends Canvas {
#Override
public double prefWidth(double height) {
return 0;
}
#Override
public double prefHeight(double width) {
return 0;
}
}
And in my controller class:
#FXML
private HBox canvasContainer;
private Canvas canvas = new ResizableCanvas();
...
#Override
public void start(Stage primaryStage) throws Exception {
...
canvas.widthProperty().bind(canvasContainer.widthProperty());
canvas.heightProperty().bind(canvasContainer.
pane.getChildren().add(canvas);
...
}

this solution work only if we don't want to use FXML
I am not sure about the merits of a resizable canvas itself, neither with/without FXML. Generally you want to redraw something on it, and then you do not have a canvas (which has no content on its own), but you are back to application-specific code, just as like the question iself and most answers around do contain some re/draw() method.
Then you could throw away the separate class, do four bindings in FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="test.TestController">
<children>
<Pane fx:id="pane" VBox.vgrow="ALWAYS">
<children>
<Canvas fx:id="canvas" height="${pane.height}" width="${pane.width}"
onWidthChange="#redraw" onHeightChange="#redraw" />
</children>
</Pane>
</children>
</VBox>
and implement only redraw() in Java:
package test;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class TestController {
#FXML
private Canvas canvas;
#FXML
private void redraw() {
double w=canvas.getWidth();
double h=canvas.getHeight();
GraphicsContext gc=canvas.getGraphicsContext2D();
gc.clearRect(0, 0, w, h);
gc.beginPath();
gc.rect(10, 10, w-20, h-20);
gc.stroke();
}
}
(If needed, find a suitable main class and module-info in https://stackoverflow.com/a/58915071/7916438)

In order to achieve a resizable Canvas, I have placed my Canvas inside a Pane:
<Pane fx:id="canvasPane" >
<Canvas fx:id="canvas" />
</Pane>
I then bound the Canvas height and width properties to those of the Pane inside the initialize method of my controller:
canvas.heightProperty().bind(canvasPane.heightProperty());
canvas.widthProperty().bind(canvasPane.widthProperty());
This allows the Pane to interact with the LayoutManager while causing the Canvas to fill the Pane.

Related

how to let the image which I draw on a javafx canvas auto scale when I change the canvas size

How to make the image on the canvas auto scale when the canvas size is changed
public class Controller implements Initializable {
#FXML
private Canvas canvas;
#Override
public void initialize(URL location, ResourceBundle resources) {
canvas.resize(100, 100);
canvas.getGraphicsContext2D().fillRect(0, 0, 100, 100);
canvas.setOnMouseClicked(event -> {
canvas.widthProperty().setValue(200);
canvas.heightProperty().setValue(200);
//I want the rect auto scale but it not work how to make it
});
}
}
you could use
canvas.scaleXProperty().set(2);
canvas.scaleYProperty().set(2);
instead of canvas.widthProperty().setValue(200); ...

Set divider position of SplitPane

I want to set the divider of a SplitPane to a certain default position. This does not work, the divider stays in the middle:
public void start(Stage primaryStage) throws Exception{
SplitPane splitPane = new SplitPane(new Pane(), new Pane());
// Report changes to the divider position
splitPane.getDividers().get(0).positionProperty().addListener(
o -> System.out.println(splitPane.getDividerPositions()[0])
);
// Doesn't work:
splitPane.setDividerPositions(0.8);
// The docs seem to recommend the following (with floats instead of
// doubles, and with one number more than there are dividers, which is
// weird), but it doesn't work either:
//splitPane.setDividerPositions(0.8f, 0.2f);
primaryStage.setScene(new Scene(splitPane));
primaryStage.setMaximized(true);
primaryStage.show();
}
The output:
0.8
0.5
It suggests that something resets it to the middle.
How can I achieve this?
The issue seems to be that the divider position is reset when the SplitPane width is set during when the Stage is maximized. Set the divider positions afterwards by listening to the window's showing property as follows:
primaryStage.showingProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
splitPane.setDividerPositions(0.8);
observable.removeListener(this);
}
}
});
During Stage initialization, window size changes several times until layout is completed. Every change modifies divider positions. If you want to control divider positions, they have to be set after Stage is fully initialized:
private boolean m_stageShowing = false;
#Override
public void start(Stage primaryStage) throws Exception {
SplitPane splitPane = new SplitPane(new Pane(), new Pane());
ChangeListener<Number> changeListener = new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
splitPane.setDividerPositions(0.8);
if (m_stageShowing) {
observable.removeListener(this);
}
}
};
splitPane.widthProperty().addListener(changeListener);
splitPane.heightProperty().addListener(changeListener);
primaryStage.setScene(new Scene(splitPane));
primaryStage.setMaximized(true);
primaryStage.show();
m_stageShowing = true;
}
I had the same problem and the above solutions where not working reliably for me.
So I created a custom skin for SplitPane:
public class DumbSplitPaneSkin extends SplitPaneSkin {
public DumbSplitPaneSkin(SplitPane splitPane) {
super(splitPane);
}
#Override
protected void layoutChildren(double x, double y, double w, double h) {
double[] dividerPositions = getSkinnable().getDividerPositions();
super.layoutChildren(x, y, w, h);
getSkinnable().setDividerPositions(dividerPositions);
}
}
This skin can be used via css or by overriding SplitPane.createDefaultSkin(). You can also set programatically as splitPane.setSkin(new DumbSplitPaneSkin(splitPane));
As pointed out by others the issue is that the divider position is reset when the Stage is maximized.
You can prevent this by setting ResizableWithParent to false.
Example
Let's say you have a SplitPane with two nested containers inside. Here is the fxml extract:
<SplitPane fx:id="splitPane" dividerPositions="0.25">
<VBox fx:id="leftSplitPaneContainer" />
<FlowPane fx:id="rightSplitPaneContainer"/>
</SplitPane>
And here is the extract from the controller class:
#FXML
private SplitPane splitPane;
#FXML
private VBox leftSplitPaneContainer;
#FXML
private FlowPane rightSplitPaneContainer;
Then you simply can call SplitPane.setResizableWithParent() on both containers to prevent resetting the divider position:
public void initialize(){
SplitPane.setResizableWithParent(leftSplitPaneContainer, false);
SplitPane.setResizableWithParent(rightSplitPaneContainer, false);
}
The divider position will now remain at 0.25 even if you maximize the window.
No complicated listeners or overwriting of SplitPaneSkin involved.
You could just wrap the call setDividerPositions with
Platform.runLater(new Runnable() {
#Override
public void run() {
splitPane.setDividerPositions(0.8);
}
});
This is not 100% reliable solution because run() method is performed in JFX thread at unspecified time but it works properly for simple initialization cases.
Here is my result:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.*;
/**
* SplitPane, Dialogbox example
* #author Pataki István
*/
public class SimpleDocking extends Application {
private double splitPosition = 0;
private SplitPane rootPane = new SplitPane();
private MyDialog dialog;
private BorderPane dockedArea;
#Override
public void start(final Stage stage) throws Exception {
rootPane.setOrientation(Orientation.VERTICAL);
rootPane.setBorder(new Border(new BorderStroke(
Color.GREEN,
BorderStrokeStyle.SOLID,
new CornerRadii(5),
new BorderWidths(3))
));
dockedArea = new BorderPane(new TextArea("Some docked content"));
final FlowPane centerArea = new FlowPane();
final Button undockButton = new Button("Undock");
centerArea.getChildren().add(undockButton);
rootPane.getItems().addAll(centerArea, dockedArea);
stage.setScene(new Scene(rootPane, 300, 300));
stage.show();
dialog = new MyDialog(stage);
undockButton.disableProperty().bind(dialog.showingProperty());
undockButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
handler(stage);
}
});
}
private void handler(Stage stage) {
splitPosition = rootPane.getDividerPositions()[0];
rootPane.getItems().remove(dockedArea);
dialog.setOnHidden(windowEvent -> {
rootPane.getItems().add(dockedArea);
rootPane.setDividerPositions(splitPosition);
});
dialog.setContent(dockedArea);
dialog.show(stage);
}
private class MyDialog extends Popup {
private BorderPane root;
private MyDialog(Window parent) {
root = new BorderPane();
root.setPrefSize(200, 200);
root.setStyle("-fx-border-width: 1; -fx-border-color: gray");
root.setTop(buildTitleBar());
setX(parent.getX() + 50);
setY(parent.getY() + 50);
getContent().add(root);
}
public void setContent(Node content) {
root.setCenter(content);
}
private Node buildTitleBar() {
BorderPane pane = new BorderPane();
pane.setStyle("-fx-background-color: burlywood; -fx-padding: 5");
final Delta dragDelta = new Delta();
pane.setOnMousePressed(mouseEvent -> {
dragDelta.x = getX() - mouseEvent.getScreenX();
dragDelta.y = getY() - mouseEvent.getScreenY();
});
pane.setOnMouseDragged(mouseEvent -> {
setX(mouseEvent.getScreenX() + dragDelta.x);
setY(mouseEvent.getScreenY() + dragDelta.y);
});
Label title = new Label("My Dialog");
title.setStyle("-fx-text-fill: midnightblue;");
pane.setLeft(title);
Button closeButton = new Button("X");
closeButton.setOnAction(actionEvent -> hide());
pane.setRight(closeButton);
return pane;
}
}
private static class Delta {
double x, y;
}
public static void main(String[] args) throws Exception {
launch();
}
}
This is works like you wish.
Since you usually have at least something in splitpane, eg. vbox, just set min and max width and it will automatically set divider.
Platform.runlater(()->splitpane.setDividerPosition(0,0.8));
Absolutely does the trick for me. This sets the first split position of my horizontal splitpane to 80% of the parents width when opening the window.
While runlater() in many cases can lead JavaFX to do a little visible jitter at times, depending on the complexity of your GUI, in my case I haven't seen this happen.
Try
splitPane.setDividerPosition(0, percentage);
The parameters are setDividerPosition(int dividerIndex, double percentage)

Drawing user input on Image JavaFX

Suppose you have an app that displays user graphic (some kind of image) then you want to allow the user to draw some lines on this image. I have the following questions regarding such situation:
How would you accomplish that?
How would you get pixel coordinates for the image from the user drag events?
How would you update the image in real time?
I will give you an example of the exact opposite [erasing the Image on JavaFX]
which I suppose will be enough as a starter point for you:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class EraseImageonCanvas extends Application {
private Pane root = new Pane();
private void setCanvas(Canvas canvas, Image img) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(img, 0, 0,canvas.getWidth(), canvas.getHeight());
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Erasing the Image");
Rectangle rect = new Rectangle(400, 400);
drawBackground(rect);
root.getChildren().add(rect);
final Canvas canvas = new Canvas(200, 200);
canvas.setTranslateX(100);
canvas.setTranslateY(100);
//For local images use
//image = new Image(getClass().getResource(#Path#).openStream());
final Image image = new Image(
"http://kyllo.com.br/wp-content/uploads/2013/12/Faroeste-Cabloco.jpg"
);
setCanvas(canvas,image);
final GraphicsContext gc = canvas.getGraphicsContext2D();
// Clear away portions as the user drags the mouse
canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
gc.clearRect(e.getX() - 2, e.getY() - 2, 5, 5);
}
});
// Reset the Canvas when the user double-clicks
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (t.getClickCount() >1) {
setCanvas(canvas, image);
}
}
});
// Add the Canvas to the Scene, and show the Stage
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
//Draws the background with a RadialGradient
private void drawBackground(Rectangle rect) {
rect.setFill(new LinearGradient(0, 0, 1, 1, true,
CycleMethod.REFLECT,
new Stop(0, Color.RED),
new Stop(1, Color.YELLOW)));
}
public static void main(String[] args) {
launch(args);
}
}
Download it on gist
this Canvas tutorial by Oracle shows exactly what you want to accomplish in the "Interacting with the User" section.
It shows how you can add an EventHandler to the Canvas to handle MouseEvent such as MouseEvent.MOUSE_DRAGGED. The GraphicsContext is then used to get the x and y coordinates and draw on the canvas.
In order to use the Canvas outside the main Application class, you'd declare the Canvas in your .fxml file as such:
<BorderPane fx:controller="controllers.MyController"
xmlns:fx="http://javafx.com/fxml">
<Canvas fx:id="drawArea" height="..." width="..."/>
</BorderPane>
Then, on your MyController class:
public class MyController implements Initializable {
#FXML
private Canvas drawArea;
private GraphicsContext gc;
#Override
public void initialize(URL location, ResourceBundle resources) {
gc = drawArea.getGraphicsContext2D();
// Java 8 syntax, beware!
drawArea.setOnMouseDragged(event -> gc.fillRect(event.getX(), event.getY(), 5, 5));
}
}

JavaFX using width/height listener to update controls

I am using JavaFX SplitPanes on my application and I need to change the divider positions when the height and width changes, because I want to keep the divider positions fixed. My code is as follows:
scene.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
GUIController.resetMainSplitPane();
}
});
scene.heightProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
GUIController.resetMusicSplitPane();
}
});
and the resetSplitPane methods:
public static void resetMusicSplitPane() {
musicSplitPane.setDividerPosition(0, 0.7);
Util.err("height changed");
}
I do get the message 'height changed' however the divider positions have not been changed at all. I think this has something to do with JavaFX performing gui changes which override my changes. In other words; I change the divider position but JavaFX changes it back because its performing layout changes responding to the resizing of the window.
Normally, divider automatically adjusts itself whenever the scene is resized (i.e. the size of both sides of the divider increases/decreases). The only reason I can think of, on why, you are trying to set divider position, is not to increase or decrease the height/width of one side of the Divider. This can be achieved by using
SplitPane.setResizableWithParent(paneToBeFixed, Boolean.FALSE);
A small example to show how it works, (example given by Sergey in here)
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TestObjectArray extends Application {
#Override
public void start(Stage stage) {
final SplitPane root = new SplitPane();
final Pane paneFixed = new StackPane();
paneFixed.getChildren().add(new Text("Fixed"));
SplitPane.setResizableWithParent(paneFixed, Boolean.FALSE);
Pane paneFree = new StackPane();
paneFree.getChildren().add(new Text("Free"));
root.getItems().addAll(paneFixed, paneFree);
Scene scene = new Scene(root, 300, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Resizing images to fit the parent node

How do I get an image in an ImageView to automatically resize such that it always fits the parent node?
Here is a small code example:
#Override
public void start(Stage stage) throws Exception {
BorderPane pane = new BorderPane();
ImageView img = new ImageView("http://...");
//didn't work for me:
//img.fitWidthProperty().bind(new SimpleDoubleProperty(stage.getWidth()));
pane.setCenter(img);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
}
#Override
public void start(Stage stage) throws Exception {
BorderPane pane = new BorderPane();
ImageView img = new ImageView("http://...");
img.fitWidthProperty().bind(stage.widthProperty());
pane.setCenter(img);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
}
This is a better solution than binding the width property (better because often when binding a child to its container, it might not be possible to make the container smaller. At other ocasions the container might even automatically start growing).
The solution below relies on overriding an ImageView so that we can let it behave as 'resizable' and then providing implementations for the minimum ,preferred, and maximum width/heights. Also important is to actually implement the resize() call.
class WrappedImageView extends ImageView
{
WrappedImageView()
{
setPreserveRatio(false);
}
#Override
public double minWidth(double height)
{
return 40;
}
#Override
public double prefWidth(double height)
{
Image I=getImage();
if (I==null) return minWidth(height);
return I.getWidth();
}
#Override
public double maxWidth(double height)
{
return 16384;
}
#Override
public double minHeight(double width)
{
return 40;
}
#Override
public double prefHeight(double width)
{
Image I=getImage();
if (I==null) return minHeight(width);
return I.getHeight();
}
#Override
public double maxHeight(double width)
{
return 16384;
}
#Override
public boolean isResizable()
{
return true;
}
#Override
public void resize(double width, double height)
{
setFitWidth(width);
setFitHeight(height);
}
}
Use ScrollPane or simply Pane to overcome this problem:
Example:
img_view1.fitWidthProperty().bind(scrollpane_imageview1.widthProperty());
img_view1.fitHeightProperty().bind(scrollpane_imageview1.heightProperty());
If you want the ImageView to fit inside a windows frame, use this line of code:
imageView.fitWidthProperty().bind(scene.widthProperty()).
Note that I am using widthProperty of the scene not the stage.
Example:
import java.io.FileNotFoundException;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
public class MapViewer extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws FileNotFoundException {
String strTitle = "Titulo de la Ventana";
int w_width = 800;
int w_height = 412;
primaryStage.setTitle(strTitle);
primaryStage.setWidth(w_width);
primaryStage.setHeight(w_height);
Group root = new Group();
Scene scene = new Scene(root);
final ImageView imv = new ImageView("file:C:/Users/utp/Documents/1.2008.png");
imv.fitWidthProperty().bind(scene.widthProperty());
imv.setPreserveRatio(true);
root.getChildren().add(imv);
primaryStage.setScene(scene);
primaryStage.show();
}
}
The aspect radio of the stage (primaryStage) should be similar to that of the image (1.2008.png)
This is a calculated width method that removes the width of the scroll bar.
first:
myImageView.setPreserveRatio(true);
monitor scrollbar width changes:
scrollPane.widthProperty().addListener((observable, oldValue, newValue) -> {
myImageView.setFitWidth(newValue.doubleValue() - (oldValue.doubleValue() - scrollPane.getViewportBounds().getWidth()));
});
scrollBar width:
oldValue.doubleValue() - scrollPane.getViewportBounds().getWidth()
How do I set init width?
after primaryStage.show();
myImageView.setFitWidth(scrollPane.getViewportBounds().getWidth());
Fill the parent whit aspect ration, this fix the problem whit when parent height and width are not in proper ration like the image.
Image image = new Image(getClass().getResource(%path%).toString());
double ratio = image.getWidth() / image.getHeight();
double width = stage.getScene().getWidth();
ImageView imageView.setImage(image);
imageView.setFitWidth(width);
imageView.setFitHeight(width/ratio);
imageView.setPreserveRatio(true);

Resources