0

So the main issue what i have is when clicking on a ScrollBar Button. The Viewport of the Scrollpane changes, but not the children (Node) of the pane. I have either to request the focus on the pane or click on the icon in the taskmenu, that the Viewport content get's refreshed. Swing does this automatically when you move the scrollbars.

Is there another way to get this functionality working as described above and by code? The call requestFocus() seems strange to me. By the way: it highlights everytime that bothers.

Here is what i got so far:

public class ScrollPaneTest extends Application {
private ScrollPane scrollPane;
private StackPane stackPane;
private Pane pane;
private Button button;

@Override
public void start(Stage primaryStage) throws Exception {

    scrollPane = new ScrollPane();
    stackPane = new StackPane();
    pane = new Pane();
    button = new Button();

    scrollPane.setPrefHeight(400.0);
    scrollPane.setPrefWidth(600.0);

    stackPane.setPrefHeight(1000.0);
    stackPane.setPrefWidth(1000.0);

    pane.setPrefHeight(200.0);
    pane.setPrefWidth(200.0);

    button.setMnemonicParsing(false);
    button.setText("Button");

    pane.getChildren().add(button);
    stackPane.getChildren().add(pane);        

    scrollPane.setContent(stackPane);      

    scrollPane.viewportBoundsProperty().addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
            Bounds oldViewportBounds = (Bounds) oldValue;
            Bounds newViewportBounds = (Bounds) newValue;       

            double x = newViewportBounds.getMinX() * -1;
            double y = newViewportBounds.getMinY() * -1;

            button.setLayoutX(x);
            button.setLayoutY(y);

            // Working if focus is requested ...
            //pane.requestFocus();
            // ... or when clicked on the application icon in the taskmenu of windows

            // Doesn't work
            //pane.requestLayout();

        }
    });

    VBox vbox = new VBox();
    vbox.setPrefHeight(400.0);
    vbox.setPrefWidth(600.0);

    vbox.getChildren().add(scrollPane);

    Scene scene = new Scene(vbox);
    primaryStage.setScene(scene);
    primaryStage.show();        

}

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

For illustration purposes:

Scrollbar button not pressed

Scrollbar button pressed twice

RobRoy
  • 21
  • 4
  • 1
    The `viewportBounds` property denotes the part of the `ScrollPane` that is not covered by the `ScrollBar`s i.e. the part that displays the part of the `content`. The property shouldn't update unless the `ScrollPane` is resized... The properties you probably want to listen to are the `hvalue` and the `vvalue` properties. – fabian Jun 05 '18 at 19:28
  • Thanks for your hint Fabian, it guided me to the correct thought... – RobRoy Jun 06 '18 at 20:13

1 Answers1

0

Thanks Stackoverflow for this great article: JavaFX ScrollPane update viewportBounds on scroll

With that i was able to found the solution i needed. See below:

public class ScrollPaneTest extends Application {

private ScrollPane scrollPane;
private StackPane stackPane;
private Pane pane;
private Button button;

@Override
public void start(Stage primaryStage) throws Exception {

    scrollPane = new ScrollPane();
    stackPane = new StackPane();
    pane = new Pane() {
        @Override
        protected void layoutChildren(){              
            super.layoutChildren();
            // System.out.println("layout children");  
            // more ToDo's if neccessary...
        }
    };

    // so that the Pane can be smaller than (potentionally) other Panes inside of the StackPane        
    pane.setManaged(false);
    // set the dimension of the ScrollPane
    pane.setPrefHeight(400.0);
    pane.setPrefWidth(600.0);        

    button = new Button();
    button.setLayoutX(25);
    button.setLayoutY(25);

    scrollPane.setPrefHeight(400.0);
    scrollPane.setPrefWidth(600.0);

    stackPane.setPrefHeight(1000.0);
    stackPane.setPrefWidth(1000.0);

    button.setMnemonicParsing(false);
    button.setText("Button");

    pane.getChildren().add(button);
    stackPane.getChildren().add(pane);        

    scrollPane.setContent(stackPane);

    scrollPane.hvalueProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            //System.out.println("observable " + observable + " oldValue " + oldValue + " " + "newValue " + newValue);
            double hmin = scrollPane.getHmin();
            double hmax = scrollPane.getHmax();
            double hvalue = scrollPane.getHvalue();
            double contentWidth = scrollPane.getContent().getLayoutBounds().getWidth();
            double viewportWidth = scrollPane.getViewportBounds().getWidth();

            double hoffset = 
            Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin);

            pane.setLayoutX(hoffset);
            pane.requestLayout();                
        }
    });        

    scrollPane.vvalueProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            //System.out.println("observable " + observable + " oldValue " + oldValue + " " + "newValue " + newValue);
            double vmin = scrollPane.getVmin();
            double vmax = scrollPane.getVmax();
            double vvalue = scrollPane.getVvalue();
            double contentHeight = scrollPane.getContent().getLayoutBounds().getHeight();
            double viewportHeight = scrollPane.getViewportBounds().getHeight();

            double voffset = 
            Math.max(0, contentHeight - viewportHeight) * (vvalue - vmin) / (vmax - vmin);

            pane.setLayoutY(voffset);
            pane.requestLayout();
        }
    });

    VBox vbox = new VBox();
    vbox.setPrefHeight(400.0);
    vbox.setPrefWidth(600.0);

    vbox.getChildren().add(scrollPane);

    Scene scene = new Scene(vbox);
    primaryStage.setScene(scene);
    primaryStage.show();        

}

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

Unfortunately i wasn't able to upvote Mr. James_D's solution, but i try to link these both use cases.

Hope this one help others also. Cheers!

RobRoy
  • 21
  • 4