[英]Is it possible to keep the ScrollBar position after the pane is resized in JavaFX?
[英]Keep or set JavaFX Scrollpane position when content resized
我想設置 Scrollpane H 和 V 值,但在調整內部內容大小之后。
一些上下文- 我得到以下結構:
我將.setOnScroll(...)
(鼠標)事件附加到 StackPane,當它發生時,內部內容被調整大小(圖像)到一些值(又名縮放)。 我想添加 StackPane 最小大小綁定到滾動窗格視口大小和窗格大小綁定到圖像大小以保持一切正常工作。
到目前為止,一切正常,當內容更改大小時,滾動條會更新,並且一切都按照預期進行調整,但我想保留或將滾動條位置設置為某些值以保持縮放到之前的中間點(例如在 photoshop 中縮放) )。 在這種情況下,我在回調中添加了這一行來測試它:
scrollPane.setHvalue(0.5);
該函數本身可以工作,但稍后會被覆蓋(可能在重新計算布局時),這樣它總是錯誤的。 我通過事件測試了值
scrollPane.hvalueProperty().addListener((observable, oldvalue, newvalue) -> {
System.out.println(newvalue);
});
輸出是一種
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
這就是為什么我的結論是我成功設置了0.5
但后來的布局可能為調整大小的內容設置了不同的值,因為我將大小限制更改為Pane
和StackPane
。
是否有任何 onResize 事件來糾正或強制我自己的價值? 或在重新計算布局時安排滾動事件或我的更新的其他方式?
// 編輯
我清理了代碼,這是示例:
主程序
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為空
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
Sample.fxml (替換圖片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>
和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);
});
}
}
雖然我不熟悉 JavaFX,但有人會認為您可以通過在滾動條邊界發生變化時調整其大小來“強制執行您自己的值” 。 因此,改為在ChangeListener回調中執行scrollPane.setHvalue(0.5)
:
scrollPane.hvalueProperty().addListener((DoubleProperty observable, double oldvalue, double newvalue) -> {
if (newvalue != 0.5) {
scrollPane.setHvalue(0.5);
}
});
這是大約一年半前被問到的,但對於仍在尋找答案的任何人來說 -布局只覆蓋 hvalue 和 vvalue 屬性一次。 這意味着,您可以附加一個偵聽器,在否決更改值的嘗試后自行處置,而不是直接設置值。
也就是說,給定自動取消注冊偵聽器的包裝類..
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class ChangeOnceListener<T> implements ChangeListener<T> {
private ChangeListener<T> wrapped;
public ChangeOnceListener(ChangeListener<T> wrapped) {
this.wrapped = wrapped;
}
@Override
public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
observable.removeListener(this);
wrapped.changed(observable, oldValue, newValue);
}
}
.. OP 的場景可以通過以下內容修復。
pane.setOnScroll(e -> {
pane.hvalueProperty().addListener(new ChangeOnceListener<>((obv, old, neww) -> {
pane.setHvalue(0.5);
}));
pane.vvalueProperty().addListener(new ChangeOnceListener<>((obv, old, neww) -> {
pane.setVvalue(0.5);
}));
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.