![](/img/trans.png)
[英]JavaFX - ScrollPane with StackPane - StackPane's children not layouted when viewportBounds change
[英]JavaFX ScrollPane update viewportBounds on scroll
我试图在JavaFX中显示一个非常大的图像。 为此,我将图像分为几个较小的图像,并且仅加载/显示了ScrollPane
中可见的部分。
为了检测ScrollPane
可见的区域,我将侦听器添加到ScrollPane.viewportBounds
。 但是, viewportBounds
仅在调整窗口大小时更新,而在滚动滚动条时不会更新。
如果要在大图像上滚动,则还需要在滚动滚动条时更新viewportBounds
。 我该怎么做呢?
我下面有一些测试代码。 点击Button
,而不是通过作品ChangeListener
- left
和right
仍然使用不变ChangeListener
。
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TestClass extends Application {
@Override
public void start(Stage stage) {
VBox vbox = new VBox();
ScrollPane scrollPane = new ScrollPane(new Rectangle(1000, 700, Color.GREEN));
scrollPane.setPrefSize(500, 300);
// Using a ChangeListener on viewportBounds doesn't work.
scrollPane.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() {
@Override
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldBounds, Bounds bounds) {
int left = -1 * (int) bounds.getMinX();
int right = left + (int) bounds.getWidth();
System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
}
});
// Neither does this.
scrollPane.hvalueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
Bounds bounds = scrollPane.getViewportBounds();
int left = -1 * (int) bounds.getMinX();
int right = left + (int) bounds.getWidth();
System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
}
});
// Clicking the button works.
Button printButton = new Button("Print");
printButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = scrollPane.getViewportBounds();
int left = -1 * (int) bounds.getMinX();
int right = left + (int) bounds.getWidth();
System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
event.consume();
}
});
vbox.getChildren().addAll(scrollPane, printButton);
Scene scene = new Scene(vbox, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
编辑更新的测试代码。
viewportBounds
只是视口的边界,即可见的可滚动内容部分的边界。 这些在滚动时不会改变(但通常会在您调整窗口大小时随着大小改变而改变)。
要响应在视口中移动的滚动内容,您需要观察ScrollPane
的hvalueProperty
和vvalueProperty
。 您可以使用相同的更改侦听器,并对参数的类型进行较小的修改:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TestClass extends Application {
@Override
public void start(Stage stage) {
ScrollPane scrollPane = new ScrollPane(new Rectangle(1000, 700, Color.GREEN));
scrollPane.setPrefSize(500, 300);
ChangeListener<Object> changeListener = new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
Bounds bounds = scrollPane.getViewportBounds();
int left = -1 * (int) bounds.getMinX();
int right = left + (int) bounds.getWidth();
System.out.println("hval:" + scrollPane.getHvalue() + " left:" + left + " right:" + right);
}
};
scrollPane.viewportBoundsProperty().addListener(changeListener);
scrollPane.hvalueProperty().addListener(changeListener);
scrollPane.vvalueProperty().addListener(changeListener);
Scene scene = new Scene(scrollPane, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
请注意,滚动时,视口(当然)不会在其父视窗中移动; 滚动窗格内容的可见部分将更改。 我没有一种简单的方法可以计算内容的可见部分:您只需要做一点数学即可。
(注意:也许有一种更简单的方法,但是我看不到。)
ScrollPane允许应用程序设置用于在水平和垂直方向上放置内容的当前值,最小值和最大值。 这些值按比例映射到所包含节点的layoutBounds上。
所以您必须对此稍作解释,但这意味着
(hvalue - hmin) / (hmax - hmin) = hoffset / freeHspace
其中hmin
, hvalue
,和hmax
是滚动窗格属性值, hoffset
是由哪个内容被水平地移位量, freeHspace
是总水平量的内容可以移动。 这是
freeHspace = contentWidth - viewportWidth
(显然,如果contentWidth
<= viewportWidth
,则不可能进行水平滚动,并且hoffset
必须为零。)
因此,如果要水平偏移量
hoffset = Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin)
垂直偏移也适用类似的公式。
因此,您可以将上面的更改侦听器替换为
ChangeListener<Object> changeListener = new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
double hmin = scrollPane.getHmin();
double hmax = scrollPane.getHmax();
double hvalue = scrollPane.getHvalue();
double contentWidth = content.getLayoutBounds().getWidth();
double viewportWidth = scrollPane.getViewportBounds().getWidth();
double hoffset =
Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin);
double vmin = scrollPane.getVmin();
double vmax = scrollPane.getVmax();
double vvalue = scrollPane.getVvalue();
double contentHeight = content.getLayoutBounds().getHeight();
double viewportHeight = scrollPane.getViewportBounds().getHeight();
double voffset =
Math.max(0, contentHeight - viewportHeight) * (vvalue - vmin) / (vmax - vmin);
System.out.printf("Offset: [%.1f, %.1f] width: %.1f height: %.1f %n",
hoffset, voffset, viewportWidth, viewportHeight);
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.