简体   繁体   English

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

[英]JavaFX ScrollPane programmatically moving the viewport - centering content

I can use the setVvalue(double) and setHvalue(double) methods to move a viewport in a JavaFX ScrollPane. 我可以使用setVvalue(double)和setHvalue(double)方法在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(). 我尝试了各种localToScene()和boundsInParent()的组合。 I've read (a lot) and seen this example 我已阅读(很多)并看过这个例子

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 安迪

I'll try explaining this without code, because I don't think that's necessary here. 我会尝试在没有代码的情况下解释这个,因为我不认为这是必要的。

Let's say the content of your scrollpane has height h , and the height of the viewport is v . 假设滚动窗格的内容高度为h ,视口的高度为v If h = v , then the content would fit perfectly into the viewport, and you would need no scrollbar. 如果h = v ,则内容将完全适合视口,并且您不需要滚动条。 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. 您无法通过滚动将其移动到视口的中心。

Now consider h to be twice the size of v (ie h = 2v ). 现在考虑hv两倍(即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. 在这种情况下,滚动窗格内容的上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 vvalue to 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. 当y点在视口内居中时,此表达式的分母是视口顶部显示的y坐标。 The denominator is the total scrollable distance. 分母是总可滚动距离。

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! 希望这可以帮助! :) :)

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. 对象的位置: node.getBoundsInParent().getMinY() or .getMinX() - 对象的最小位置。 You can get its height and width too. 你也可以得到它的高度和宽度。

Then calculate position of your center and move scrollbars according this formula. 然后根据此公式计算中心的位置并移动滚动条。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM