I want to set Scrollpane H and V values but after inner content was resized.
Some context - I got following structure:
I attached .setOnScroll(...)
(mouse) event to StackPane and when it happens, the inner content is resized (image) to some values (aka zoom). I want to add that StackPane min size is binded to scrollpane viewport size and Pane size binded to image size to keep everything working properly.
Up to this point everything works fine, when content changes size, scrollbars are updated and everything adjust as it supossed to be but I want to keep or rather set scrollbars positions to some values to keep zooming to previous middle point (something like zoom in photoshop). In this case I added this line in my callback to test it:
scrollPane.setHvalue(0.5);
The function itself works but it's overridden later (probably when layout recalculate) and this way it's always wrong. I tested values via event
scrollPane.hvalueProperty().addListener((observable, oldvalue, newvalue) -> {
System.out.println(newvalue);
});
and the output is kind of
0.5
0.45172283226050736
0.5
0.45196805476326296
0.5
0.4522703818369453
// or this when scaled down
0.5
0.5591296121097445
0.5
0.5608324439701174
That's why my conclusion is that I succeed setting 0.5
but later layout probably set different value for resized content becuase i changed size restrictions to Pane
and StackPane
.
My question then how should I do this?
Is there any onResize event to corrent or force my own value? or other way to schedule scroll event or my updates to be done when layout is recalculated?
// EDIT
I cleaned the code and here is example:
Main.java
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Sample.fxml"));
Scene scene = new Scene(root,800,600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
application.css is empty
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
Sample.fxml (replace image url)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
<center>
<ScrollPane fx:id="scrollPane" prefHeight="600.0" prefWidth="800.0" BorderPane.alignment="CENTER">
<content>
<StackPane fx:id="stackPane">
<children>
<Pane fx:id="clipContainer">
<children>
<ImageView fx:id="img" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../../../../Desktop/sample.jpg" />
</image>
</ImageView>
</children>
</Pane>
</children>
</StackPane>
</content>
</ScrollPane>
</center>
</BorderPane>
and SampleController.java
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
public class SampleController implements Initializable {
@FXML
private Pane clipContainer;
@FXML
private StackPane stackPane;
@FXML
private ImageView img;
@FXML
private ScrollPane scrollPane;
final double SCALE_DELTA = 1.1;
private double imgW;
private double imgH;
private double scale = 1;
@Override
public void initialize(URL location, ResourceBundle resources) {
clipContainer.setStyle("-fx-background-color: #999999");
stackPane.setStyle("-fx-background-color: #CCCCCC");
imgW = img.getImage().getWidth();
imgH = img.getImage().getHeight();
// bind max and min to adjust to new image bounds
stackPane.minWidthProperty().bind(Bindings.createDoubleBinding(() ->
scrollPane.getViewportBounds().getWidth(), scrollPane.viewportBoundsProperty()
));
stackPane.minHeightProperty().bind(Bindings.createDoubleBinding(() ->
scrollPane.getViewportBounds().getHeight(), scrollPane.viewportBoundsProperty()
));
clipContainer.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
img.getFitWidth(), img.fitWidthProperty()
));
clipContainer.maxHeightProperty().bind(Bindings.createDoubleBinding(() ->
img.getFitHeight(), img.fitHeightProperty()
));
// initial scale
img.setFitWidth(imgW * 1);
img.setFitHeight(imgH * 1);
// on mouse scroll
stackPane.setOnScroll(event -> {
event.consume();
if (event.getDeltaY() == 0) {
return;
}
double scaleFactor =
(event.getDeltaY() > 0)
? SCALE_DELTA
: 1/SCALE_DELTA;
scale *= scaleFactor;
img.setFitWidth(imgW * scale);
img.setFitHeight(imgH * scale);
// HERE i want to do something to keep my image where it was
scrollPane.setHvalue(0.5);
});
scrollPane.hvalueProperty().addListener((observable, oldvalue, newvalue) -> {
System.out.println(newvalue);
});
}
}
ScrollPane
? UsingScrollPane
to resize instead of scrolling up/down/left/right sounds like bad usage to me.setOnScroll()
can be used on any node. – Jai