java - 如果小于视口(viewport),则扩展滚动 Pane 的内容

标签 java javafx

我想制作一个 ScrollPane,里面有一个自定义 Pane ,它有两个子项。一张用来放置我的元素,另一张仅用于背景。我想做到这一点,如果我缩小,并且内容小于视口(viewport),那么内容的大小会扩大,填充视口(viewport)中的新位置。如果我放大回来,它会保持不变,并且我现在在区域中拥有更大的内容。内容的新宽度将为:originalWidth + viewportWidth -scaledWidth。

我已经制作了网格,并且缩放可以工作,但我无法制作它来调整内容的大小。我尝试在缩放到当前视口(viewport)大小时设置内容大小,但它不起作用。

问题:

  • 我做错了什么?

布局在 fxml 中定义。另一个比ScrollPane内容设置为填充高度和宽度没有什么不寻常的。

自定义 Pane 类:

public class CustomPane extends StackPane implements Initializable {

@FXML
StackPane view;
@FXML
AnchorPane objectPane;
@FXML
GriddedPane background;

private DoubleProperty zoomFactor = new SimpleDoubleProperty(1.5);
private BooleanProperty altStatus = new SimpleBooleanProperty(false);

public CustomPane() {
    super();
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getClassLoader().getResource("CustomCanvas.fxml"));
    loader.setController(this);
    loader.setRoot(this);
    try {
        loader.load();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void initialize(URL location, ResourceBundle resources) {
    objectPane.setStyle("-fx-background-color: transparent");
    objectPane.prefWidthProperty().bind(prefWidthProperty());
    objectPane.prefHeightProperty().bind(prefHeightProperty());
    objectPane.getChildren().add(new Circle(512, 378, 20, Color.RED));
}

public void zoom(ScrollPane parent, Node node, double factor, double x, double y) {
    Timeline timeline = new Timeline(60);
    // determine scale
    double oldScale = node.getScaleX();
    double scale = oldScale * factor;
    double f = (scale / oldScale) - 1;

    // determine offset that we will have to move the node
    Bounds bounds = node.localToScene(node.getBoundsInLocal());


    double dx = (x - (bounds.getWidth() / 2 + bounds.getMinX()));
    double dy = (y - (bounds.getHeight() / 2 + bounds.getMinY()));

    // timeline that scales and moves the node

    timeline.getKeyFrames().clear();
    timeline.getKeyFrames().addAll(
            new KeyFrame(Duration.millis(100), new KeyValue(node.translateXProperty(), node.getTranslateX() - f * dx)),
            new KeyFrame(Duration.millis(100), new KeyValue(node.translateYProperty(), node.getTranslateY() - f * dy)),
            new KeyFrame(Duration.millis(100), new KeyValue(node.scaleXProperty(), scale)),
            new KeyFrame(Duration.millis(100), new KeyValue(node.scaleYProperty(), scale))
    );

    timeline.play();

    Bounds viewportBounds = parent.getViewportBounds();
    if (bounds.getWidth() < viewportBounds.getWidth()) {
        setMinWidth(viewportBounds.getWidth());
        requestLayout();
    }

    if (getMinHeight() < viewportBounds.getHeight()) {
        setMinHeight(viewportBounds.getHeight());
        requestLayout();
    }
}

public final Double getZoomFactor() {
    return zoomFactor.get();
}

public final void setZoomFactor(Double zoomFactor) {
    this.zoomFactor.set(zoomFactor);
}

public final DoubleProperty zoomFactorProperty() {
    return zoomFactor;
}

public boolean getAltStatus() {
    return altStatus.get();
}

public BooleanProperty altStatusProperty() {
    return altStatus;
}

public void setAltStatus(boolean altStatus) {
    this.altStatus.set(altStatus);
}
}

Controller 类:

public class Controller implements Initializable {
public ScrollPane scrollPane;
public CustomPane customPane;
public AnchorPane anchorPane;
public Tab tab1;

@Override
public void initialize(URL location, ResourceBundle resources) {
    scrollPane.viewportBoundsProperty().addListener((observable, oldValue, newValue) -> {
        customPane.setMinSize(newValue.getWidth(), newValue.getHeight());
    });
    scrollPane.requestLayout();

    tab1.getTabPane().addEventFilter(KeyEvent.KEY_PRESSED, event1 -> {
        if (event1.getCode() == KeyCode.ALT)
            customPane.setAltStatus(true);
    });
    tab1.getTabPane().addEventFilter(KeyEvent.KEY_RELEASED, event1 -> {
        if (event1.getCode() == KeyCode.ALT)
            customPane.setAltStatus(false);
    });

    scrollPane.setOnScroll(event -> {
        double zoomFactor = 1.5;
        if (event.getDeltaY() <= 0)
            zoomFactor = 1 / zoomFactor;
        customPane.setZoomFactor(zoomFactor);
        if (customPane.getAltStatus())
            customPane.zoom(scrollPane, customPane, customPane.getZoomFactor(), event.getSceneX(), event.getSceneY());
    });
}
}

GriddedPane 类:

public class GriddedPane extends Pane implements Initializable {

DoubleProperty gridWidth = new SimpleDoubleProperty(this, "gridWidth", 10);
DoubleProperty gridHeight = new SimpleDoubleProperty(this, "gridHeight", 10);

public GriddedPane() {
    super();
}

@Override
public void initialize(URL location, ResourceBundle resources) {

}

@Override
protected void layoutChildren() {
    getChildren().clear();
    setMouseTransparent(true);
    toBack();
    for (int i = 0; i < getHeight(); i += getGridWidth())
        getChildren().add(makeLine(0, i, getWidth(), i, "x"));
    for (int i = 0; i < getWidth(); i += getGridHeight())
        getChildren().add(makeLine(i, 0, i, getHeight(), "y"));
}

public void redrawLines() {
    for (Node n : getChildren()) {
        Line l = (Line) n;
        if (l.getUserData().equals("x")) {
            l.setEndX(getWidth());
        } else if (l.getUserData().equals("y")) {
            l.setEndY(getHeight());
        }
    }
}

private Line makeLine(double sx, double sy, double ex, double ey, String data) {
    final Line line = new Line(sx, sy, ex, ey);
    if (ex % (getGridWidth() * 10) == 0.0) {
        line.setStroke(Color.BLACK);
        line.setStrokeWidth(0.3);
    } else if (ey % (getGridHeight() * 10) == 0.0) {
        line.setStroke(Color.BLACK);
        line.setStrokeWidth(0.3);
    } else {
        line.setStroke(Color.GRAY);
        line.setStrokeWidth(0.1);
    }
    line.setUserData(data);
    return line;
}

public double getGridWidth() {
    return gridWidth.get();
}

public DoubleProperty gridWidthProperty() {
    return gridWidth;
}

public void setGridWidth(double gridWidth) {
    this.gridWidth.set(gridWidth);
}

public double getGridHeight() {
    return gridHeight.get();
}

public DoubleProperty gridHeightProperty() {
    return gridHeight;
}

public void setGridHeight(double gridHeight) {
    this.gridHeight.set(gridHeight);
}
}

最佳答案

不太确定我是否理解您想要实现的目标。但是,如果您的目标是使scrollPane的内容永远不会小于scrollPane的宽度,那么这对我来说是有效的:

public class Zoom extends Application {

    @Override
    public void start(Stage primaryStage) {

        ImageView imageView = new ImageView(new Image(someImage));
        ScrollPane scrollPane = new ScrollPane(imageView);
        StackPane root = new StackPane(scrollPane);

        imageView.fitWidthProperty().bind(scrollPane.widthProperty());
        imageView.fitHeightProperty().bind(scrollPane.heightProperty());

        scrollPane.setOnScroll(evt -> {
            boolean zoomOut = evt.getDeltaY() < 0;
            double zoomFactor = zoomOut ? -0.2 : 0.2;

            imageView.setScaleX(imageView.getScaleX() + zoomFactor);
            imageView.setScaleY(imageView.getScaleY() + zoomFactor);

            if (zoomOut) {
                Bounds bounds = imageView.getBoundsInParent();
                if (bounds.getWidth() < scrollPane.getWidth()) {
                    imageView.setScaleX(1);
                    imageView.setScaleY(1);

                }
            }
        });

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

关于java - 如果小于视口(viewport),则扩展滚动 Pane 的内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36669769/

相关文章:

java - javafx 独立应用程序中的 RMI 连接

java - 如何将 JPanel 嵌入到 JavaFX Pane 中?

java - Jetty 可以在热部署时获取对 WEB-INF/lib 的更改吗?如何?

java - 无法初始化类 org.apache.xerces.jaxp.datatype.xmlgregoriancalendar

java - 定时器和loop()/noLoop()的问题

java - 在 JavaFX 中切换主题

java - 使用eclipse和scene builder将数据从一个选项卡发送到javafx 8中的另一个选项卡

java - 如何获取Java类的真实模块依赖关系?

java - VertxexecuteBlocking 使用事件循环而不是工作线程

java - 如何在选项卡的右侧显示关闭按钮