我可以使用setVvalue(double)和setHvalue(double)方法来移动JavaFX ScrollPane中的viewport。我正在努力做的是根据其位置在滚动窗格的内容中居中一个特定节点。我已经尝试了各种组合的localToScene()和boundsInParent()。我读过(很多)并且看过这个例子
如何滚动使一个节点内的ScrollPane的内容可见?
接近但不居中,只是让它们可见。拥有内置的鼠标平移功能是非常棒的,但我对程序化的平移功能感到不满。
最终我需要能够做缩放,所以我已经把实际的形状在一个组,并将组添加到滚动窗格的内容。我想我应该对组进行缩放我需要能够围绕组的中心进行缩放所以我们又回到了操作和识别当前中心位置的问题上。任何指针或例子,可以提供将是非常非常感激。上面链接中的代码示例是一个很好的SSCCE。
提前感谢,
安迪我将尝试不用代码来解释,因为我认为这里没有必要。
假设滚动窗格的内容的高度为h
,而视口的高度为v
。如果是h = v
,那么内容将完全适合视窗,你不需要滚动条。在这种情况下(使用不可移动的滚动条),要使元素居中,需要将其放置在滚动窗格内容的中心。你不能通过滚动把它移动到视窗的中心。
现在考虑h
的大小是v
(即h = 2v
)的两倍。在这种情况下,滚动窗格的上1/4和下1/4的内容不能通过滚动来居中。
(如果您绝对需要通过滚动来居中任何组件,您应该考虑填充您的内容窗格,但我们将继续使用无填充的解决方案)
考虑一下,您会意识到滚动条可能的可滚动距离是h - v
,您将通过将vvalue
设置为1.0来滚动这个量。
要居中y
点(这里的点y是滚动窗格的内容窗格的坐标),可以使用以下vvalue:
vvalue = (y - 0.5 * v) / (h - v)
该表达式的提名者是当点y位于视口中居中时在视口中顶部显示的y坐标。分母为可滚动的总距离。
编辑:添加一些代码无论如何!
public void centerNodeInScrollPane(ScrollPane scrollPane, Node node) {
double h = scrollPane.getContent().getBoundsInLocal().getHeight();
double y = (node.getBoundsInParent().getMaxY() +
node.getBoundsInParent().getMinY()) / 2.0;
double v = scrollPane.getViewportBounds().getHeight();
scrollPane.setVvalue(scrollPane.getVmax() * ((y - 0.5 * v) / (h - v)));
}
(请注意,这假设节点是滚动窗格的内容窗格的直接子节点)
希望这对你有帮助!:)
我知道,这有点晚了,但也许有人会发现它有用。
至于定心,这并不难,只需要一点点数学知识。你需要两样东西。ScrollPane整体内容的大小(可滚动区域的宽度和高度,不仅是有滚动条的框架,也包括没有滚动条的框架)和ScrollPane内容中居中对象的位置。
你得到内容的大小像这样:scrollPane.getContent().getBoundsInLocal().getHeight() or .getWidth()
对象的位置:node.getBoundsInParent().getMinY() or .getMinX()
-对象的最小位置。你也可以得到它的高度和宽度。
然后计算中心位置并根据此公式移动滚动条
double vScrollBarPosition = scrollPane.getVMax() * (yPositionOfCenter / heightOfScrollPaneContent)
下面是一个关于如何居中旋转图片的示例。代码是Scala的,因此适用于ScalaFX,但JavaFX和Java开发人员将能够阅读/使用它:
def turnPic(angle:Int) {
val virtualSurfaceMultiplier = 8.0;
currentAngle = ( floor((currentAngle % 360) / 90) * 90 + angle + 360 ) % 360;
imageView.rotate = currentAngle;
val orientationSwitched = (currentAngle / 90) % 2 > 0;
val width= scrollPane.getViewportBounds().getWidth();
val height= scrollPane.getViewportBounds().getHeight();
val viewPort = new Rectangle2D(0, 0, image.width.value, image.height.value);
imageView.setViewport(viewPort);
imageView.fitHeight.value = virtualSurfaceMultiplier * height;
imageView.fitWidth.value = 0 //virtualSurfaceMultiplier * width;
def centerV(picHeight:Double) {
val node = scrollPane.getContent();
val totalHeight = scrollPane.getContent().getBoundsInLocal().getHeight();
val offsetToCenter = (node.getBoundsInParent().getMaxY() + node.getBoundsInParent().getMinY()) / 2.0;
val viewportWidth = scrollPane.getViewportBounds().getHeight();
val res = (scrollPane.getVmax() * ((offsetToCenter - 0.5 * picHeight * zoomFactor) / (totalHeight - picHeight * zoomFactor)));
scrollPane.setVvalue(res);
}
def centerH(picWidth:Double) {
val node = scrollPane.getContent();
val totalWidth = scrollPane.getContent().getBoundsInLocal().getWidth();
val offsetToCenter = (node.getBoundsInParent().getMaxX() + node.getBoundsInParent().getMinX()) / 2.0;
val viewportWidth = scrollPane.getViewportBounds().getWidth();
val res = (scrollPane.getHmax() * ((offsetToCenter - 0.5 * picWidth * zoomFactor) / (totalWidth - picWidth * zoomFactor)));
scrollPane.setHvalue(res);
System.err.println(s"trace centerH> $FILE:$LINE:" + " totalWidth:" + totalWidth + " offsetToCenter=" + offsetToCenter + " vWidth=" + viewportWidth + "res=" + res);
}
if (orientationSwitched) {
zoomFactor = 1.0 / (virtualSurfaceMultiplier * image.width.value / image.height.value);
centerH(height);
centerV(width);
}
else
{
zoomFactor = 1.0 / virtualSurfaceMultiplier;
centerH(width);
centerV(height);
}
imageView.setScaleX(zoomFactor);
imageView.setScaleY(zoomFactor);
}