JavaFX ScrollPane以编程方式移动视口 – 居中内容

我可以使用setVvalue(double)和setHvalue(double)方法在JavaFX ScrollPane中移动视口。 我正在努力做的是根据其位置将特定节点置于滚动窗格的内容中。 我尝试了各种localToScene()和boundsInParent()的组合。 我已阅读(很多)并看到了这个例子

如何滚动以使ScrollPane的内容中的节点可见?

哪个接近但不使对象居中只是让它们可见。 内置鼠标平移是很棒的,但我正在进行程序化平移的恶劣天气。

最终我需要能够进行缩放,所以我将实际形状放在一个组中,并将该组添加到滚动窗格内容中。 我想我应该对组进行缩放,然后我需要能够围绕组的中心进行缩放,以便我们回到操作并识别当前的中心位置。 任何可以提供的指针或示例都会非常感激。 上面链接中的代码示例是一个很好的SSCCE。

提前致谢,

安迪

我会尝试在没有代码的情况下解释这个,因为我不认为这是必要的。

假设滚动窗格的内容高度为h ,视口的高度为v 。 如果h = v ,那么内容将完全适合视口,并且您不需要滚动条。 在这种情况下(使用不可移动的滚动条),对于要居中的元素,需要将其定位在滚动窗格内容的中心。 您无法通过滚动将其移动到视口的中心。

现在考虑hv两倍(即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); }