当前位置: 首页 > 知识库问答 >
问题:

调整内容大小时保持或设置JavaFX滚动窗格位置

羊舌旭尧
2023-03-14

我想设置Scrollpane H和V值,但内部内容调整大小后。

一些背景-我得到了以下结构:

我附上了<代码>。setOnScroll(…)

到目前为止,一切都很好,当内容大小发生变化时,滚动条会更新,所有内容都会按预期进行调整,但我想保持或更确切地说,将滚动条位置设置为一些值,以保持缩放到上一个中点(类似于photoshop中的缩放)。在本例中,我在回调中添加了这一行来测试它:

scrollPane.setHvalue(0.5);

函数本身可以工作,但稍后会被覆盖(可能是在重新计算布局时),这样它总是错误的。我通过事件测试值

scrollPane.hvalueProperty().addListener((observable, oldvalue, newvalue) -> {
     System.out.println(newvalue);
});

输出是一种

0.5
0.45172283226050736
0.5
0.45196805476326296
0.5
0.4522703818369453

// or this when scaled down 

0.5
0.5591296121097445
0.5
0.5608324439701174

这就是为什么我的结论是我成功地设置了0.5,但后来布局可能会为调整大小的内容设置不同的值,因为我将大小限制更改为窗格和堆栈窗格。

是否有任何onResize事件来关联或强制我自己的值?或其他方式来安排滚动事件或在重新计算布局时完成我的更新?

//编辑

我清理了代码,这是示例:

主要的Java语言

package application;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Sample.fxml"));
            Scene scene = new Scene(root,800,600);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

应用css为空

/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */

Sample.fxml(替换图片url)

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>

<BorderPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
   <center>
      <ScrollPane fx:id="scrollPane" prefHeight="600.0" prefWidth="800.0" BorderPane.alignment="CENTER">
         <content>
            <StackPane fx:id="stackPane">
               <children>
                  <Pane fx:id="clipContainer">
                     <children>
                        <ImageView fx:id="img" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../../../../Desktop/sample.jpg" />
                           </image>
                        </ImageView>
                     </children>
                  </Pane>
               </children>
            </StackPane>
         </content>
      </ScrollPane>
   </center>
</BorderPane>

和SampleController。Java语言

package application;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;


public class SampleController implements Initializable {


    @FXML
    private Pane clipContainer;

    @FXML
    private StackPane stackPane;

    @FXML
    private ImageView img;

    @FXML
    private ScrollPane scrollPane;

    final double SCALE_DELTA = 1.1;

    private double imgW;
    private double imgH;
    private double scale = 1;


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


        clipContainer.setStyle("-fx-background-color: #999999");
        stackPane.setStyle("-fx-background-color: #CCCCCC");

        imgW = img.getImage().getWidth();
        imgH = img.getImage().getHeight();

        // bind max and min to adjust to new image bounds

        stackPane.minWidthProperty().bind(Bindings.createDoubleBinding(() -> 
            scrollPane.getViewportBounds().getWidth(), scrollPane.viewportBoundsProperty()
        ));
        stackPane.minHeightProperty().bind(Bindings.createDoubleBinding(() -> 
            scrollPane.getViewportBounds().getHeight(), scrollPane.viewportBoundsProperty()
        ));

        clipContainer.maxWidthProperty().bind(Bindings.createDoubleBinding(() -> 
            img.getFitWidth(), img.fitWidthProperty()
        ));
        clipContainer.maxHeightProperty().bind(Bindings.createDoubleBinding(() -> 
            img.getFitHeight(), img.fitHeightProperty()
        ));

        // initial scale
        img.setFitWidth(imgW * 1);
        img.setFitHeight(imgH * 1);

        // on mouse scroll
        stackPane.setOnScroll(event -> {
            event.consume();

            if (event.getDeltaY() == 0) {
              return;
            }

            double scaleFactor =
              (event.getDeltaY() > 0)
                ? SCALE_DELTA
                : 1/SCALE_DELTA;


            scale *= scaleFactor;

            img.setFitWidth(imgW * scale);
            img.setFitHeight(imgH * scale);

            // HERE i want to do something to keep my image where it was
            scrollPane.setHvalue(0.5);

        });

        scrollPane.hvalueProperty().addListener((observable, oldvalue, newvalue) -> {
            System.out.println(newvalue);
        });

    }
}

共有2个答案

程举
2023-03-14

大约一年半前有人问过这个问题,但对于仍在寻找答案的人来说,布局只会覆盖hvalue和vvalue属性一次。这意味着,您可以附加一个侦听器,该侦听器在否决更改值的尝试后自行处理,而不是直接设置值。

也就是说,给定自动取消注册侦听器的包装类。

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

public class ChangeOnceListener<T> implements ChangeListener<T> {

    private ChangeListener<T> wrapped;

    public ChangeOnceListener(ChangeListener<T> wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
        observable.removeListener(this);
        wrapped.changed(observable, oldValue, newValue);
    }

}

.. OP的场景可以通过以下方式修复。

        pane.setOnScroll(e -> {
            pane.hvalueProperty().addListener(new ChangeOnceListener<>((obv, old, neww) -> {
                pane.setHvalue(0.5);
            }));

            pane.vvalueProperty().addListener(new ChangeOnceListener<>((obv, old, neww) -> {
                pane.setVvalue(0.5);
            }));
        });
阳建弼
2023-03-14

虽然我不熟悉JavaFX,但人们会认为您可以通过在滚动条边界发生变化时调整滚动条边界来“强制您自己的值”。因此,改为在ChangeListener回调中执行scrollPane.setHvalue(0.5)

scrollPane.hvalueProperty().addListener((DoubleProperty observable, double oldvalue, double newvalue) -> {

    if (newvalue != 0.5) {
        scrollPane.setHvalue(0.5);
    }

});
 类似资料:
  • 我正在尝试构建一个包含6个窗格(作为父级添加到GridPane布局中)的简单Java项目。我必须在开始时设置窗口大小,并通过参考根布局的宽度和高度,设法将它们均匀地拆分。 但我想要他们调整大小,因为我改变了窗口的大小(使用鼠标,现在他们得到固定的大小)。 下面是我的代码:

  • 我正在制作这个用户界面,其中显示了一个图像映射,包装成一个锚窗格,本身包装成一个滚动窗格。我想在左下角添加一个覆盖,显示坐标。当然,即使在调整视图大小和左右滚动地图的时候,这个覆盖也必须是可见的。我试图在anchorpane中使用约束,但是当向右滚动地图时,坐标字段消失了。在这里,您可以看到我的层次结构的视图和我希望字段保持的位置:场景构建器视图Thx为您的答案。

  • 问题内容: 我已经创建了JTextpane并在textpane中插入了组件(像Jtextarea这样的组件)。当我在该JTextpane中插入新组件时,JTextpane的Jscrollpane(的垂直滚动条)会自动设置为底部。我想将其设置为最高位置。我怎样才能做到这一点 感谢Sunil Kumar Sahoo 问题答案: 这是我使用的实用程序类。可用于滚动到的顶部,底部,左侧,右侧或水平/垂直中

  • 如何在FXML(JavaFX)中获得用户调整大小后的新窗口大小?我已经搜索了一个“onResizeWindow”方法,但我什么也没有找到。

  • 我希望我的所有组件(文本字段、标签等)保持在固定大小的中心。我在scene builder中制作它,当我看到预览时它会工作,但是当我启动它时,当我调整它的大小时,组件不会停留在中心。 请看这里的图片:http://postimg.org/image/r42kvvfbf/ FXML文件: 我尝试过使用StackPane,也只使用VBox,但我无法实现我想要的。请帮帮忙!

  • 我正在编写一个JavaFX程序,制作一个随机颜色的网格。当调整窗口大小时,网格应该调整到尽可能大,同时仍然保持正方形,并在底部为文本留出空间。 一切工作正常,但我遇到的问题是,当调整GridPane的大小时,它会留下一些空隙。调整窗口大小时,间隙的大小会略有变化。有人能帮我找出如何消除这些差距吗?我包括完整的代码。它不太长。谢谢你。