簡體   English   中英

調整內容大小時保持或設置 JavaFX 滾動窗格位置

[英]Keep or set JavaFX Scrollpane position when content resized

我想設置 Scrollpane H 和 V 值,但在調整內部內容大小之后。

一些上下文- 我得到以下結構:

在此處輸入圖片說明

我將.setOnScroll(...) (鼠標)事件附加到 StackPane,當它發生時,內部內容被調整大小(圖像)到一些值(又名縮放)。 我想添加 StackPane 最小大小綁定到滾動窗格視口大小和窗格大小綁定到圖像大小以保持一切正常工作。

到目前為止,一切正常,當內容更改大小時,滾動條會更新,並且一切都按照預期進行調整,但我想保留或將滾動條位置設置為某些值以保持縮放到之前的中間點(例如在 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但后來的布局可能為調整大小的內容設置了不同的值,因為我將大小限制更改為PaneStackPane

我的問題那么我應該怎么做?

是否有任何 onResize 事件來糾正或強制我自己的價值? 或在重新計算布局時安排滾動事件或我的更新的其他方式?

// 編輯

我清理了代碼,這是示例:

主程序

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

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

    }
}

雖然我不熟悉 JavaFX,但有人會認為您可以通過在滾動條邊界發生變化時調整其大小來“強制執行您自己的值” 因此,改為在ChangeListener回調中執行scrollPane.setHvalue(0.5)

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

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

});

這是大約一年半前被問到的,但對於仍在尋找答案的任何人來說 -布局只覆蓋 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);
            }));
        });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM