我有一个需要放大ScrollPane的应用程序,但是我目前仍然面临着两个挑战.为了复制问题,我写了一个小应用程序ZoomApp,你会发现下面的代码.它的有限功能允许在某些任意形状上放大和缩小(使用Ctrl鼠标滚轮).当缩放的内容增长到窗口的边界之外时,滚动条应该是禁止的.
挑战1.
当滚动条显示为innerGroup在大小上升的结果时,ScrollEvent不再到达我的ZoomHandler.相反,我们开始向下滚动窗口,直到它到达底部,当再次缩放时工作正常.我以为也许
scrollPane.setPannable(false);
会有所作为,但不会.如何避免这种不必要的行为?
挑战2.
如何将innerGroup放在scrollPane的中心位置,而不需要在内部组的左上角绘制一个像素到所需的三角形到正方形?
根据JavaDoc for ScrollPane的说法:“如果应用程序希望滚动是基于节点的可视边界(对于缩放内容等),则需要将滚动节点包裹在组”中.这就是为什么我在ScrollPane里面有一个innerGroup和一个outerGroup.
引导我解决这个问题的任何建议都非常受到JavaFX新手的赞赏.
import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.SceneBuilder; import javafx.scene.control.ScrollPane; import javafx.scene.input.ScrollEvent; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; /** * Demo of a challenge I have with zooming inside a {@code ScrollPane}. * <br> * I am running JavaFx 2.2 on a Mac. {@code java -version} yields: * <pre> * java version "1.7.0_09" * Java(TM) SE Runtime Environment (build 1.7.0_09-b05) * Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02,mixed mode) * </pre> * 6 rectangles are drawn,and can be zoomed in and out using either * <pre> * Ctrl + Mouse Wheel * or Ctrl + 2 fingers on the pad. * </pre> * It reproduces a problem I experience inside an application I am writing. * If you magnify to {@link #MAX_SCALE},an interesting problem occurs when you try to zoom back to {@link #MIN_SCALE}. In the beginning * you will see that the {@code scrollPane} scrolls and consumes the {@code ScrollEvent} until we have scrolled to the bottom of the window. * Once the bottom of the window is reached,it behaves as expected (or at least as I was expecting). * * @author Skjalg Bjørndal * @since 2012.11.05 */ public class ZoomApp extends Application { private static final int WINDOW_WIDTH = 800; private static final int WINDOW_HEIGHT = 600; private static final double MAX_SCALE = 2.5d; private static final double MIN_SCALE = .5d; private class ZoomHandler implements EventHandler<ScrollEvent> { private Node nodeToZoom; private ZoomHandler(Node nodeToZoom) { this.nodeToZoom = nodeToZoom; } @Override public void handle(ScrollEvent scrollEvent) { if (scrollEvent.isControlDown()) { final double scale = calculateScale(scrollEvent); nodeToZoom.setScaleX(scale); nodeToZoom.setScaleY(scale); scrollEvent.consume(); } } private double calculateScale(ScrollEvent scrollEvent) { double scale = nodeToZoom.getScaleX() + scrollEvent.getDeltaY() / 100; if (scale <= MIN_SCALE) { scale = MIN_SCALE; } else if (scale >= MAX_SCALE) { scale = MAX_SCALE; } return scale; } } public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { final Group innerGroup = createSixRectangles(); final Group outerGroup = new Group(innerGroup); final ScrollPane scrollPane = new ScrollPane(); scrollPane.setContent(outerGroup); scrollPane.setOnScroll(new ZoomHandler(innerGroup)); StackPane stackPane = new StackPane(); stackPane.getChildren().add(scrollPane); Scene scene = SceneBuilder.create() .width(WINDOW_WIDTH) .height(WINDOW_HEIGHT) .root(stackPane) .build(); stage.setScene(scene); stage.show(); } private Group createSixRectangles() { return new Group( createRectangle(0,0),createRectangle(110,createRectangle(220,createRectangle(0,110),220),220) ); } private Rectangle createRectangle(int x,int y) { Rectangle rectangle = new Rectangle(x,y,100,100); rectangle.setStroke(Color.ORANGERED); rectangle.setFill(Color.ORANGE); rectangle.setStrokeWidth(3d); return rectangle; } }
解决方法
好的,所以我终于找到了解决我的问题的办法.
只是用行代替
scrollPane.setOnScroll(new ZoomHandler(innerGroup));
同
scrollPane.addEventFilter(ScrollEvent.ANY,new ZoomHandler(innerGroup));
它现在按预期工作.不需要神秘的矩形或其他黑客.
那么下一个问题是为什么?根据this excellent article on Processing Events,
An event filter is executed during the event capturing phase.
而
An event handler is executed during the event bubbling phase.
我认为这是有什么区别的.