滚动时JavaFX ScrollPane更新viewportBounds [英] JavaFX ScrollPane update viewportBounds on scroll
问题描述
我想要在JavaFX中显示一个非常大的图像。为此,我将图像拆分为几个较小的图像,并仅加载/显示我的 ScrollPane
中可见的部分。
I have a very large image I'm trying to display in JavaFX. To do this, I've split the image into several smaller ones, and only loading/displaying the parts that are visible in my ScrollPane
.
要检测 ScrollPane
中可见的区域,我将监听器添加到 ScrollPane.viewportBounds
。但是, viewportBounds
仅在我调整窗口大小时更新,但在滚动滚动条时不会更新。
To detect the area visible in the ScrollPane
, I'm adding listener to ScrollPane.viewportBounds
. However, the viewportBounds
is only updated when I resize the window, but not when I scroll the scroll bars.
如果我要在我的大图像上滚动,我需要在滚动条形滚动时更新 viewportBounds
。我该怎么做?
If I'm to be scrolling around my large image, I need viewportBounds
to be updated when I scroll the scroll bars as well. How do I do this?
我在下面有一些测试代码。单击按钮
有效,但不能通过 ChangeListener
- 左
和右
使用 ChangeListener
仍保持不变。
I have some test code below. Clicking the Button
works, but not through the ChangeListener
- left
and right
still remain unchanged using the 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
只是视口的边界,即可滚动内容的可见部分的边界。滚动时这些不会改变(但是随着大小的改变,通常会随着窗口大小的改变而改变)。
The viewportBounds
are just the bounds of the viewport, i.e. the bounds of the portion of the scrollable content that is visible. These won't change as you scroll (but will usually change as you resize the window, as the size changes).
要响应在内部移动的滚动内容viewport,你需要观察 ScrollPane的
。您可以使用相同的更改侦听器,只需对参数类型进行少量修改: hvalueProperty
和 vvalueProperty
To respond to the scroll content being moved within the viewport, you need to observe the hvalueProperty
and vvalueProperty
of the ScrollPane
. You can use the same change listener, with minor modifications to the types of the parameters:
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);
}
}
请注意,滚动时,视口(当然)不会在其父母身上移动;滚动窗格内容的可见部分会发生变化。我没有简单的方法来计算内容的可见部分:你只需要做一些数学运算。
Note that when you scroll, the viewport (of course) does not move in its parent; the visible portion of the scrollpane's content changes. There's no simple way I can see to compute the visible portion of the content: you just have to do a bit of maths.
(注意:可能有一种更简单的方法,但我无法看到它。)
(Note: there may be an easier way, but I am not able to see it.)
The API docs for ScrollPane
state
ScrollPane允许应用程序设置当前,最小和
用于在水平和
垂直方向上定位内容的最大值。这些值按比例映射到包含节点的
layoutBounds。
The ScrollPane allows the application to set the current, minimum, and maximum values for positioning the contents in the horizontal and vertical directions. These values are mapped proportionally onto the layoutBounds of the contained node.
所以你必须解释一下,但是这意味着
So you have to interpret that a bit, but it means that
(hvalue - hmin) / (hmax - hmin) = hoffset / freeHspace
其中 hmin
, hvalue
和 hmax
是滚动窗格属性值, hoffset
是内容水平移动的数量, freeHspace
是内容可以移动的总水平量。这是
where hmin
, hvalue
, and hmax
are scroll pane property values, hoffset
is the amount by which the content is shifted horizontally, and freeHspace
is the total horizontal amount the content can move. This is
freeHspace = contentWidth - viewportWidth
(显然,如果 contentWidth
< = viewportWidth
,则无法进行水平滚动, hoffset
必然为零。)
(Obviously if contentWidth
<= viewportWidth
, no horizontal scrolling is possible, and hoffset
is necessarily zero.)
所以如果你想要水平偏移
So if you want the horizontal offset you have
hoffset = Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin)
类似的公式适用于垂直偏移。
And a similar formula holds for the vertical offset.
所以你可以用
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);
}
};
这篇关于滚动时JavaFX ScrollPane更新viewportBounds的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!