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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15840513/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 21:00:55  来源:igfitidea点击:

JavaFX ScrollPane programmatically moving the viewport - centering content

javajavafx-2viewportpanningscrollpane

提问by andyw

I can use the setVvalue(double) and setHvalue(double) methods to move a viewport in a JavaFX ScrollPane. What I'm struggling to do is center a particular node in the content of the scroll pane based on its position. I've tried all sorts of combos of localToScene() and boundsInParent(). I've read (a lot) and seen this example

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

How to scroll to make a Node within the content of a ScrollPane visible?

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

Which is close but doesn't center the objects just puts them visible. Having the built in mouse panning is brilliant but I'm making heavy weather of the programmatic panning.

这是接近但不居中对象只是让它们可见。拥有内置的鼠标平移功能非常棒,但我正在使程序平移变得恶劣。

Ultimately I need to be able to do a zoom too so I have put the actual shapes in a Group and added the group to the scroll pane content. I think I'm supposed to do the zooming on the group and again I need to be able to zoom around the center of the group so we are back to manipulating and identifying the current center position. Any pointers or examples that can be provided would be really really appreciated. The code sample in the link above is a good SSCCE.

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

Thanks in advance,

提前致谢,

Andy

安迪

回答by H?vard Geithus

I'll try explaining this without code, because I don't think that's necessary here.

我会尝试在没有代码的情况下解释这一点,因为我认为这里没有必要。

Let's say the contentof your scrollpane has height h, and the height of the viewport is v. If h = v, then the content would fit perfectly into the viewport, and you would need no scrollbar. In this situation (with a non-movable scrollbar), for an element to be centered, it would need to be positioned at the center of the scrollpane's content. You can't move it to the viewport's center by means of scrolling.

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

Now consider hto be twice the size of v(i.e. h = 2v). In this situation the upper 1/4 and the lower 1/4 of the scrollpane's content could not be centered by scrolling.

现在考虑hv(ie h = 2v) 的两倍大小。在这种情况下,滚动窗格内容的上 1/4 和下 1/4 无法通过滚动居中。

(If you absolutely needed to center any component by scrolling you should consider padding your content pane, but we'll continue with the unpadded solution here)

(如果您绝对需要通过滚动将任何组件居中,您应该考虑填充内容窗格,但我们将在此处继续使用未填充的解决方案)

When you think about it, you'll realize the possible scrollable distance of the scrollbar is h - v, and you will scroll that amount by setting vvalueto 1.0.

当您考虑它时,您会意识到滚动条的可能滚动距离是h - v,并且您将通过设置vvalue为 1.0 来滚动该距离。

To center a point y(here the point y is a coordinate of the scrollpane's content pane) you can use the following vvalue:

要使一个点居中y(这里的 y 点是滚动窗格内容窗格的坐标),您可以使用以下 vvalue:

vvalue = (y - 0.5 * v) / (h - v) 

The nominator of this expression is the y-coordinate of what is shown at the top of the viewport when the point y is centered inside the viewport. The denominator is the total scrollable distance.

此表达式的提名者是当点 y 在视口内居中时在视口顶部显示的 y 坐标。分母是可滚动的总距离。

Edit: Adding some code anyway!

编辑:无论如何添加一些代码!

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)));
}

(Please note this assumes that the node is a direct child of the scrollpane's contentpane)

(请注意,这假定节点是滚动窗格内容窗格的直接子节点)

Hope this helps! :)

希望这可以帮助!:)

回答by none_

I know, it is little bit late, but maybe someone will find it useful.:)

我知道,有点晚了,但也许有人会发现它很有用。:)

As regards centering, this is not hard task it takes only little bit of math. You need two things. Size of whole content of ScrollPane (width and height of scrollable area, not only of frame with scrollbars, but complete as if no scrollbars are present) and position of centered object inside of ScrollPane content.

至于居中,这不是一项艰巨的任务,只需要一点点数学知识。你需要两件事。ScrollPane 的整个内容的大小(可滚动区域的宽度和高度,不仅是带有滚动条的框架,而且是完整的,就好像没有滚动条一样)以及 ScrollPane 内容内居中对象的位置。

You get size of content like this: scrollPane.getContent().getBoundsInLocal().getHeight() or .getWidth()

你会得到这样的内容大小: scrollPane.getContent().getBoundsInLocal().getHeight() or .getWidth()

And position of your object: node.getBoundsInParent().getMinY() or .getMinX()- minimal position of object. You can get its height and width too.

和物体的位置:node.getBoundsInParent().getMinY() or .getMinX()-物体的最小位置。你也可以得到它的高度和宽度。

Then calculate position of your center and move scrollbars according this formula.

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

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

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

回答by wlgrinfeld

Here is an example on how to center a pic that has been rotated. The code is in Scala and therefore applicable to ScalaFX, but JavaFX and Java developers will be able to read/use it :

这是有关如何将旋转的图片居中的示例。代码在 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);
  }