JavaFX ScrollPane 编程移动视口 - 将内容居中

9
我可以使用setVvalue(double)和setHvalue(double)方法在JavaFX ScrollPane中移动视口。 我正在努力根据其位置将特定节点居中于滚动窗格的内容中。 我尝试了各种本地转场(localToScene())和父级边界(boundsInParent())的组合。 我读了很多资料并看了这个例子。

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

它接近但不是居中对象只是让它们可见。 内置的鼠标平移非常棒,但编程平移却很困难。 最终,我还需要能够进行缩放,因此我将实际形状放入一个Group中,并将该组添加到滚动窗格内容中。 我认为应该在组上进行缩放,再次需要能够围绕组的中心进行缩放,因此我们又回到了操作和识别当前中心位置的问题。 如果提供指针或示例,将非常感激。 上面链接中的代码示例是一个好的SSCCE。

提前致谢,

安迪


嗯,我觉得如果问题非常非常简单而提问者又很蠢或者问题非常困难而没有人愿意去解答,那么在StackOverflow上这些问题似乎都不会得到回答!我非常乐意被告知我是个白痴!! - andyw
3个回答

19

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

假设您的滚动窗格的内容高度为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)));
}
请注意,这假定节点是滚动窗格内容面板的直接子级。
希望这有所帮助! :)

4
谢谢,即使节点不是滚动面板的直接子节点,这个方法也完美地起作用了:我中间还有一个AnchorPane。 - user3804769
1
非常感谢!这非常有帮助! - undefined

0

我知道,可能有人会发现这很有用:)

关于居中,这并不是一个难题,只需要一点点的数学知识。你需要两个东西。ScrollPane整个内容的大小(可滚动区域的宽度和高度,不仅仅是带滚动条的框架,而是完全的没有滚动条的情况下),以及居中对象在ScrollPane内容中的位置。

您可以通过以下方式获取内容大小:scrollPane.getContent().getBoundsInLocal().getHeight() or .getWidth()

以及您的对象的位置:node.getBoundsInParent().getMinY() or .getMinX() - 对象的最小位置。您还可以获取其高度和宽度。

然后根据此公式计算出中心位置,并移动滚动条。

double vScrollBarPosition = scrollPane.getVMax() * (yPositionOfCenter / heightOfScrollPaneContent)


1
这不会精确地居中组件,因为您假定滚动窗格可以滚动整个内容面板的高度。 - Håvard Geithus

-1

这里有一个关于如何居中旋转后的图片的示例。 代码是用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);
  }

3
在回答问题时,您需要对代码进行格式化,否则它将完全无法阅读。 - Evan Frisch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接